├── scraping └── ubu │ ├── ubu │ ├── __init__.py │ ├── items.py │ ├── spiders │ │ ├── __init__.py │ │ └── outsiders_spider.py │ ├── settings.py │ └── pipelines.py │ ├── requirements.txt │ ├── .gitignore │ ├── scrapy.cfg │ └── README.md ├── git ├── README.md └── blurb.txt ├── sql ├── orm │ ├── .gitignore │ ├── package.json │ ├── README.md │ ├── models.js │ └── test │ │ └── models.js ├── prep.md └── README.md ├── regex ├── blurb.txt └── README.md ├── testing ├── code │ ├── .gitignore │ ├── package.json │ ├── fizzBuzzSum.js │ └── test │ │ └── fizzBuzzSum.js ├── write-the-tests │ ├── .gitignore │ ├── isPalindrome.js │ ├── test │ │ ├── gemometryTest.js │ │ └── palindromeTest.js │ ├── package.json │ └── geometry.js ├── make-a-feature-with-tests │ ├── .gitignore │ ├── eschaton.js │ ├── package.json │ └── test │ │ └── eschatonTest.js ├── blurb.txt ├── README.md ├── API_and_unit_tests.md └── rails_api.md ├── object-oriented-programming-basics ├── start │ ├── lib │ │ ├── cat.rb │ │ ├── dog.rb │ │ ├── plant.rb │ │ ├── animal.rb │ │ ├── venus_fly_trap.rb │ │ └── person.rb │ └── script.rb └── oop-review.pdf ├── restfulness └── README.md ├── code_challenge ├── README.md ├── reverse_string.rb ├── max_characters.js ├── palindromes.rb ├── reverse_string.js ├── palindromes.js └── anagrams.rb ├── promises ├── promises.html ├── README.md ├── callbacks.html ├── callbacks.js └── promises.js ├── lessonideas.md ├── react ├── blurb.txt └── README.md ├── mk ├── refactoring ├── extract_method.rb ├── extractmethodanswer.rb ├── extract_class.rb ├── extract_classanswer.rb └── refactoring.md ├── ssh ├── workshop.md └── lesson-plan.md ├── closures └── README.md ├── vim.md ├── design_patterns └── README.md ├── interviewing └── code_challenge skills.md ├── big-o.md ├── docker ├── more_docker │ ├── README.md │ └── DEPLOYING_TO_AWS.md └── README.md ├── terminal-fu.md ├── databases └── README.md └── typescript └── typescript.md /scraping/ubu/ubu/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /git/README.md: -------------------------------------------------------------------------------- 1 | # Shell Hacks: Git 2 | -------------------------------------------------------------------------------- /sql/orm/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /regex/blurb.txt: -------------------------------------------------------------------------------- 1 | Regular Expressions 2 | -------------------------------------------------------------------------------- /scraping/ubu/requirements.txt: -------------------------------------------------------------------------------- 1 | Scrapy==1.3.3 2 | -------------------------------------------------------------------------------- /scraping/ubu/.gitignore: -------------------------------------------------------------------------------- 1 | env 2 | **/__pycache__ 3 | -------------------------------------------------------------------------------- /testing/code/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /testing/write-the-tests/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /testing/make-a-feature-with-tests/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /object-oriented-programming-basics/start/lib/cat.rb: -------------------------------------------------------------------------------- 1 | class Cat 2 | 3 | end 4 | -------------------------------------------------------------------------------- /object-oriented-programming-basics/start/lib/dog.rb: -------------------------------------------------------------------------------- 1 | class Dog 2 | 3 | end 4 | -------------------------------------------------------------------------------- /object-oriented-programming-basics/start/lib/plant.rb: -------------------------------------------------------------------------------- 1 | class Plant 2 | 3 | end 4 | -------------------------------------------------------------------------------- /object-oriented-programming-basics/start/lib/animal.rb: -------------------------------------------------------------------------------- 1 | class Animal 2 | 3 | end 4 | -------------------------------------------------------------------------------- /restfulness/README.md: -------------------------------------------------------------------------------- 1 | Start here: 2 | 3 | https://github.com/trivett/express-todo-api-starter -------------------------------------------------------------------------------- /object-oriented-programming-basics/start/lib/venus_fly_trap.rb: -------------------------------------------------------------------------------- 1 | class VenusFlyTrap 2 | 3 | end 4 | -------------------------------------------------------------------------------- /scraping/ubu/scrapy.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | default = ubu.settings 3 | 4 | [deploy] 5 | project = ubu 6 | -------------------------------------------------------------------------------- /object-oriented-programming-basics/start/lib/person.rb: -------------------------------------------------------------------------------- 1 | class Person 2 | attr_accessor :pet 3 | 4 | end 5 | -------------------------------------------------------------------------------- /code_challenge/README.md: -------------------------------------------------------------------------------- 1 | # Code challenge exercises 2 | 3 | This folder contains code challenges of the sort that you might encounter in a 4 | technical interview. 5 | -------------------------------------------------------------------------------- /object-oriented-programming-basics/oop-review.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backend-mentoring/teaching_topics/HEAD/object-oriented-programming-basics/oop-review.pdf -------------------------------------------------------------------------------- /promises/promises.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Document 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /scraping/ubu/ubu/items.py: -------------------------------------------------------------------------------- 1 | from scrapy import Field, Item 2 | 3 | 4 | class UbuItem(Item): 5 | category = Field() 6 | filename = Field() 7 | content_type = Field() 8 | content = Field() 9 | -------------------------------------------------------------------------------- /scraping/ubu/ubu/spiders/__init__.py: -------------------------------------------------------------------------------- 1 | # This package will contain the spiders of your Scrapy project 2 | # 3 | # Please refer to the documentation for information on how to create and manage 4 | # your spiders. 5 | -------------------------------------------------------------------------------- /scraping/ubu/README.md: -------------------------------------------------------------------------------- 1 | # scraping with scrapy 2 | 3 | ```sh 4 | $ virtualenv -p python3 env 5 | $ . ./env/bin/activate 6 | $ pip install scrapy 7 | $ # scrapy startproject ubu && customize files 8 | $ scrapy crawl outsiders 9 | ``` 10 | -------------------------------------------------------------------------------- /testing/write-the-tests/isPalindrome.js: -------------------------------------------------------------------------------- 1 | 2 | const isPalindrome = (origWord) => { 3 | let inputArray = origWord.split(""); 4 | let reversed = inputArray.reverse().join(""); 5 | return reversed === origWord; 6 | } 7 | 8 | module.exports = isPalindrome; 9 | -------------------------------------------------------------------------------- /code_challenge/reverse_string.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | 3 | Create a function that takes in a string and returns the reverse of the string. 4 | 5 | Obviously, do not use reverse method on the string class. 6 | 7 | This is what I expect: 8 | 9 | reverse_string("love") 10 | # => "evol" 11 | 12 | =end 13 | 14 | 15 | -------------------------------------------------------------------------------- /sql/orm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "orm", 3 | "version": "1.0.0", 4 | "main": "models.js", 5 | "scripts": { 6 | "test": "./node_modules/.bin/mocha" 7 | }, 8 | "devDependencies": { 9 | "chai": "^3.5.0", 10 | "mocha": "^3.2.0" 11 | }, 12 | "dependencies": { 13 | "sqlite3": "^3.1.8" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /testing/code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testing", 3 | "description": "an example of testing in javascript", 4 | "scripts": { 5 | "test": "./node_modules/.bin/mocha --reporter min --watch test" 6 | }, 7 | "license": "WTFPL", 8 | "devDependencies": { 9 | "chai": "^3.5.0", 10 | "mocha": "^3.2.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scraping/ubu/ubu/settings.py: -------------------------------------------------------------------------------- 1 | BOT_NAME = 'ubu' 2 | 3 | EXPORT_DIRECTORY = '~/Downloads/ubu' 4 | 5 | SPIDER_MODULES = ['ubu.spiders'] 6 | NEWSPIDER_MODULE = 'ubu.spiders' 7 | 8 | ROBOTSTXT_OBEY = True 9 | 10 | ITEM_PIPELINES = { 11 | 'ubu.pipelines.ValidateContentTypePipeline': 300, 12 | 'ubu.pipelines.FileExporterPipeline': 500, 13 | } 14 | -------------------------------------------------------------------------------- /promises/README.md: -------------------------------------------------------------------------------- 1 | # Promises 2 | Everybody go here: [https://worst-api-ever.herokuapp.com](https://worst-api-ever.herokuapp.com) 3 | 4 | Now clone this: [https://github.com/trivett/good-consumption-of-a-bad-api](https://github.com/trivett/good-consumption-of-a-bad-api) 5 | 6 | You know what to do. 7 | 8 | ![FUUUUCK](https://media.giphy.com/media/119cVU19ICcAKc/giphy.gif) -------------------------------------------------------------------------------- /promises/callbacks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Document 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /object-oriented-programming-basics/start/script.rb: -------------------------------------------------------------------------------- 1 | require 'pry' #this is a gem that lets you drop in a repl, like debugger 2 | 3 | require_relative 'lib/cat' 4 | require_relative 'lib/dog' 5 | require_relative 'lib/plant' 6 | require_relative 'lib/venus_fly_trap' 7 | require_relative 'lib/person' 8 | 9 | 10 | c = Cat.new 11 | 12 | d = Dog.new 13 | 14 | binding.pry 15 | 16 | -------------------------------------------------------------------------------- /lessonideas.md: -------------------------------------------------------------------------------- 1 | # Lesson Ideas 2 | 3 | Feel free to make a make a pull request to suggest another topic 4 | 5 | * OO (redo) 6 | * Big O 7 | * Sorting algorithms 8 | * Regex (again) 9 | * MVC 10 | * Design patterns 11 | * Make an Open source contribution 12 | * Linux. What is it? 13 | * Data Structures 14 | * Generators 15 | * Functional Programming 16 | * Golang 17 | * Testing (again) 18 | -------------------------------------------------------------------------------- /testing/write-the-tests/test/gemometryTest.js: -------------------------------------------------------------------------------- 1 | const { assert, expect } = require('chai'); 2 | 3 | const geometry = require('../geometry'); 4 | 5 | describe('geometry', () => { 6 | 7 | it('should exist', () => { 8 | expect(geometry).to.exist; 9 | }); 10 | 11 | describe('all triangles', () =>{ 12 | 13 | }) 14 | 15 | describe('right triangles', () =>{ 16 | 17 | }) 18 | 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /testing/make-a-feature-with-tests/eschaton.js: -------------------------------------------------------------------------------- 1 | const eschaton = { 2 | fight: (defender, attacker) => { 3 | // assuming an object as defender and attacker with a strength attribute, 4 | // depending on who is stronger, defender or attacker wins 5 | // after that passes, include a shield attribute that helps decide the winner 6 | // 7 | 8 | // 9 | } 10 | }; 11 | 12 | module.exports = eschaton; 13 | -------------------------------------------------------------------------------- /react/blurb.txt: -------------------------------------------------------------------------------- 1 | No Magic React 2 | 3 | The modern javascript stack is a point of contention for many web developers, 4 | both new and experienced. It introduces a long list of frequently changing tools 5 | that seem to melt into each other in inexplicable ways. We're going to demystify 6 | react by building a site with nothing but react. This way will help us 7 | understand the role it plays, and how it can be effectively complemented by 8 | other modern tools. 9 | 10 | -------------------------------------------------------------------------------- /testing/blurb.txt: -------------------------------------------------------------------------------- 1 | Testing 2 | 3 | You know that feeling when you make no coding mistakes ever, and every one you 4 | code with is perfect? Me Neither. This week we'll be showing you how to design 5 | and write effective tests. In the words of Donald Knuth, "Beware of bugs in the 6 | above code; I have only proved it correct, not tried it." Tests are our way of 7 | trying the code. 8 | 9 | We'll also provide an opportunity to work through obstacles in your personal 10 | projects. 11 | 12 | -------------------------------------------------------------------------------- /testing/code/fizzBuzzSum.js: -------------------------------------------------------------------------------- 1 | // http://www.mathsisfun.com/algebra/sequences-sums-arithmetic.html 2 | // where a = n, simplify 3 | const arithmeticSum = (n, d) => (n / 2) * (n + 1) * d; 4 | 5 | const sumMultiplesUntil = (m, n) => arithmeticSum(Math.floor(n / m), m); 6 | 7 | const fizzBuzzSum = n => 8 | // add up the 3s and 5s 9 | sumMultiplesUntil(3, n) + sumMultiplesUntil(5, n) - 10 | // but only count the 15s once 11 | sumMultiplesUntil(15, n); 12 | 13 | module.exports = { fizzBuzzSum }; 14 | -------------------------------------------------------------------------------- /testing/write-the-tests/test/palindromeTest.js: -------------------------------------------------------------------------------- 1 | const { assert, expect} = require('chai'); 2 | 3 | const isPalindrome = require('../isPalindrome'); 4 | 5 | describe('isPalindrome', () => { 6 | it('should exist', () => { 7 | expect(isPalindrome).to.exist; 8 | }); 9 | 10 | // let's think of some cases to test 11 | 12 | // test the happy path and the sad path. 13 | 14 | // if your tests fail on somthing that should have worked, go ahead and alter the code accordingly. 15 | 16 | 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /testing/write-the-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "write-the-tests", 3 | "version": "1.0.0", 4 | "description": "This time, i supply working src, you write the specs", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha --reporter min --watch test" 8 | }, 9 | "keywords": [ 10 | "cle", 11 | "llaves", 12 | "kagi" 13 | ], 14 | "author": "Vincent Trivett", 15 | "license": "ISC", 16 | "devDependencies": { 17 | "chai": "^3.5.0", 18 | "mocha": "^3.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /testing/make-a-feature-with-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "write-the-tests", 3 | "version": "1.0.0", 4 | "description": "Create a silly game with test driven development", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha --reporter min --watch test" 8 | }, 9 | "keywords": [ 10 | "cle", 11 | "llaves", 12 | "kagi" 13 | ], 14 | "author": "Vincent Trivett", 15 | "license": "ISC", 16 | "devDependencies": { 17 | "chai": "^3.5.0", 18 | "mocha": "^3.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /code_challenge/max_characters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Feel free to rename this file and use a language other than Javascript 3 | * 4 | * Prompt: 5 | * 6 | * Given a string, return the character that has the most occurrances in the string. 7 | * 8 | * For example: 9 | * 10 | * maxChars('hello'); 11 | * > 'l' 12 | * 13 | * Get a solution working, then see if you can handle edge cases such as capitals, spaces, special characters, and more than one 'winner'. 14 | * 15 | * 16 | */ 17 | 18 | 19 | 20 | 21 | /** 22 | * Now code a solution to find out which character is the most common in the entire English langauge. 23 | */ -------------------------------------------------------------------------------- /code_challenge/palindromes.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Create a function that determines whether a string is a palindrome 3 | * This is the expected output: 4 | * is_palindrome('tacocat') 5 | * > true 6 | * is_palindrome('beans') 7 | * > false 8 | =end 9 | 10 | # code here 11 | 12 | =begin 13 | * Now let's make things interesting. 14 | * 15 | * Find out how many words in the English language are palindromes 16 | * 17 | * There is a file on your Mac (if you have a mac) at the path provided. Use Ruby to read this file, and figure out the number of palindromes that there are in English and log it to the console. 18 | =end 19 | 20 | # code here -------------------------------------------------------------------------------- /testing/write-the-tests/geometry.js: -------------------------------------------------------------------------------- 1 | const geometry = { 2 | isTriangle: (side1,side2,side3) => { 3 | return side1 + side2 > side3 || 4 | side1 + side3 > side2 || 5 | side2+ side3 > side1 6 | }, 7 | 8 | isRightTriangle: (side1,side2,hypoteneuse) => { 9 | 10 | let squaresOfSides = (Math.pow(side1, 2) + Math.pow(side2, 2)); 11 | let hypoteneuseSquared = Math.pow(hypoteneuse, 2); 12 | return geometry.isTriangle(side1,side2,hypoteneuse) && squaresOfSides === hypoteneuseSquared; 13 | } 14 | 15 | }; 16 | 17 | console.log(geometry.isRightTriangle(3,4,5)); 18 | 19 | 20 | 21 | module.exports = geometry; 22 | -------------------------------------------------------------------------------- /code_challenge/reverse_string.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Create a function that takes in a string and returns the reverse of the string. 4 | 5 | Obviously, do not use reverse method on the String prototype. 6 | 7 | This is what I expect: 8 | 9 | reverseString("love") 10 | > "evol" 11 | 12 | */ 13 | 14 | 15 | function reverseString(str){ 16 | return str.split('').reduce((reversed, character) => { 17 | console.log('reversed', reversed); 18 | console.log('character', character); 19 | return character + reversed; 20 | }, '+') 21 | } 22 | 23 | console.log(reverseString('love')); 24 | 25 | console.log(reverseString('tacocat')); 26 | 27 | -------------------------------------------------------------------------------- /testing/make-a-feature-with-tests/test/eschatonTest.js: -------------------------------------------------------------------------------- 1 | const { assert, expect } = require('chai'); 2 | 3 | const eschaton = require('../eschaton'); 4 | 5 | describe('eschaton', () => { 6 | it('should exist', () => { 7 | expect(eschaton).to.exist; 8 | }); 9 | 10 | 11 | // create our beligerents in a before block ( just to get practice ) 12 | // https://mochajs.org/#hooks 13 | 14 | // write a test to make sure that the stronger beligerent wins 15 | 16 | // then write the code to make it pass 17 | 18 | // then add shields as a thing and make the old test still pass 19 | 20 | // keep making it more complex with your tests guiding you. 21 | 22 | // just get in those reps! 23 | 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /mk: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | NAME="$1" 6 | 7 | if [ -z $NAME ] || [ "$NAME" = "-h" ] || [ "$NAME" = "--help" ]; then 8 | echo 9 | echo "Makes a workshop directory with default files." 10 | echo 11 | echo "Usage:" 12 | echo " ./mk []" 13 | echo " ./mk -h | --help" 14 | echo 15 | exit 1 16 | fi 17 | 18 | # done below [ -z $NAME ] to make sure the shift is safe 19 | shift 20 | TITLE="$*" 21 | 22 | if [ -e $NAME ]; then 23 | echo "Error: workshop directory already exists." 24 | exit 1 25 | fi 26 | 27 | 28 | mkdir "$NAME" 29 | 30 | if [ -z "$TITLE" ]; then 31 | touch "$NAME/blurb.txt" 32 | touch "$NAME/README.md" 33 | else 34 | echo "$TITLE" > "$NAME/blurb.txt" 35 | echo "# $TITLE" > "$NAME/README.md" 36 | fi 37 | 38 | echo "Ready." 39 | tree --noreport "$NAME" 40 | 41 | -------------------------------------------------------------------------------- /sql/orm/README.md: -------------------------------------------------------------------------------- 1 | # Object Relational Mapping 2 | 3 | We are going to put together an ORM for a small SQL database. 4 | 5 | Let's use [sqlite](https://sqlite.org/docs.html) for its simplicity and ease of 6 | deployment, and [node-sqlite3](https://github.com/mapbox/node-sqlite3) to 7 | perform the underlying communication with the database. 8 | 9 | The schema is predetermined for this exercise. You can see the table creation 10 | statements at the top of model.js. 11 | 12 | You will write a `Person` class that corresponds to the `people` table. `Person` 13 | instances will correspond to rows `people`. `Friendship`s between people 14 | will be stored in the (**drum roll please**) `friendships` table. And again, 15 | instances will correspond with rows in the table. 16 | 17 | Run `npm test` to test the completeness of your model classes. 18 | -------------------------------------------------------------------------------- /promises/callbacks.js: -------------------------------------------------------------------------------- 1 | console.log('we are runnin...'); 2 | 3 | document 4 | .querySelector('html') 5 | .addEventListener('click', function (event) { 6 | 7 | console.log('A click, a click!'); 8 | console.log('Lets do something asynchronous'); 9 | 10 | $.getJSON('http://swapi.co/api/planets/1', function (planetData) { 11 | 12 | var numberOfFilms = planetData.films.length; 13 | var receivedFilms = []; 14 | 15 | planetData.films.forEach(function (link) { 16 | $.getJSON(link, receiveFilm); 17 | }); 18 | 19 | function receiveFilm(theFilm) { 20 | 21 | receivedFilms.push(theFilm.title); 22 | 23 | if (receivedFilms.length === numberOfFilms) { 24 | console.log(receivedFilms); 25 | } 26 | } 27 | 28 | }); 29 | }); 30 | 31 | console.log('we are really runnin...') 32 | -------------------------------------------------------------------------------- /promises/promises.js: -------------------------------------------------------------------------------- 1 | console.log('we are runnin...'); 2 | 3 | document 4 | .querySelector('html') 5 | .addEventListener('click', event => { 6 | 7 | console.log('A click, a click!'); 8 | console.log('Lets do something asynchronous'); 9 | 10 | fetch('http://swapi.co/api/planets/1') 11 | .then(r => { 12 | if (r.ok) { 13 | return r.json(); 14 | } 15 | throw new Error('not ok'); 16 | }) 17 | .then(planetData => 18 | Promise.all(planetData.films.map(link => 19 | fetch(link) 20 | .then(r => r.json()) 21 | .then(f => f.title)))) 22 | .then(filmTitles => { console.log(filmTitles); }) 23 | .catch(err => { 24 | console.log(`well, I really wanted to see some starwars films, but I guess I'll just have to not... :( ${err}`); 25 | }); 26 | }); 27 | 28 | console.log('we are really runnin...') 29 | -------------------------------------------------------------------------------- /scraping/ubu/ubu/spiders/outsiders_spider.py: -------------------------------------------------------------------------------- 1 | from os.path import basename 2 | from urllib.parse import urlparse 3 | 4 | from scrapy.linkextractors import LinkExtractor 5 | from scrapy.spiders import CrawlSpider, Rule 6 | 7 | from ..items import UbuItem 8 | 9 | 10 | class OutsidersSpider(CrawlSpider): 11 | name = 'outsiders' 12 | allowed_domains = ['www.ubu.com'] 13 | start_urls = ['http://www.ubu.com/outsiders/ass/'] 14 | 15 | rules = ( 16 | Rule(LinkExtractor(allow=(), deny_extensions=['html']), callback='parse_item'), 17 | ) 18 | 19 | def parse_item(self, response): 20 | 21 | url = urlparse(response.url) 22 | filename = basename(url.path) 23 | 24 | content_type = response.headers.get('Content-Type') 25 | 26 | yield UbuItem( 27 | category='outsiders', 28 | filename=filename, 29 | content_type=content_type, 30 | content=response.body) 31 | -------------------------------------------------------------------------------- /refactoring/extract_method.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'pry' 3 | 4 | class Person 5 | attr_accessor :first_name, :middle_name, :last_name, :city 6 | 7 | def initialize(first,middle,last,city) 8 | @first_name = first 9 | @middle_name = middle 10 | @last_name = last 11 | @city = city 12 | end 13 | 14 | def introduction 15 | "Greetings, I am #{first_name} #{middle_name} #{last_name} of #{city}, I come in peace." 16 | end 17 | 18 | def address 19 | "#{first_name} #{middle_name} #{last_name} \n#{city}" 20 | end 21 | 22 | end 23 | 24 | 25 | RSpec.describe Person do 26 | me = Person.new("Vincent", "Paul", "Trivett", "Brooklyn") 27 | 28 | describe "introduction" do 29 | it "delivers the right formal intro" do 30 | expect(me.introduction).to eq("Greetings, I am Vincent Paul Trivett of Brooklyn, I come in peace.") 31 | end 32 | end 33 | 34 | describe "address" do 35 | it "delivers the right formal intro" do 36 | expect(me.address).to eq("Vincent Paul Trivett \nBrooklyn") 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /refactoring/extractmethodanswer.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'pry' 3 | 4 | class Person 5 | attr_accessor :first_name, :middle_name, :last_name, :city 6 | 7 | def initialize(first,middle,last,city) 8 | @first_name = first 9 | @middle_name = middle 10 | @last_name = last 11 | @city = city 12 | end 13 | 14 | def introduction 15 | "Greetings, I am #{full_name}, I come in peace." 16 | end 17 | 18 | def address 19 | "#{full_name} \n#{city}" 20 | end 21 | 22 | private 23 | 24 | def full_name 25 | "#{first_name} #{middle_name} #{last_name} of #{city}" 26 | end 27 | end 28 | 29 | 30 | RSpec.describe Person do 31 | me = Person.new("Vincent", "Paul", "Trivett", "Brooklyn") 32 | 33 | describe "introduction" do 34 | it "delivers the right formal intro" do 35 | expect(me.introduction).to eq("Greetings, I am Vincent Paul Trivett of Brooklyn, I come in peace.") 36 | end 37 | end 38 | 39 | describe "address" do 40 | it "delivers the right formal intro" do 41 | expect(me.address).to eq("Vincent Paul Trivett \nBrooklyn") 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /sql/orm/models.js: -------------------------------------------------------------------------------- 1 | const { Database } = require('sqlite3').verbose(); 2 | 3 | // store everything in memory, meaning the database is empty on every execution. 4 | const db = new Database(':memory:'); 5 | 6 | const createTablePeople = ` 7 | CREATE TABLE people ( 8 | id INTEGER PRIMARY KEY ASC, 9 | name TEXT NOT NULL, 10 | eye_color TEXT NOT NULL 11 | );`; 12 | 13 | const createTableFriendships = ` 14 | CREATE TABLE friendships ( 15 | id INTEGER PRIMARY KEY ASC, 16 | person_a INTEGER, 17 | person_b INTEGER, 18 | FOREIGN KEY(person_a, person_b) REFERENCES people(id, id), 19 | CHECK (person_a < person_b) 20 | ); 21 | CREATE UNIQUE INDEX distinct_friends ON friendships (person_a, person_b);`; 22 | 23 | // callback will receive the Person and Friendship class. 24 | module.exports = function (callback) { 25 | 26 | // create the tables, and then define the models. 27 | db.exec(createTablePeople + createTableFriendships, defineModels); 28 | 29 | function defineModels(err) { 30 | 31 | class Person { 32 | } 33 | if (err !== null) { 34 | throw err; 35 | } 36 | 37 | class Friendship { 38 | } 39 | 40 | callback({ Person, Friendship }); 41 | 42 | } 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /testing/code/test/fizzBuzzSum.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai'); 2 | 3 | const { fizzBuzzSum } = require('../fizzBuzzSum'); 4 | 5 | describe('fizzBuzzSum', () => { 6 | 7 | it('should work for 0', () => { 8 | assert.equal(fizzBuzzSum(0), 0); 9 | }); 10 | 11 | it('should work for 1', () => { 12 | assert.equal(fizzBuzzSum(1), 0); 13 | }); 14 | 15 | it('should add up the first number', () => { 16 | assert.equal(fizzBuzzSum(3), 3); 17 | }); 18 | 19 | it('should work for 5', () => { 20 | assert.equal(fizzBuzzSum(5), 3 + 5); 21 | }); 22 | 23 | it('should work for 6', () => { 24 | assert.equal(fizzBuzzSum(6), 3 + 5 + 6); 25 | }); 26 | 27 | it('should count 15 once', () => { 28 | assert.equal( 29 | fizzBuzzSum(15), 30 | 3 + 5 + 6 + 9 + 10 + 12 + 15); 31 | }); 32 | 33 | it('should count two cycles of 15 (30)', () => { 34 | assert.equal( 35 | fizzBuzzSum(30), 36 | 3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 + 20 + 21 + 24 + 25 + 27 + 30); 37 | }); 38 | 39 | it('should count two cycles of 15 and 7 (37)', () => { 40 | assert.equal( 41 | fizzBuzzSum(37), 42 | 3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 + 20 + 21 + 24 + 25 + 27 + 30 + 33 + 35 + 36); 43 | }); 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /refactoring/extract_class.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | 3 | class Student 4 | attr_accessor :first_term_assiduity, :first_term_test, :first_term_behavior 5 | attr_accessor :second_term_assiduity, :second_term_test, :second_term_behavior 6 | attr_accessor :third_term_assiduity, :third_term_test, :third_term_behavior 7 | 8 | def set_all_grades_to grade 9 | %w(first second third).each do |which_term| 10 | %w(assiduity test behavior).each do |criteria| 11 | send "#{which_term}_term_#{criteria}=".to_sym, grade 12 | end 13 | end 14 | end 15 | 16 | def first_term_grade 17 | (first_term_assiduity + first_term_test + first_term_behavior) / 3 18 | end 19 | 20 | def second_term_grade 21 | (second_term_assiduity + second_term_test + second_term_behavior) / 3 22 | end 23 | 24 | def third_term_grade 25 | (third_term_assiduity + third_term_test + third_term_behavior) / 3 26 | end 27 | end 28 | 29 | 30 | 31 | RSpec.describe Student do 32 | 33 | describe "term grades" do 34 | student = Student.new 35 | student.set_all_grades_to 10 36 | it "has a grade for all three terms" do 37 | 38 | expect(student.first_term_grade).to eq(10) 39 | expect(student.second_term_grade).to eq(10) 40 | expect(student.third_term_grade).to eq(10) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /git/blurb.txt: -------------------------------------------------------------------------------- 1 | Shell Hacks: Git 2 | 3 | This week we'll present some advanced git features, as well as explore higher level patterns of use that can turn git into a streamlined part of your development process. The workshop should address scenarios you may have already found your self in: 4 | 5 | - (insert favorite profanity here), that last commit wasn't quite right. 6 | - Merge conflicts... 7 | - I want to explore an alternative to the most recent changes in my working directory without throwing them out. 8 | - I've lost track of what's happened in the last couple hours of coding. 9 | - Something about my most recent changes no longer works. What could it be? 10 | - How do I work cleanly with other developers? 11 | - What should be committed, what should be ignored, why? 12 | 13 | We'll prompt you to think about your own experience with git, and discover the workflow that works for you. 14 | 15 | --- 16 | 17 | Git Foundations 18 | 19 | The first hour will be dedicated to your personal challenges and interests. Vincent and Allon will be available to offer their guidance on whatever aspect of development you are facing. 20 | 21 | > git add . 22 | > git commit -m 'stuff' 23 | > # we can do better than that... 24 | 25 | Hour number two will be dedicated to mastering git through foundational understanding and good habit. We'll be looking at how git is best used individually and collaboratively to prepare you for coding in a professional environment. 26 | 27 | -------------------------------------------------------------------------------- /sql/prep.md: -------------------------------------------------------------------------------- 1 | # SQL 2 | 3 | ## History 4 | 5 | SQL stands for Structured Query Language designed initially to manage Relational Database data. 6 | a relational database is a DB in which the data is structured tables of columns and rows with an unique key to identify each row that are connected by foreign keys.You can represent this way arbitrarily complex data relationships. 7 | It was initially developed at IBM in the early 1970's. 8 | There are several dialects that are expansion on the standards, because of different implementations like Postgres and MySQL. They might differ in data and time, syntax and commands. Postgres aims to be standard compliance. also SQLite most queries will work for other implementations as well. You should be aware of using special features if being able to switch database is important. 9 | There are some libraries that allow you to communicate with your relational database using the constructs of your programming language (interpreters). These are called Object Relational Mapping tools (ORM) some examples are ActiveRecords for Rails and Doctrine NHibernate. Each of them has their internal API (Application Programming Interface) 10 | Schema migrations are a way to include database changes and they use SQL syntax. (Using ALTER TABLE statements) 11 | A Schema is a description of the database structure. 12 | 13 | SQL allows you to do CRUD operations on a database using statements and queries. 14 | SQL SELECT Queries declare the desired form of the returned data and let the underlying system make that happen. 15 | -------------------------------------------------------------------------------- /refactoring/extract_classanswer.rb: -------------------------------------------------------------------------------- 1 | require 'rspec' 2 | require 'pry' 3 | 4 | 5 | class Student 6 | attr_accessor :terms 7 | 8 | def initialize 9 | @terms = [ 10 | Term.new(:first), 11 | Term.new(:second), 12 | Term.new(:third) 13 | ] 14 | end 15 | 16 | def set_all_grades_to grade 17 | @terms.each { |term| term.set_all_grades_to(grade) } 18 | end 19 | 20 | def first_term_grade 21 | term(:first).grade 22 | end 23 | 24 | def second_term_grade 25 | term(:second).grade 26 | end 27 | 28 | def third_term_grade 29 | term(:third).grade 30 | end 31 | 32 | def term reference 33 | terms.find { |term| term.name == reference } 34 | end 35 | end 36 | 37 | class Term 38 | 39 | attr_reader :name, :assiduity, :test, :behavior 40 | 41 | def initialize(name) 42 | @name = name 43 | @assiduity = 0 44 | @test = 0 45 | @behavior = 0 46 | end 47 | 48 | def set_all_grades_to(grade) 49 | @assiduity = grade 50 | @test = grade 51 | @behavior = grade 52 | end 53 | 54 | def grade 55 | (assiduity + test + behavior) / 3 56 | end 57 | end 58 | 59 | 60 | 61 | RSpec.describe Student do 62 | 63 | describe "term grades" do 64 | student = Student.new 65 | student.set_all_grades_to 10 66 | it "has a grade for all three terms" do 67 | 68 | expect(student.first_term_grade).to eq(10) 69 | expect(student.second_term_grade).to eq(10) 70 | expect(student.third_term_grade).to eq(10) 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /code_challenge/palindromes.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Create a function that determines whether a string is a palindrome 4 | * This is the expected output: 5 | * isPalindrome('tacocat') 6 | * > true 7 | * isPalindrome('beans') 8 | * > false 9 | * Try it with your reverseString function and then try to see if you can do it without reversing anything. 10 | * This might be a more roundabout way of solving the problem but it shows your knowledge of your tools and the kind of thinking that they are looking for 11 | */ 12 | 13 | 14 | 15 | function reverseString(str){ 16 | return str.split('').reduce((reversed, character) => { 17 | return character + reversed; 18 | }, '') 19 | } 20 | 21 | // function isPalindrome(str){ 22 | // const reversed = reverseString(str); 23 | // return reversed === str; 24 | // } 25 | 26 | function isPalindrome(str){ 27 | let middleIndex = Math.floor(str.length / 2); 28 | 29 | let halfString = str.split('').slice(0, middleIndex); 30 | return halfString.every((character, i) => { 31 | console.log('i', i); 32 | console.log('character', character); 33 | console.log(str.length - i - 1); 34 | return character === str[str.length - i - 1]; 35 | 36 | }); 37 | } 38 | 39 | console.log(isPalindrome('tacocat')); 40 | console.log('================'); 41 | console.log(isPalindrome('tacotown')); 42 | 43 | 44 | /** 45 | * Homework! 46 | * 47 | * Find out how many words in the English language are palindromes 48 | * 49 | * There is a file on your Mac (if you have a mac) at the path provided. Use node.js to read this file, and figure out the number of palindromes that there are in English and log it to the console. 50 | */ 51 | 52 | 53 | -------------------------------------------------------------------------------- /scraping/ubu/ubu/pipelines.py: -------------------------------------------------------------------------------- 1 | from mimetypes import types_map 2 | from os import mkdir 3 | from os.path import expanduser, join 4 | 5 | from scrapy.exceptions import DropItem 6 | 7 | 8 | MIME_TYPES = set(types_map.values()) 9 | 10 | 11 | # filters out items with a Content Type that is not a valid mime type 12 | class ValidateContentTypePipeline(object): 13 | 14 | def process_item(self, item, spider): 15 | if item['content_type'] and item['content_type'].decode('utf-8') in MIME_TYPES: 16 | return item 17 | raise DropItem('Bad Content-Type encountered: %s' % item) 18 | 19 | 20 | # writes item content out to disk 21 | class FileExporterPipeline(object): 22 | 23 | @classmethod 24 | def from_crawler(cls, crawler): 25 | export_directory = expanduser(crawler.settings.get('EXPORT_DIRECTORY')) 26 | return cls(export_directory=export_directory) 27 | 28 | def __init__(self, export_directory): 29 | self.export_directory = export_directory 30 | self.created_categories = set() 31 | 32 | def open_spider(self, spider): 33 | # make sure the export directory exists 34 | try: 35 | mkdir(self.export_directory) 36 | except OSError: 37 | pass 38 | 39 | def process_item(self, item, spider): 40 | 41 | category_directory = join(self.export_directory, item['category']) 42 | 43 | # make sure the category directory exists 44 | if not item['category'] in self.created_categories: 45 | try: 46 | mkdir(category_directory) 47 | self.created_categories.add(item['category']) 48 | except OSError: 49 | pass 50 | 51 | # write the item content out to file 52 | with open(join(category_directory, item['filename']), 'wb') as f: 53 | f.write(item['content']) 54 | -------------------------------------------------------------------------------- /testing/README.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | ## Theory 4 | 5 | ### Tests are small programs that exercise a piece of your application. 6 | 7 | ```javascript 8 | describe('feature A', () => { 9 | 10 | it('should behave like XYZ', () => { 11 | // use feature A and assert that XYZ occurs 12 | }); 13 | 14 | it('should also behave like 123', () => { 15 | // use feature A again, maybe differently, and assert that 123 occurs 16 | }); 17 | 18 | }); 19 | ``` 20 | 21 | ### Tests may be written at different levels of scale. 22 | 23 | - [unit, functional, acceptance, integration?](http://stackoverflow.com/questions/4904096/whats-the-difference-between-unit-functional-acceptance-and-integration-test/4904533#4904533) 24 | - [small, medium, large?](https://testing.googleblog.com/2010/12/test-sizes.html) 25 | 26 | Definitions are chose per organization or project, and generally follow how many componenets must communicate, how much state must be coordinated, or how long tests take to run. 27 | 28 | ### Tests may exercise different interfaces. 29 | 30 | - `js` 31 | - `http` 32 | - `stdin/stdout` 33 | - combinations thereof for higher level tests 34 | 35 | ### Tests may run in different environments. 36 | 37 | - web applications have a server and a browser client 38 | - mobile apps may have a server and a mobile app 39 | - ... 40 | 41 | ## Practice 42 | 43 | ```shell 44 | mkdir your-own 45 | mkdir your-own/test 46 | ``` 47 | 48 | We'll use [`mocha`](http://mochajs.org/) and [`chai`](http://chaijs.com/) for describing tests, running tests, and writing assertions. 49 | 50 | ```shell 51 | npm init 52 | npm install --save-dev mocha chai 53 | ``` 54 | 55 | Add a `test` script to your `package.json` file. Continuous testing! 56 | 57 | ```json 58 | { 59 | "scripts": { 60 | "test": "./node_modules/.bin/mocha --reporter min --watch test" 61 | } 62 | } 63 | ``` 64 | 65 | Take a look at the `code/` directory. 66 | 67 | Grab a couple of files. 68 | 69 | ```shell 70 | cp code/fizzBuzzSum.js your-own/ 71 | cp code/test/fizzBuzzSum.js your-own/test/ 72 | ``` 73 | 74 | Let's see how the tests are written and make them pass! 75 | 76 | ## Questions... 77 | 78 | So? -------------------------------------------------------------------------------- /sql/README.md: -------------------------------------------------------------------------------- 1 | History / Theory: 2 | 3 | [SQL](https://en.wikipedia.org/wiki/SQL) 4 | 5 | [Relational Database](https://en.wikipedia.org/wiki/Relational_database) 6 | 7 | --- 8 | CRUD for databases: 9 | 10 | Create -> INSERT 11 | Read -> SELECT 12 | Update -> UPDATE 13 | Delete -> DELETE 14 | 15 | ## Most important datatypes 16 | 17 | - TEXT = Holds a string with a maximum length of 65,535 characters 18 | - VARCHAR = Holds a variable length string (can contain letters, numbers, and special characters). The maximum size is specified in parenthesis. Can store up to 255 characters. Note: If you put a greater value than 255 it will be converted to a TEXT type 19 | - INT = integer number, the maximum number of digits may be specified in parenthesis 20 | - FLOAT = decimal The maximum number of digits may be specified in parenthesis 21 | 22 | 23 | {short} Core Language (leading into orm exercise): 24 | 25 | contrived examples that apply to the upcoming exercise 26 | 27 | INSERT 'hi', sdf, asdjfk into people 28 | 29 | --- 30 | 31 | {short} programming for SQL: 32 | 33 | [List of ORMs](https://en.wikipedia.org/wiki/List_of_object-relational_mapping_software) 34 | 35 | [Schema Migration](https://en.wikipedia.org/wiki/Schema_migration) 36 | 37 | --- 38 | 39 | Make Your Own ORM for a predetermined schema: 40 | 41 | here it is: 42 | 43 | table person 44 | columns id, name, eye_color 45 | 46 | table friendship 47 | columns id, person_a_id, person_b_id 48 | 49 | Example of functions by the orm: 50 | 51 | Person 52 | 53 | p = Person.create(name, eye_color) 54 | p = Person.read(id) 55 | p = Person.update(id, { name | height | mood }) 56 | p = Person.destory(id) 57 | 58 | they will pull a piece of code that includes table creation statements already, as well as the beginning of their ORM interface, as well as a test suite they can run to test completeness. 59 | 60 | Include a higher level of abstraction for some part of the ORM. 61 | 62 | --- 63 | 64 | On Dialects: 65 | 66 | [SQLite](https://www.sqlite.org/docs.html) 67 | 68 | [Postgres](https://www.postgresql.org/docs/9.6/static/index.html) 69 | 70 | [MySQL](https://dev.mysql.com/doc/refman/5.7/en/) 71 | 72 | [List of Implementations](https://en.wikipedia.org/wiki/List_of_relational_database_management_systems) 73 | 74 | --- 75 | 76 | indexes? joins? acid? injection? normalization? 77 | -------------------------------------------------------------------------------- /code_challenge/anagrams.rb: -------------------------------------------------------------------------------- 1 | require 'pry' 2 | require 'rspec' 3 | =begin 4 | 5 | This one is in Ruby since I felt like only writing tests for one language. 6 | 7 | Let's make a class that can be configured with a list of words, and then will return every 8 | anagram for that word. An anagram is when the letters of one word can be rearranged to create a 9 | different word. 10 | 11 | So for example: 12 | 13 | anagram_machine = AnagramMachine.new(%w(apple ate eat lemon lettuce melon menu salt tea)) 14 | anagram_machine.match(apple) 15 | # => [] 16 | anagram_machine.match(ate) 17 | # => [eat, tea] 18 | anagram_machine.match(lemon) 19 | # => [melon] 20 | anagram_machine.match(last) 21 | # => [salt] 22 | 23 | 24 | Try your code out with pry. When you think you have it, run tests by running: 25 | 26 | $ rspec anagrams.rb 27 | 28 | Red, green, refactor. 29 | 30 | BONUS round: Initialize your class with the words in /usr/share/dict/words and make sure it still 31 | works & doesn't have major performance issues. 32 | 33 | =end 34 | 35 | 36 | 37 | # code here 38 | 39 | 40 | RSpec.describe AnagramMachine do 41 | let(:anagram_machine) { AnagramMachine.new(["apple", "ate", "eat", "lemon", "lettuce", "menu", "salt", "TEA"]) } 42 | # let(:english) {AnagramMachine.new(LANGUAGE)} 43 | describe "#match" do 44 | context "it fails" do 45 | subject { anagram_machine.match('blah') } 46 | 47 | it { is_expected.to eq [] } 48 | end 49 | end 50 | 51 | describe "#match" do 52 | context "it works with an anagram" do 53 | subject { anagram_machine.match('melon') } 54 | 55 | it { is_expected.to eq ["lemon"] } 56 | end 57 | end 58 | describe "#match" do 59 | context "it returns empty if the only anagram is the word itself" do 60 | subject { anagram_machine.match('apple') } 61 | 62 | it { is_expected.to eq [] } 63 | end 64 | end 65 | describe "#match" do 66 | context "it includes anagrams that are of different case in the list" do 67 | subject { anagram_machine.match('eat') } 68 | 69 | it { is_expected.to include "TEA" } 70 | end 71 | end 72 | describe "#match" do 73 | context "it includes anagrams that are of different case in the word" do 74 | subject { anagram_machine.match('EAT') } 75 | 76 | it { is_expected.to include "TEA" } 77 | it { is_expected.to include "ate" } 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /ssh/workshop.md: -------------------------------------------------------------------------------- 1 | # SSH Workshop 2 | 3 | Secure Shell (SSH) is a cryptographic network protocol for operating network services securely over unsecured networks. 4 | 5 | ### lab 6 | 7 | 1. Open a secure shell session using **password authentication**. 8 | 9 | ``` 10 | > ssh username@hostname 11 | [enter password] 12 | [...] 13 | > exit 14 | ``` 15 | 16 | 2. Create your local **.ssh directory** with the right permissions. 17 | 18 | ``` 19 | > mkdir ~/.ssh 20 | > chmod 0700 ~/.ssh 21 | ``` 22 | 23 | 3. **Generate** a public-private **key pair** on your local machine. 24 | 25 | ``` 26 | > ssh-keygen -f ~/.ssh/keyname 27 | [choose a password or no password] 28 | ``` 29 | 30 | 4. Copy your **public key** to the **authorized_keys** file for your user on the server. 31 | 32 | ``` 33 | > ssh-copy-id -i ~/.ssh/keyname username@hostname 34 | [enter password] 35 | ``` 36 | 37 | 5. Open a secure shell session using **publickey authentication**. 38 | 39 | ``` 40 | > ssh -i ~/.ssh/keyname username@hostname 41 | ``` 42 | 43 | 6. Mastering ssh with **ssh_config**. 44 | 45 | ``` 46 | > touch ~/.ssh/config 47 | > chmod 0600 ~/.ssh/config 48 | ``` 49 | ``` 50 | [append to ~/.ssh/config:] 51 | 52 | Host sloth-command 53 | HostName hostname 54 | IdentityFile ~/.ssh/keyname 55 | User username 56 | ``` 57 | ``` 58 | > ssh sloth-command 59 | ``` 60 | 61 | ### game time 62 | 63 | ``` 64 | > ssh sloth-command 65 | > users 66 | [alice bob julia] 67 | > write julia 68 | [enter message for julia] 69 | [^C] 70 | ``` 71 | 72 | 1. player one writes a random word to player two 73 | 2. player two writes a phrase describing the word to player three 74 | 3. player three guesses what word was being described and writes it to player four 75 | 4. goto step 2 until all players have played 76 | 77 | ### resources 78 | 79 | - `man ssh` 80 | - `/AUTHENTICATION` 81 | - `/FILES` 82 | - `man ssh-keygen` 83 | - `man ssh-copy-id` 84 | - `man ssh_config` 85 | - [Search Hacker News for SSH](https://hn.algolia.com/?query=ssh) 86 | - [Simplify Your Life With an SSH Config File](http://nerderati.com/2011/03/17/simplify-your-life-with-an-ssh-config-file/) 87 | - [Upcase Intro to SSH](https://thoughtbot.com/upcase/videos/intro-to-ssh) 88 | - [gitolite advanced ssh usage](http://gitolite.com/gitolite/ssh.html) 89 | - [wikipedia ssh](https://en.wikipedia.org/wiki/Secure_Shell) 90 | - [wikipedia OpenSSH](https://en.wikipedia.org/wiki/OpenSSH) 91 | - [wikipedia PuTTY](https://en.wikipedia.org/wiki/PuTTY) 92 | 93 | -------------------------------------------------------------------------------- /sql/orm/test/models.js: -------------------------------------------------------------------------------- 1 | const { assert } = require('chai'); 2 | 3 | describe('orm', () => { 4 | 5 | let Person; 6 | let Friendship; 7 | 8 | // asynchronously require Person and Friend before running tests. 9 | before(done => { 10 | require('../models')(models => { 11 | Person = models.Person; 12 | Friendship = models.Friendship; 13 | done(); 14 | }); 15 | }); 16 | 17 | 18 | describe('Person', () => { 19 | 20 | it('should exist', () => { 21 | assert.isFunction(Person); 22 | }); 23 | 24 | it('should be able to create a person, and fetch it from the new id', done => { 25 | 26 | // construct a person instance and create it in the database 27 | const p = new Person({ name: 'Giulia', eye_color: 'blue' }); 28 | p.create(err => { 29 | 30 | assert.isNull(err, 'create failed'); 31 | 32 | // construct an instance with the same id and fetch its contents from the database 33 | const alsoP = new Person({ id: p.id }); 34 | alsoP.fetch(err => { 35 | 36 | assert.isNull(err, 'fetch failed'); 37 | 38 | assert.equal(p.name, alsoP.name); 39 | assert.equal(p.eye_color, alsoP.eye_color); 40 | 41 | done(); 42 | }); 43 | }); 44 | }); 45 | 46 | it('should be able to become friends with another person', done => { 47 | 48 | const a = new Person({ name: 'Giulia', eye_color: 'blue' }); 49 | const b = new Person({ name: 'Allon', eye_color: 'brown' }); 50 | 51 | a.create(err => { 52 | assert.isNull(err, 'create a failed'); 53 | b.create(err => { 54 | assert.isNull(err, 'create b failed'); 55 | 56 | a.becomeFriends(b, err => { 57 | 58 | assert.isNull(err, 'friendship failed'); 59 | 60 | // make sure b is a friend of a 61 | a.getFriends((err, friends) => { 62 | assert.isNull(err, 'your friends could not be located'); 63 | assert.sameMembers(friends.map(f => f.id), [b.id]); 64 | 65 | // make sure a is a friend of b 66 | b.getFriends((err, friends) => { 67 | assert.isNull(err, 'your friends could not be located'); 68 | assert.sameMembers(friends.map(f => f.id), [a.id]); 69 | done(); 70 | }); 71 | }); 72 | }); 73 | }); 74 | }); 75 | }); 76 | }); 77 | 78 | describe('Friendship', () => { 79 | 80 | it('should exist', () => { 81 | assert.isFunction(Friendship); 82 | }); 83 | }); 84 | 85 | }); 86 | -------------------------------------------------------------------------------- /regex/README.md: -------------------------------------------------------------------------------- 1 | # Regular Expressions (AKA regex, regexp) 2 | 3 | Regular Expressions are a language for describing search patterns. The concept 4 | comes from the field of formal language theory through the work of Stephen Cole 5 | Kleene in the 1950s. Since then regular expressions have been [implemented many 6 | times] (https://stackoverflow.com/questions/4644847/list-of-all-regex-implementations) 7 | with extended features and expressiveness. Due to this variety, it's rather 8 | difficult to come up with a comprehensive definition of regular expressions. 9 | 10 | # The JavaScript Implementation 11 | 12 | - [Reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) 13 | - [Guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) 14 | 15 | ## Creating a Regex 16 | 17 | Literal Notation: 18 | 19 | ```javascript 20 | var re = /ab+c/; 21 | ``` 22 | 23 | Constructuctor Notation: 24 | 25 | ```javascript 26 | var re = new RegExp('ab+c'); 27 | ``` 28 | 29 | ## Now What? 30 | 31 | | Method | Description | 32 | |---------|----------------------------------------------------------------------------------------------------------------------------------| 33 | | exec | A RegExp method that executes a search for a match in a string. It returns an array of information or null on a mismatch. | 34 | | test | A RegExp method that tests for a match in a string. It returns true or false. | 35 | | match | A String method that executes a search for a match in a string. It returns an array of information or null on a mismatch. | 36 | | search | A String method that tests for a match in a string. It returns the index of the match, or -1 if the search fails. | 37 | | replace | A String method that executes a search for a match in a string, and replaces the matched substring with a replacement substring. | 38 | | split | A String method that uses a regular expression or a fixed string to break a string into an array of substrings. | 39 | 40 | ## But What *is* a Regex? 41 | 42 | Patterns are composed of **literals** which are characters that represent 43 | themselves, and **metacharacters** which have special interpretation. 44 | 45 | For a comprehensive list of metacharacter meaning: [RTFM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Special_characters_meaning_in_regular_expressions) 46 | 47 | For a good cheatsheet and examples, take a look at [regexr](http://regexr.com/). 48 | 49 | # Cool Resources 50 | 51 | - [regexr](http://regexr.com/) 52 | - [regex101](https://regex101.com/) 53 | - [regex crossword](https://regexcrossword.com/) 54 | 55 | -------------------------------------------------------------------------------- /refactoring/refactoring.md: -------------------------------------------------------------------------------- 1 | #Refactoring 2 | 3 | Refactoring is when you take existing code that gets the job done, and you improve the quality, readability, or idiomaticness of your code without any noticable improvement for the user. According to Martin Fowler, who more or less invented the term it means: 4 | 5 | 6 | <blockquote>Refactoring (noun): A change made to the internal structure of software to make it easier to understand and cheaper to modify with-out changing its observable behavior.</blockquote> 7 | 8 | It's a term that is commonly misused. I often hear other programmers say that they are going to refactor something to fix bugs or improve performance. This is not what we are talking about. 9 | 10 | Refactoring is a favor to your future self reading code from ages ago or a collaborator that you want to give the benefit of readable, modular, easily changed code. After you get used to doing it every now and again, you will become skilled at finding 'code smells' and you will know how to fix them. It's also a great way to get to know a codebase. If you go around and improve the code without breaking anything, you will come away knowing what its doing, and your collaborators will thank you (unless they are insecure dicks). 11 | 12 | ##Tests are a big deal here 13 | 14 | The first thing that you need before you refactor code is a robust (or robust enough) test suite so you know that your fancy code improvement isn't the butterfly that flaps its wings in Mombasa and causes a hurricane in Florida. Without that, it's best to let sleeping dogs lie. 15 | 16 | This is part of the reality of software development. People don't always follow best practices, including yours truly. There are such things as deadlines, and unfortunately, not everyone has the luxury of writing clean code. People cut corners to save time, and it ends up costing far more in the long run. 17 | 18 | ##Refactoring kata #1: Extract Method 19 | 20 | ####The smell: Repeated code in multiple places within a class 21 | 22 | ####The solution: DRY it up! 23 | 24 | Check out `extract_method.rb` for a simple example. 25 | 26 | 27 | ##Refactoring kata #2: Extract Class/module 28 | 29 | One class should have one responsibility. Don't forget that in Rails, you can have model classes that aren't representative of Active Record tables. Having classes that each do one thing and know only what they need makes your program a lot easier to understand. 30 | 31 | Keep in mind that cleaner code doesn't always mean *less* code. The Extract Class example shows this clearly. Think of the future though. The more concise version (the before) is harder to read and harder to expand on. 32 | 33 | 34 | ##Help is out there 35 | 36 | Though I personally prefer against them, IDEs such as Ruby Mine and PyCharm from JetBrains offer built in advice. Integrated development environments offer 37 | 38 | For ruby, there is a gem called [Rubocop](https://github.com/bbatsov/rubocop) which studies your code and points out uncleanliness. It's based on the widely followed [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide). Unlike Python, Ruby lacks a canonical set of rules regarding coding style, and some companies have their own style guides. It's a bit strict, but Rubocop provides food for thought when refactoring your code. -------------------------------------------------------------------------------- /closures/README.md: -------------------------------------------------------------------------------- 1 | # closures 2 | 3 | Let's start with some code: 4 | 5 | ```js 6 | var greeter = function (name) { 7 | return function () { 8 | return 'Hello ' + name; 9 | }; 10 | }; 11 | 12 | var daveGreeter = greeter('Dave'); 13 | 14 | daveGreeter(); 15 | // => 'Hello Dave' 16 | ``` 17 | 18 | In the words of Dave Thomas: 19 | 20 | > This works because functions in ~~Elixir~~ Javascript automatically carry 21 | > with them the bindings of variables in the scope in which they are defined. 22 | > In our example, the variable `name` is bound in the scope of the outer 23 | > function. When the inner function is defined, it inherits this scope and 24 | > carries the binding of `name` around with it. This is a *closure*-the scope 25 | > encloses the bindings of its variables, packaging them into something that can 26 | > be saved and used later. 27 | 28 | --- 29 | 30 | ## closures in action 31 | 32 | Encapsulation is an important tool in OOP because it allows us to explicitly 33 | separate the implementation from the interface. 34 | 35 | OOP is about objects, right? 36 | 37 | But importantly, it's about passing messages (method calls) to those objects and 38 | letting them determine how to correctly implement the requested behavior. 39 | 40 | More code: 41 | 42 | ```js 43 | var dave = { 44 | name: 'Dave', 45 | greet: function () { 46 | return 'Hello ' + this.name; 47 | } 48 | }; 49 | dave.greet(); 50 | // => 'Hello Dave' 51 | ``` 52 | 53 | But what happens when client code starts meddling with the implementation? 54 | 55 | ```js 56 | dave.name = 'Poopyhead Dave'; 57 | dave.greet(); 58 | // => 'Hello Poopyhead Dave' 59 | ``` 60 | 61 | Everything goes south. 62 | 63 | --- 64 | 65 | Now this last example is contrived, and Dave's name change is probably Okay. 66 | But in real-world code there may be intricate requirements about how internal 67 | code can be executed, perhaps in specific order, or with arguments that fall in 68 | an expected range. 69 | 70 | With complicated assumptions clients risk misusing the implementation. 71 | 72 | More code: 73 | 74 | ```js 75 | function counter(from, to) { 76 | 77 | var count; 78 | 79 | if (from > to) { 80 | throw new Error('from may not be greater than to'); 81 | } 82 | 83 | function reset() { 84 | count = from; 85 | } 86 | 87 | function next() { 88 | if (count > to) return null; 89 | return count++; 90 | } 91 | 92 | reset(); 93 | 94 | return { 95 | reset: reset, 96 | next: next 97 | }; 98 | 99 | } 100 | var ones = counter(1, 3); 101 | 102 | ones.next(); 103 | // => 1 104 | ones.next(); 105 | // => 2 106 | 107 | ones.reset(); 108 | 109 | ones.next(); 110 | // => 1 111 | ones.next(); 112 | // => 2 113 | ones.next(); 114 | // => 3 115 | ones.next(); 116 | // => null 117 | ``` 118 | 119 | In the case of `counter` there is an explicit public interface (`next`, `reset`) 120 | to interact with the internal state of the object. You can be confident that 121 | your `counter` will always be used properly, in the ways sanctioned by its 122 | public interface. 123 | 124 | To be clear: 125 | 126 | - `reset` is an example of a closure because it encloses over the binding of 127 | `count` and `from` 128 | - `next` is a closure over the bindings of `count` and `to` 129 | 130 | Encapsulation is a more abstract concept which can be realized in javascript 131 | using closures. 132 | 133 | -------------------------------------------------------------------------------- /vim.md: -------------------------------------------------------------------------------- 1 | #Vim! 2 | 3 | In today's class, I am going to get you started with Vim, which is a pretty good text editor and my second favorite. 4 | 5 | But why?! First of all, you don't have to know vim to edit text. There are other options out there. Also, vim users get a bad rap for being imperious and self-righteous. 6 | 7 | ![http://imgur.com/gkaLzYz](https://i.imgur.com/gkaLzYz.png) 8 | 9 | I personally prefer Sublime Text and left to my own devices, I would use Sublime forever and spend my time learning how to be a better coder, not fiddling around with a text editor. But there are some important needs that vim addresses. 10 | 11 | 1. First, it runs right in your terminal, and it comes with Mac and every Linux distribution that I know of. This is important when you SSH into another server and you have to edit text. It's a real pain in the ass syncing files from your desktop to the server ever time you need to save changes. Personally, I didn't even attempt to learn vim until I absolutely had to work on a server at work. I was pretty hamstrung for a few weeks as a result. 12 | 2. It blows away the competition. You can (and probably have) used `nano` and if you are an evil wizard, `emacs` but vim is much more full-featured and there is a whole galaxy of plugins that you can use to speed up your workflow. It's incredibly customizable to the point where using it on someone else's machine is like learning a new language. 13 | 3. It has also been around forever (it's parent, `vi` has been around since the 70s - `vim` means "vi improved") and it's part of the common language for developers on Unix like systems. 14 | 4. You are going to run into it all the time when you are doing stuff like rebasing commits with git or renaming migrations in rails. 15 | 5. It's actually really nice. You might find that you prefer it. There is a 16 | reason why it is the [second most popular dev environment available for Unix 17 | based 18 | systems](https://stackoverflow.com/research/developer-survey-2016#technology-development-environments) 19 | 20 | 21 | ##Getting started 22 | 23 | Open your terminal and type `vim` 24 | 25 | There it is! On a Mac `vi` opens vim 26 | 27 | Now what? 28 | ![joke](https://i.imgur.com/2P07j9T.png) 29 | 30 | Can you quit? 31 | 32 | `:q` 33 | 34 | `:` opens vim's command line at the bottom gutter. Hit `Esc` to leave it. 35 | 36 | `:q!` < --- the `!` is like 'i mean it' `:q!` is the ejection seat in case you can't figure out what you have done. 37 | 38 | Yep. Good 39 | 40 | ##Write to a file 41 | 42 | `:w` 43 | 44 | ##Write and quit 45 | 46 | `:wq` 47 | 48 | There is usually an English acronym/mnemonic to vim commands. It's useful to 49 | learn them along with the command itself. 50 | 51 | ##Opening files 52 | `vim <filename>` 53 | 54 | Open a new file in vim 55 | `:e <filename>` 56 | 57 | ##Modes: 58 | 59 | We won't go over them all. 60 | 61 | `Esc` goes back to 'normal' mode. 62 | 63 | `i` Insert mode. Now the keys type letters and stuff instead of issue commands to vim. Your arrow keys work in insert mode 64 | 65 | ##Moving around character by character: 66 | 67 | ![](http://www.catonmat.net/images/why-vim-uses-hjkl/adm-3a-hjkl-keyboard.jpg) 68 | 69 | This takes some getting used to. I recommend disabling the arrow keys (I'll show 70 | you later). 71 | 72 | ##Moving by word: 73 | 74 | `w` skip forward a word 75 | 76 | `b` move back a word 77 | 78 | ##Lines 79 | 80 | `A` Start editing the end of the line you are on 81 | `$` End of line 82 | `o` Start a new line under what you are on 83 | `O` Start a new line above 84 | `^` Move to beginning of the line 85 | 86 | `G` Go to the end of the file 87 | `gg` Go to the start of the file 88 | 89 | ##Search 90 | 91 | `/` Then type some stuff and hit enter. Hit `n` to find the next match 92 | 93 | ##Deleting stuff 94 | 95 | `x` Delete the one character under your cursor 96 | `dd` Delete the whole line 97 | `r` Change a single character to the next character you type 98 | 99 | 100 | ##Your `.vimrc` 101 | 102 | Disable the arrow keys in normal mode! 103 | 104 | ``` 105 | nnoremap <Left> :echoe "Use h"<CR> 106 | nnoremap <Right> :echoe "Use l"<CR> 107 | nnoremap <Up> :echoe "Use k"<CR> 108 | nnoremap <Down> :echoe "Use j"<CR> 109 | ``` 110 | 111 | Leader key: 112 | 113 | `let mapleader = " "` 114 | 115 | `nmap <leader>e $` 116 | 117 | ##More stuff to get started 118 | 119 | `$ vimtutor` 120 | 121 | `:help` 122 | 123 | [Vimawesome: a ton of great plugins](http://vimawesome.com/) 124 | 125 | [Zelda-like game to learn vim](http://vim-adventures.com/) 126 | 127 | -------------------------------------------------------------------------------- /design_patterns/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns 2 | 3 | What do we want to accomplish with design patterns? 4 | 5 | 1. **Simplification** 6 | 7 | 2. **Decoupling** 8 | 3. **Single Responsibility Principle** 9 | 10 | 11 | 12 | ## Creational Patterns 13 | 14 | These patterns focus on the automatic creation of objects without having to instantiate them directly. This simplifies our interface and allows for the creation of objects through a single interface. The responsibility of object creation will fall on this interface alone, so when we change something in the other objects, we only need to update the factory to pass tests. 15 | 16 | ### Factory 17 | 18 | This pattern uses polymorphism to create a common interface to create different types of objects. This may arise when we see the responsibility of creating multiple objects spread across many other classes/objects. 19 | 20 | ```python 21 | class Circle(object): 22 | def draw(self): 23 | print('im a circle') 24 | 25 | class Square(object): 26 | def draw(self): 27 | print('im a square') 28 | 29 | # single function to create ALL types of shapes 30 | def shape_factory(type): 31 | if type == 'circle': return Circle() 32 | if type == 'square': return Square() 33 | ``` 34 | 35 | 36 | 37 | **Excercise:** 38 | 39 | ```python 40 | class ChessBoard(object): 41 | def __init__(self, rows, columns): 42 | self.board = [[None for _ in range(rows)] for _ in range(columns)] 43 | self.populate_board() 44 | 45 | def populate_board(): 46 | # populate the board with pieces represented by json strings 47 | ``` 48 | 49 | 50 | 51 | ## Structural Patterns 52 | 53 | Structural patterns are focused on the composition of classes and objects. We create different interfaces that define methods to be used by another subsystem. Sometimes these classes will be abstract only. 54 | 55 | ### Facade 56 | 57 | As our code becomes more complex, the number of objects grows. To simplify our high level API we create an interface object to expose methods that handle all of the objects in our system. 58 | 59 | In this example we have a car which is made up of many smaller parts. In this implementation, the user will have to interact with the smaller parts in order to create the whole car. 60 | 61 | ```python 62 | class Car(object): 63 | def __init__(self, tires, fuel_tank): 64 | self.tires = tires 65 | self.fuel_tank = fuel_tank 66 | 67 | class Tire(object): 68 | def __init__(self, orientation, pressure): 69 | self.orientation = orientation 70 | self.pressure = pressure 71 | 72 | def fill(self, amount): 73 | self.pressure = self.pressure + amount 74 | 75 | class FuelTank(object): 76 | def __init__(self, level): 77 | self.level = level 78 | 79 | def fill(self, amount): 80 | self.level = self.level + amount 81 | 82 | # how the user interacts with our system 83 | tire1 = Tire('front-left', 100) 84 | tire2 = Tire('front-right', 100) 85 | tire3 = Tire('back-left', 100) 86 | tire4 = Tire('back-right', 100) 87 | 88 | fuel_tank = FuelTank(100) 89 | 90 | my_car = Car([tire1, tire2, tire3, tire4], fuel_tank) 91 | my_car.fuel_tank.fill(10) 92 | ``` 93 | 94 | 95 | 96 | **Excercise**: 97 | 98 | How can we make this better? 99 | 100 | Which class can act as our facade? 101 | 102 | 103 | 104 | ### Adapter 105 | 106 | This pattern allows us to create an adapter class for an object that is not compatible with an interface. Let's say we are making a game where, whenever someone clicks an object, it makes a sound. The problem is we have so many objects, but we want a simple and clean implementation of a **click_object** function. 107 | 108 | ```python 109 | class Bush(object): 110 | def __init__(self, color): 111 | self.color = color 112 | 113 | def rustle(self): 114 | return 'rustling sound' 115 | 116 | class Animal(object): 117 | def __init__(self, species, sound): 118 | self.species = species 119 | self.sound = sound 120 | 121 | def make_sound(self): 122 | return self.sound 123 | 124 | def click_object(thing): 125 | print(thing.make_sound()) 126 | 127 | # adapter class for our incompatible Bush 128 | class BushAdapter(object): 129 | def __init__(self, bush): 130 | self.bush = bush 131 | 132 | def make_sound(self): 133 | return self.bush.rustle() 134 | 135 | # now we click a bush using the same function 136 | bush = BushAdapter(Bush('red')) 137 | tiger = Animal('tiger', 'tiger purr') 138 | 139 | click_object(bush) 140 | click_object(tiger) 141 | ``` 142 | 143 | 144 | 145 | ## Behavioral Patterns 146 | 147 | ### Chain of Responsibility 148 | 149 | ```python 150 | class LogHandler(object): 151 | 152 | class DatabaseHandler(object): 153 | def execute(self): 154 | ``` 155 | 156 | -------------------------------------------------------------------------------- /react/README.md: -------------------------------------------------------------------------------- 1 | # no magic 2 | 3 | Typing helps wear out the magic. Learn this by doing it yourself. 4 | 5 | We'll be interacting directly with `ReactDOM` and `React`, so don't hesitate to consult the [documentation](https://facebook.github.io/react/docs/react-api.html). 6 | 7 | 1. create a project directory 8 | 9 | ```sh 10 | cd wherever/you/like/to/code 11 | mkdir no-magic 12 | cd no-magic 13 | ``` 14 | 15 | 2. grab `react.min.js` and `react-dom.min.js` 16 | 17 | ```sh 18 | curl https://unpkg.com/react@15/dist/react.min.js -Lo "react.min.js" 19 | curl https://unpkg.com/react-dom@15/dist/react-dom.min.js -Lo "react-dom.min.js" 20 | ``` 21 | 22 | 3. create an unmagical `script.js` 23 | 24 | ```javascript 25 | ReactDOM.render( 26 | React.createElement('h1', null, 'Hello, World!'), 27 | document.getElementById('root')); 28 | ``` 29 | 30 | 4. create an unmagical `index.html` 31 | 32 | ```html 33 | <!DOCTYPE html> 34 | <html> 35 | <head> 36 | <meta charset="utf-8"> 37 | <title>no magic react 38 | 39 | 40 |
41 | 42 | 43 | 44 | 45 | ``` 46 | 47 | 5. serve the static website you just made 48 | 49 | ```sh 50 | python -m SimpleHTTPServer 51 | ``` 52 | 53 | 5. [profit](http://127.0.0.1:8000/) 54 | 55 | ## let's break that down 56 | 57 | ```javascript 58 | ReactDOM.render( 59 | React.createElement('h1', null, 'Hello, World!'), 60 | document.getElementById('root')); 61 | ``` 62 | 63 | | | | description | 64 | |---|---|---| 65 | |[ReactDOM.render](https://facebook.github.io/react/docs/react-dom.html#render)||Render a React element into the DOM in the supplied container| 66 | |[React.createElement](https://facebook.github.io/react/docs/react-api.html#createelement)|element|Create and return a new React element of the given type| 67 | |[document.getElementById](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById)|container|Returns a reference to the element by its ID| 68 | 69 | ``` 70 | React.createElement( 71 | type, 72 | [props], 73 | [...children] 74 | ) 75 | ``` 76 | 77 | ## we can type less 78 | 79 | ```javascript 80 | var c = React.createElement; 81 | ``` 82 | 83 | Using 2-space indentation and a single letter abbreviation makes nested elements indent properly - [source](https://github.com/ustun/react-without-jsx). 84 | 85 | ```javascript 86 | c('div', null, 87 | c('h2', null, 'My Story'), 88 | c('p', null, 'Once upon a time, in a land far far away...'), 89 | c('div', null, 90 | c('h3', null, 'Subplot'), 91 | c('p', null, 'Lucy wanted a machine gun for her birthday, but mama would not have it.')), 92 | c('p', null, '...But back to the story!')) 93 | ``` 94 | 95 | ## componenets and state 96 | 97 | Let's add a `Counter` component to `script.js` using [React.createClass](https://facebook.github.io/react/docs/react-api.html#createclass): 98 | 99 | ```javascript 100 | var c = React.createElement; 101 | 102 | var Counter = React.createClass({ 103 | 104 | displayName: 'Counter', 105 | 106 | getInitialState: function () { 107 | return { count: 0 }; 108 | }, 109 | 110 | countBy: function (delta) { 111 | this.setState(function (previousState) { 112 | return { count: previousState.count + delta }; 113 | }); 114 | }, 115 | 116 | render: function() { 117 | return ( 118 | c('div', null, 119 | c('button', { onClick: this.countBy.bind(this, 1) }, 'up'), 120 | c('div', null, this.state.count), 121 | c('button', { onClick: this.countBy.bind(this, -1) }, 'down')) 122 | ); 123 | } 124 | 125 | }); 126 | 127 | ReactDOM.render( 128 | c(Counter, null), 129 | document.getElementById('root')); 130 | 131 | ``` 132 | 133 | ## interesting bits? 134 | 135 | > With React.createClass(), you have to provide a separate getInitialState method that returns the initial state: 136 | 137 | ```javascript 138 | getInitialState: function () { 139 | return { count: 0 }; 140 | } 141 | ``` 142 | 143 | > props... 144 | 145 | ```javascript 146 | c('button', { onClick: this.countBy.bind(this, 1) }, 'up') 147 | ``` 148 | 149 | > The type argument can be either a tag name string (such as 'div' or 'span'), or a React component type (a class or a function). 150 | 151 | ```javascript 152 | c(Counter, null) 153 | ``` 154 | 155 | ## create-react-app: under the hood 156 | 157 | ```sh 158 | # create and eject an app 159 | create-react-app everything-is-on-fire 160 | cd everything-is-on-fire 161 | npm run eject 162 | 163 | echo "a fresh create-react-app depends on $(ls node_modules | wc -l) modules" 164 | echo "the ejected app has this file structure..." 165 | tree -I node_modules 166 | 167 | # do some exploring... 168 | 169 | cd .. 170 | rm -rf everything-is-on-fire 171 | ``` 172 | -------------------------------------------------------------------------------- /ssh/lesson-plan.md: -------------------------------------------------------------------------------- 1 | # SSH Workshop 2 | 3 | ### addstudent 4 | 5 | To add a student user, run: 6 | 7 | ``` 8 | > sudo addstudent username 9 | [...] 10 | created student with password username:initialpassword 11 | > 12 | ``` 13 | 14 | This will create an unprivileged user with CPU, RAM, and Disk limits. 15 | 16 | The initial password is immediately expired, requiring the student to set their own when they first ssh. 17 | 18 | ### delstudent 19 | 20 | To remove a student user, run: 21 | 22 | ``` 23 | > sudo delstudent user 24 | ``` 25 | 26 | This will remove their home directory and disk quota. 27 | 28 | Warning: any files they created outside of their home directory will continue to exist. 29 | 30 | ### remember 31 | 32 | - keep track of time 33 | - engagement 34 | - have students be complete participants to the lesson 35 | - ask questions 36 | - closing is as important as opening 37 | - review the material 38 | - leave time for this 39 | 40 | ### discussion 41 | 42 | 1. "what is heroku used for, and what are the different ways that you can interact with heroku?" 43 | - command line client 44 | - git push 45 | - web frontend 46 | 47 | 2. "why would you want to run your own server?" 48 | - how does heroku make heroku? 49 | - herokus all the way down :P 50 | - performance/cost 51 | - lower rates for equivelant resources 52 | - also the ability to prioritize certain kinds of resource configurations 53 | - CPU 54 | - Primary Memory Capacity/Rates 55 | - Seconday Memory Capacity/Rates 56 | - Internet Rates 57 | - Custom Network Topology (Private Network at Least) 58 | [physical control] 59 | - For physical 60 | - security for regulated industries or projects with special needs 61 | - or ease of mind - the less you have to trust, the better 62 | - control 63 | - run whichever software you want (mysql) 64 | - no daily reboot 65 | 66 | 3. "why would you want to run your web application on heroku?" 67 | - let professionals worry about the server going down on Saturday at 2am 68 | - they do the system administration for you 69 | 70 | 4. "we need a way to communicate with our server" 71 | 72 | ### definition 73 | 74 | Secure Shell (SSH) is a cryptographic network protocol for operating network services securely over unsecured networks. 75 | 76 | ### lab 77 | 78 | 1. Open a secure shell session using **password authentication**. 79 | 80 | ``` 81 | > ssh username@hostname 82 | [enter password] 83 | [...] 84 | > exit 85 | ``` 86 | 87 | - these commands (`[...]`) will be running on the server 88 | - `exit` or `^D` will end the session which means commands run locally 89 | 90 | 2. Create your local **.ssh directory** with the right permissions. 91 | 92 | ``` 93 | > mkdir ~/.ssh 94 | > chmod 0700 ~/.ssh 95 | ``` 96 | 97 | - see `man ssh` `/FILES` 98 | 99 | 3. **Generate** a public-private **key pair** on your local machine. 100 | 101 | ``` 102 | > ssh-keygen -f ~/.ssh/keyname 103 | [choose a password or no password] 104 | ``` 105 | 106 | - `ls ~/.ssh` notice the two files 107 | - `cat ~/.ssh/keyname``.pub` 108 | - the private key (no pub) should never ever leave your computer (never be written over a network) 109 | - its secrecy is the only thing that mathematically proves the authenticity of the public key 110 | 111 | 4. Copy your **public key** to the **authorized_keys** file for your user on the server. 112 | 113 | ``` 114 | > ssh-copy-id -i ~/.ssh/keyname username@hostname 115 | [enter password] 116 | ``` 117 | 118 | - this makes use of password authentication before publickey authentication is available 119 | 120 | 5. Open a secure shell session using **publickey authentication**. 121 | 122 | ``` 123 | > ssh -i ~/.ssh/keyname username@hostname 124 | ``` 125 | 126 | - we should talk about the advantages/disadvantages of publickey authentication over password authentication 127 | 128 | 6. Mastering ssh with **ssh_config**. 129 | 130 | ``` 131 | > touch ~/.ssh/config 132 | > chmod 0600 ~/.ssh/config 133 | ``` 134 | ``` 135 | [append to ~/.ssh/config:] 136 | 137 | Host sloth-command 138 | HostName hostname 139 | IdentityFile ~/.ssh/keyname 140 | User username 141 | ``` 142 | ``` 143 | > ssh sloth-command 144 | ``` 145 | 146 | - `man ssh_config` 147 | - and the tutorial 148 | 149 | ### game time 150 | 151 | ``` 152 | > ssh sloth-command 153 | > users 154 | [alice bob julia] 155 | > write julia 156 | [enter message for julia] 157 | [^C] 158 | ``` 159 | 160 | 1. player one writes a random word to player two 161 | 2. player two writes a phrase describing the word to player three 162 | 3. player three guesses what word was being described and writes it to player four 163 | 4. goto step 2 until all players have played 164 | 165 | ### resources 166 | 167 | - `man ssh` 168 | - `/AUTHENTICATION` 169 | - `/FILES` 170 | - `man ssh-keygen` 171 | - `man ssh-copy-id` 172 | - `man ssh_config` 173 | - [Search Hacker News for SSH](https://hn.algolia.com/?query=ssh) 174 | - [Simplify Your Life With an SSH Config File](http://nerderati.com/2011/03/17/simplify-your-life-with-an-ssh-config-file/) 175 | - [Upcase Intro to SSH](https://thoughtbot.com/upcase/videos/intro-to-ssh) 176 | - [gitolite advanced ssh usage](http://gitolite.com/gitolite/ssh.html) 177 | - [wikipedia ssh](https://en.wikipedia.org/wiki/Secure_Shell) 178 | - [wikipedia OpenSSH](https://en.wikipedia.org/wiki/OpenSSH) 179 | - [wikipedia PuTTY](https://en.wikipedia.org/wiki/PuTTY) 180 | 181 | -------------------------------------------------------------------------------- /interviewing/code_challenge skills.md: -------------------------------------------------------------------------------- 1 | # Acing the code interview 2 | 3 | Coding interviews scare the bejesus out of me. I must admit that I dread them. The unfortunate fact of life for developers is that high-pressure, high-stakes whiteboard coding is not going anywhere any time soon. Interviewers do this because an astonishing number of otherwise qualified looking applicants can't do fizzbuzz. We hate being on both sides of the table in this situation but it cannot be helped. 4 | 5 | Today I would like to talk about some ways to maximize your chances of succeeding in an interview setting. You've gotten in the door, a busy engineer is devoting some of their time to evaluating you, now what? 6 | 7 | ## The variables that you can control 8 | 9 | There are several things that could maximize your chances of landing a job. 10 | 11 | 1. Personal referral 12 | 2. Confidence, a good personality 13 | 3. A relevant degree 14 | 4. Years of experience in the industry or specific technology in the company's stack 15 | 5. Solving sample problems within the time allotted and demonstrating sophisticated problem solving skills and awareness of the efficiency of your code. 16 | 17 | A personal referral can be huge. Every company gets piles of resumes, but an actual referral from someone puts you on third base. Unfortunately, you only have a limited number of friends and cousins who can vouche for you. You should absolutely get yourself out there IRL at tech meetups and online on developer networks like dev.to. Expanding your network only goes so far. 18 | 19 | Your personality is hard to change or fake. 20 | 21 | College is expensive, don't go back to school, you will spend the rest of your life in debt. 22 | 23 | Too bad you are just starting out, you need to get that first offer, kick ass, and that's that. 24 | 25 | So 1 - 4 are more or less constants right now, the variable that you have the most leverage over is your ability to shine in an interview, especially the technical part. 26 | 27 | ## Non-technical stuff 28 | 29 | ![haha](https://i2.wp.com/www.developermemes.com/wp-content/uploads/2014/09/Web-Developer-With-A-Job-Web-Developer-Without-A-Job-Meme.jpg?w=625) 30 | 31 | Generally, web developers dress like eight-year-old kids, but leave the sandals and shoes behind today. Put on some clothes that don't have pizza on them. Formal attire comes off as kind of thirsty and makes you look like you are going for sales positions, but just dress like you give a damn. 32 | 33 | Get to know the company FFS. If a listing or a recruiter mentions the product or the team, look them up. Read a little bit about the industry and problem domain that the team works on. If you are applying at a real estate related company, being totally ignorant about real estate will hurt you. 34 | 35 | If you talked to someone and know their name, look them up on LinkedIn (ugh I know), Twitter, or whatever. You might have friends in common for all you know, or maybe you are both into something niche like Nu Metal, Kendo, or Belgian history or something. 36 | 37 | As I mentioned last week, you will be asked questions about stuff on your resume. Don't think you can bullshit here. You better have a story or two to tell about how you got to know a particular tool, how it differs from others and such. On the flip side, if you are asked something that you don't know, just say you don't know! A passionate, confident, and honest interviewee is what thou wants to be. 38 | 39 | ## Parsing technical interview questions 40 | 41 | Some pointers before you start coding or whiteboarding: 42 | 43 | #### Ask clarifying questions 44 | 45 | The worst thing you can do in a technical interview is just charge up the wrong mountain. Solving the wrong problem says to the interviewer that you are going to waste time on stuff and not get your work done. 46 | 47 | Ask your interviewer for specs, requirements, and inputs. Making an assumption right out of the gates will ruin your chances of success. Also, if something sounds really really hard, you might just be misunderstanding it. The interviewers usually ask something that they know a decent candidate would be able to solve in a limited timeframe. If something takes like more than a dozen or two lines of code, you might be on the wrong track. 48 | 49 | Walk through the problem out loud, create an example of the implementation in action. 50 | 51 | #### Brute force it first 52 | 53 | At the very least, you want to solve the problem given to you. Look for the simplest possible solution and _then_ go for the O(1) or O(n) solution by refactoring. You might run out of time just getting to brute force! 54 | 55 | Spell out a ham-fisted but functional solution, then say, "of course this would create huge problems if the inputs were much larger than this simple example so…" and go on to address your shortcomings and optimize your solution. 56 | 57 | #### Don't sweat small stuff until you are actually done 58 | 59 | Interviewers rarely care about your syntax, perfect indentation, and sometimes if your thought process was sound, won't care if your code actually works. They realize that your code editor or IDE takes care of that. 60 | 61 | There are of course exceptions. If you apply at a shop that specializes in TDD, by all means write a test first! This is where pre-interview prep and familiarity with the company come in handy. 62 | 63 | Also, save edge cases for the end. For instance if you have to sort an array, ask if you can assume clean inputs of the same type at first. This is the opposite of how you should behave in a production environment but c'est la vie. 64 | 65 | ## DO IT LIVE 66 | 67 | ![https://giphy.com/gifs/tHdS8HFtuawZa/html5](https://media.giphy.com/media/tHdS8HFtuawZa/giphy.gif) 68 | 69 | This is a version of a question that I hear comes up a lot in interviews at Google (they basically encourage technical interviewers to crib from _Cracking the Coding Interview_) 70 | 71 | You can use any language you like. Let's try to do this with Phillip and I acting as ~~tormentors~~ interviewers for half the class respectively. 72 | -------------------------------------------------------------------------------- /big-o.md: -------------------------------------------------------------------------------- 1 | # Big O notation and algorithm efficiency 2 | 3 | Not all algorithms (read: blocks of code that solve a problem) are created equally. 4 | 5 | It's one thing to get the right output. Depending on how you are getting your result, you might be setting yourself up for some really inefficient use of computing resources. 6 | 7 | Normally, it is _obvious_ that your code is inefficient if you notice your CPU cooling fan going berserk and your code taking forever to run. This is ultimately why programmer have to care about efficiency. If your sloppy code is making too many assignments, bloating memory unnecessarily, and using too many CPU cycles, you will find yourself paying more for your severs and/or users will have to wait longer to get a response. 8 | 9 | Devops engineers be like: 10 | 11 | ![shitty code](https://cdn-images-1.medium.com/max/1600/0*uoMExsEFIS8HZyYU.gif) 12 | 13 | 14 | 15 | If this is the case, it would be a good idea to triage your problem and use a service such as [Scout](https://scoutapp.com/) or [New Relic](https://newrelic.com/) to pinpoint the sluggish code. 16 | 17 | ## Hardware agnostic analysis 18 | 19 | | Tired | Wired | 20 | | ---------------------------------------- | ---------------------------------------- | 21 | | Timing a function given different inputs to see which bloat up and run slowly | Using a hardware agnostic notation to describe the consequences of running the code on a bigger input | 22 | 23 | Enter Big O notation. The letter O is used because the growth rate of a function is also referred to as **order of the function**. Order is another way of expressing the rate of growth of a function. 24 | 25 | Big O is a formalized notation that classifies algorithms according to how much runtime or resource use grows as the input size grows. It means nothing if I run some code on a recent ish MacBookPro and someone else runs the same code on like a dogshit Kindle Fire. It has nothing to do with seconds or milliseconds or kilobytes or whatnot. It's purely mathematical and you can do it without a computer if you so wish. 26 | 27 | These are the common Big O types: 28 | 29 | | Big O type | Name | 30 | | ---------- | ----------- | 31 | | O(1) | Constant | 32 | | O(log (n)) | Logarithmic | 33 | | n | Linear | 34 | | O(nlog(n)) | Log Linear | 35 | | O(n^2) | Quadratic | 36 | | O(n^3) | Cubic | 37 | | O(2^n) | Exponential | 38 | 39 | Here is a graph visualizing what this actually means for runtime: 40 | 41 | 42 | 43 | ![bit o graph](https://interactivepython.org/runestone/static/pythonds/_images/newplot.png) 44 | 45 | I think you can see which types of Big O types are desirable. 46 | 47 | 48 | 49 | ## Some examples of Big O in Python 50 | 51 | ### Constant 52 | 53 | No matter what, a constant Big O has to do the same amount of work regardless of the input. One example that I found was a company in South Africa that was complaining about the internet speed so it demonstrated that a carrier pigeon can carry X gigabytes to the other office some ways away faster than their crappy internet connection. Everyone was like wtf! 54 | 55 | But think about it, a carrier pigeon can carry a micro SD card with 1kb as easily as it can carry 200 gb. Pigeon don't care. Pigeon has a Big O of 1. On the internet, more data === more time. After a certain threshold, the pigeon is faster. Here is an example in Python code: 56 | 57 | ```python 58 | def constant_function(n): 59 | print n[0] 60 | ``` 61 | 62 | This function is constant. Whether the list `n` is 2 or a bajillion, it takes the same amount of time. 63 | 64 | ### Linear 65 | 66 | Linear is like a book. The number of pages directly correlates to how long it takes (assuming similar font size, margins, and spacing, which is something that I know a lot about having dicked around until the night before shit is due in college). 67 | 68 | Here is an example: 69 | 70 | ```python 71 | def linear_function(n): 72 | for value in n: 73 | print value 74 | ``` 75 | 76 | The longer this is, the more operations the Python runtime has to do. 77 | 78 | ### Quadratic 79 | 80 | Quadratic functions do the square of the operations of the length of the input. A good example is a nested loop like so: 81 | 82 | ```python 83 | def quadratic_function(n): 84 | for i in n: 85 | for j in n: 86 | print i, j 87 | ``` 88 | 89 | Assuming that there is a list of [1,2,3], the computer will have to do nine things, that's three squared. n times n. 90 | 91 | In an interview setting, you might be asked to find the order for a particular algorithm, they might fit in one of these buckets, or it might be something random such as O(2n). 92 | 93 | 94 | 95 | # Best case, worst case 96 | 97 | We are much more interested in the worst case scenarios than the best case scenarios. In some cases, say a function that breaks at a random interval somewhere between 1 and n, the order is effectively n, since that is the worst case. O(2n) is effectively O(n). 98 | 99 | 100 | 101 | ## Quiz 102 | 103 | What is the big O value for these simple functions? 104 | 105 | ```python 106 | def startup_idea(n): 107 | for x in range(n): 108 | for y in range(n): 109 | print 'yo!' 110 | ``` 111 | 112 | ```python 113 | def done_machine(n): 114 | for x in range(n): 115 | print 'it is done' 116 | ``` 117 | 118 | ```python 119 | def counter(n): 120 | w = 0 121 | for x in range(n): 122 | w = w + 1 123 | 124 | for y in range(n): 125 | w = w - 1 126 | ``` 127 | 128 | ```python 129 | def contains(n, i): 130 | '''Returns true if i is in the list n.''' 131 | for x in n: 132 | if x == i: 133 | return True 134 | return False 135 | ``` 136 | 137 | 138 | 139 | Some cool links for learning more: 140 | 141 | http://bigocheatsheet.com/ 142 | 143 | https://stackoverflow.com/questions/487258/what-is-a-plain-english-explanation-of-big-o-notation/487278#487278 144 | 145 | https://www.youtube.com/watch?v=MyeV2_tGqvw -------------------------------------------------------------------------------- /docker/more_docker/README.md: -------------------------------------------------------------------------------- 1 | # More Docker 2 | 3 | ### docker-machine 4 | 5 | Let's setup a proper docker machine locally. To do this we will use the **docker-machine create** command to spin up a new docker host for our containers. 6 | 7 | Before we create, we need to checkout the documentation: https://docs.docker.com/machine/reference/create/ 8 | 9 | As we can see, a **driver** is necessary to create a docker machine. The driver will be determined by the provider available to us on our machine or the remote provider we would like to utilize (e.g. AWS). Let's take a look at the list of drivers supported by docker machine: https://docs.docker.com/machine/drivers/ 10 | 11 | For our purposes we will be using `virtualbox` (Mac/Linux) and `hyperv` (Windows) drivers, depending on the operating system you are using. 12 | 13 | Let's create a machine for deploying our staging environment. 14 | 15 | ```bash 16 | docker-machine create --driver virtualbox staging 17 | ``` 18 | 19 | **OR** 20 | 21 | ```shell 22 | docker-machine create --driver hyperv staging 23 | ``` 24 | 25 | We will see our machine start up and get assigned an IP. The assigned IP will be our app's IP for us to access. To get the IP of a specific machine we can use the **ip** command. 26 | 27 | ```shell 28 | docker-machine ip staging 29 | ``` 30 | 31 | Alternatively we list all of our machines and their IPs using the list command 32 | 33 | ```shell 34 | docker-machine ls 35 | ``` 36 | 37 | Now we have the power to spin up multiple machines!! 38 | 39 | ##### Switching between machines 40 | 41 | We have our machine and we want to start deploying our application on this machine. Let's first check out the environment running on the machine we just created using the **env** command 42 | 43 | ```shell 44 | docker-machine env staging 45 | ``` 46 | 47 | What does it return? Anything interesting there? 48 | 49 | At the bottom of the output, we see two comment lines. The env command is showing us how to configure our shell to use the machine we just created. It looks like a strange command, but we will be using this a lot when deploying to different machines. 50 | 51 | Let's go ahead and configure our shell for our staging machine. 52 | 53 | ```shell 54 | eval $(docker-machine env staging) 55 | ``` 56 | 57 | Our shell is now connected to our brand new docker-machine. 58 | 59 | Let's list our docker images to ensure this is the fresh machine we just created. 60 | 61 | ```shell 62 | docker images 63 | ``` 64 | 65 | We should see no images listed! 66 | 67 | 68 | 69 | ### docker-compose 70 | 71 | With our fresh machine in hand, it is time to refine our compose file to sort out some of the issues faced last time. As always let's start by checking out some documentation. 72 | 73 | https://docs.docker.com/compose/compose-file/compose-file-v2/ 74 | 75 | First thing we notice is the **version**. As docker devs continue to refine the compose process, new configurations are released to make deployment even easier. 76 | 77 | Let's add the latest version definition to our docker-compose file. 78 | 79 | ###### docker-compose.yml 80 | 81 | ```yaml 82 | version: '2' 83 | services: 84 | api: 85 | build: . 86 | command: npm run start 87 | ports: 88 | - "3000:3000" 89 | links: 90 | - db:db 91 | db: 92 | image: mongo:3.0.2 93 | ``` 94 | 95 | We now define our db and api under the services key. This is a much better and more explicit definition of our staging environment. 96 | 97 | Next we are going to take a look at a few other service configuration options. 98 | 99 | #### restart 100 | 101 | This option let's us define how/if a service should restart under certain conditions. To solve the problem of failed db connections, when the database is not available, we will define a restart policy to always restart if stopped or exited due to an error. 102 | 103 | ```yaml 104 | services: 105 | api: 106 | ... 107 | restart_policy: always 108 | ``` 109 | 110 | #### depends_on 111 | 112 | This option defines if a service relies on another service to start before it can start itself. Since our app requires the db to start up, let's define our depends on. 113 | 114 | ```yaml 115 | services: 116 | api: 117 | ... 118 | depends_on: 119 | - db 120 | ``` 121 | 122 | Time to spin up our system. 123 | 124 | ```shell 125 | docker-compose up 126 | ``` 127 | 128 | Now we can access our app at the IP we listed earlier. 129 | 130 | We can use postman to make a post request to create a todo in our staging environment. 131 | 132 | What will happen when we stop our docker containers and restart?? 133 | 134 | 135 | 136 | #### Volumes 137 | 138 | There is a problem with our db service. Everytime we deploy a new version of our application, the database loses all of the data we had before. In staging, we may have sample data that we want to persist across deployments. This is where volumes come into play. With volumes we can define directories that are linked from the host to the docker container. Since our host will continue running even when containers are down, we can persist the data there by linking. Let's refine our db service. 139 | 140 | ```yaml 141 | services: 142 | db: 143 | image: mongo:3.0.2 144 | command: mongod -dbpath /data/db --smallfiles 145 | volumes: 146 | - /data/db:/data/db 147 | ``` 148 | 149 | We have defined our own start up command so that we can use the —smallfiles option for staging environment, otherwise mongo requires a large amount of space on our virtualbox, which we do not have. 150 | 151 | In the volumes definition we define it by first giving our host directory, then followed by the container directory. **data/db** is a standard location for these kinds of files so we are going to attach the host data/db as a volume to the container. 152 | 153 | Now when we start our app we can create a new todos, shut down and remove our containers, and restart them, and the data is still there. 154 | 155 | 156 | 157 | ##### Replication and Replacement 158 | 159 | The concept we explored above further exemplifies the ideas behind containers. Containers are meant to be **replaced** and redeployed when new versions of the app become available. By removing the data file from the database process or application, we are removing the concern of persisting data to another source. 160 | 161 | In addition, when we want to **replicate** the application and scale horizontally, we require this sort of separation in order for our mongo service to serve up the same data across all containers. Without this separation you would never know what data you are accessing when connecting to the most available mongo service. -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | ## Deploying with Docker 2 | 3 | ### What is docker? 4 | 5 | Docker is a software container platform that allows you to run apps side by side. Containers do not run full OS, but instead run a minimal system with libraries and settings required to run your application. Think of containers has containing just enough to make it work. 6 | 7 | VMs | Docker 8 | :-------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------: 9 | ![VM Architecture](https://www.docker.com/sites/default/files/VM%402x.png "VMs") | ![Docker Architecture](https://www.docker.com/sites/default/files/Container%402x.png "Docker") 10 | 11 | Docker gets flack for not being as fast as VMs, and many people question why switch to docker? The reason is resource efficiency. Docker machines unlike VMs, can run any docker container no matter which application it may be. VMs are set up specifically for one type of application and can be scaled per cluster of VMs. 12 | 13 | ### Deployment? 14 | 15 | Docker Toolbox contains tools to coordinate and configure the applications you wish to deploy. 16 | 17 | | CLI Tool | Responsibility | 18 | | -------------- | ---------------------------------------- | 19 | | docker | building, running, stopping images and containers based on a configuration file called the Dockerfile | 20 | | docker-compose | running and building applications together and make necessary connections, setting, envs | 21 | | docker-machine | managing the docker machines (e.g. servers/vms running docker) remotely and locally | 22 | 23 | 24 | 25 | ### Installation 26 | 27 | Follow the setup instructions for your OS: 28 | 29 | https://docs.docker.com/engine/installation/ 30 | 31 | Once you have installed the docker toolbox, you can run the following container to confirm everything is setup correctly. 32 | 33 | ```bash 34 | docker run hello-world 35 | ``` 36 | 37 | Also, let's ensure we have all of the tools. 38 | 39 | ```shell 40 | docker -v 41 | docker-compose -v 42 | docker-machine -v 43 | ``` 44 | 45 | 46 | 47 | ### Docker and the Dockerfile 48 | 49 | The dockerfile is the main configuration file for the container that your app will run inside. Dockerfile commands include: 50 | 51 | | Command | Description | 52 | | :------ | ---------------------------------------- | 53 | | FROM | command that pulls a base image from dockerhub or local images | 54 | | RUN | runs a shell command | 55 | | ENV | set an environment variable | 56 | | ADD | add files to the container | 57 | | EXPOSE | open a container port to allow connections from the outside | 58 | | CMD | set the command to execute when starting the container | 59 | 60 | The FROM command is very important as it gives us a base image for building our container. Dockerhub is a great resource for finding a base container for just about every piece of software out there. Let's take a look: 61 | 62 | https://hub.docker.com/explore/ 63 | 64 | Todos API finished: https://github.com/trivett/finished-example-todos-api 65 | 66 | Let's create the Dockerfile for our TODOS API. Dockerfiles are usually located in the root directory of the application they are containerizing. For our purposes, the dockerfile below is a sufficient start to get it running. 67 | 68 | ###### Dockerfile 69 | 70 | ```dockerfile 71 | # set the base image, must be the first instruction in the file 72 | FROM node:7-onbuild 73 | # set our app root as an environment variable 74 | ENV APP_HOME /app 75 | # create the app root folder 76 | RUN mkdir $APP_HOME 77 | # add our package.json and install the dependencies 78 | ADD package.json $APP_HOME 79 | RUN npm install 80 | # add the rest of our application files 81 | ADD . $APP_HOME 82 | # expose port 3000 so we can connect to our api 83 | EXPOSE 3000 84 | # set the default run command to start our app 85 | CMD npm run start 86 | ``` 87 | 88 | Now that we have defined our container build config, lets go ahead and build it. Run the following command inside of the project root. 89 | 90 | ```shell 91 | docker build -t todos_api:v1 . 92 | ``` 93 | 94 | **Note:** each command runs in succession creating layers in the build process. These layers will increase the efficiency of rebuilding your app when source code changes are made. Keep this in mind when structuring dockerfiles. 95 | 96 | If the build was successful, we should see our newly created image in our list. 97 | 98 | ```shell 99 | docker images 100 | ``` 101 | 102 | Now let's run our container. 103 | 104 | ```shell 105 | docker run todos_api:v1 106 | ``` 107 | 108 | What happened? 109 | 110 | 111 | 112 | ### Docker-Compose 113 | 114 | Remember that mongod process thing that we needed to run our application? Well we still need it and we need to connect it to the container running our API. 115 | 116 | This is where docker-compose comes in. With compose we can coordinate running containers and connect them together. The docker-compose configuration is a YAML file that will also live in the project root. 117 | 118 | In the file we define each service that we want to run. Each service will be spawned in its own container based on a combination of the compose file, and the apps Dockerfile. Let's take a look at some more documentation: 119 | https://docs.docker.com/compose/compose-file/ 120 | 121 | ###### docker-compose.yml 122 | 123 | ``` 124 | api: 125 | build: . 126 | command: npm run start 127 | ports: 128 | - "3000:3000" 129 | links: 130 | - db:db 131 | environment: 132 | - MONGODB_URI=mongodb://db:27017/TodoApp # replaced localhost with db as per link 133 | db: 134 | image: mongo:3.0.2 135 | ``` 136 | 137 | 138 | 139 | With one command we can create and start both containers in sync and connected. 140 | 141 | ```shell 142 | docker-compose up 143 | ``` 144 | 145 | This works for the first run. If we make changes to our source code and want to rebuild the image, then we need to call the build command first. 146 | 147 | ```shell 148 | docker-compose build 149 | ``` 150 | 151 | We should now have two running containers with a link to communicate to one another. We just deployed two applications in isolated containers! 152 | 153 | To access the application we must first figure out the IP of our local docker-machine 154 | 155 | ```shell 156 | docker-machine ip default 157 | ``` 158 | 159 | Since we opened port 3000 in our container, we can access the application at {DOCKER_MACHINE_IP}:3000/todos 160 | -------------------------------------------------------------------------------- /docker/more_docker/DEPLOYING_TO_AWS.md: -------------------------------------------------------------------------------- 1 | # Deploying to AWS with Docker 2 | 3 | Now that we have our application built with a frontend and all, it is finally time to deploy our application to a instance in our cloud provider of choice: AWS. 4 | 5 | ## Getting Started 6 | 7 | Setup an AWS account / Amazon account. Using a new credit card and email will give you access to free tier services offered by AWS. This includes the t1.micro instance that we will be using in this lesson. (More on instance types later) 8 | 9 | Once you've created your account, login and let's take a peak around. It is important to note that we are currently logged into AWS with our ROOT CREDENTIALS. Why is this important? Because it is dangerous. Whether dealing with a server or your AWS account, root users should be avoided due to the fact that they have open permissions for all resources and actions. 10 | 11 | #### Some AWS Best Practices 12 | 13 | Let's start by using a service called **IAM**. This service allows us to make users for our account, and associate users to permissions so that they do not operate beyond their purpose. For our purpose we need a user that can spin up a server in **EC2**. 14 | 15 | To create a new profile/user: 16 | 17 | 1. Click **Services** in the top left hand corner of the AWS Management Console to drop down the list of services 18 | 2. Under **Security, Identity & Compliance** click **IAM** 19 | 3. In the left sidebar, click **Users** 20 | 4. Enter a **User Name** and select **Programmatic Access** as the Access Type and click **Next** 21 | 5. Select **Attach Existing Policies Directly** and search for then select **AmazonEC2FullAccess**, then click **Next** 22 | 6. Review and **Create User** 23 | 24 | Now we have a new user specific to our docker deployment. This user cannot mess with other services outside of EC2 and we have therefore decreased our risk and exposure to someone with malicious intent. 25 | 26 | Let's grab the **Access key ID** and **Secret access key**, or simply download the .csv for our own records. 27 | 28 | 29 | 30 | ## Creating our Docker Machine 31 | 32 | Now that we have our credentials ready to go, we can use docker-machine to create a new machine remotely using the AWS driver. Documentation is here: 33 | 34 | https://docs.docker.com/machine/drivers/aws/ 35 | 36 | This driver comes with extra options to specify our credentials and what kind of machine in AWS we want to spin up. We will be using the free tier instance type **t2.micro** for our docker-machine deployment. Also we will choose to deploy in region **us-west-2**, since this is the recommended region by AWS due to it being the newest and least trafficked. 37 | 38 | Spinning up our remote docker-machine requires only one command with the following options. 39 | 40 | ```shell 41 | docker-machine create --driver amazonec2 \ 42 | --amazonec2-access-key AKI******* \ 43 | --amazonec2-secret-key 8T93C******* \ 44 | --amazonec2-region us-west-2 \ 45 | --amazonec2-instance-type t2.micro \ #free tier, also the default 46 | todos-service 47 | ``` 48 | 49 | This may take a few minutes as AWS launches the instance and docker-machine installs docker on the instance. 50 | 51 | If we look in our management console we can follow along the process. If all went well, we now have a running docker-machine in AWS. 52 | 53 | ### Security Groups 54 | 55 | Every EC2 instance that you create will be assigned a security group to manage inbound and outbound traffic. With docker-machine managing the creation, it also created and assigned the docker-machine security group to our instance. 56 | 57 | This instance has two inbound ports open to the world, SSH (22) and Docker Machine TCP Rule (2376). 58 | 59 | #### SSH 60 | 61 | The first allows us to SSH into our instance as with any other machine. We can do this using following command. 62 | 63 | ```shell 64 | docker-machine ssh todos-service 65 | ``` 66 | 67 | Inside we can see the machine as the ubuntu user. We can verify docker is running. First we have to add the ubuntu user to the docker group to give permission to connect to the docker daemon. 68 | 69 | ```shell 70 | sudo usermod -aG docker ubuntu 71 | ``` 72 | 73 | Now we have to exit and reconnect to verify our ubuntu user can see docker processes. Run **exit** and SSH back in. 74 | 75 | ```shell 76 | docker ps 77 | ``` 78 | 79 | #### Docker Machine 80 | 81 | To connect with docker-machine and deploy our containers remotely we will use the following command. 82 | 83 | ```shell 84 | eval $(docker-machine env todos-service) 85 | ``` 86 | 87 | This configures our shell to use the todos-service to listen for docker processes. **Note**: You must always do this when running a new shell/terminal tab. If you don't see your processes, try running this command again to reconfigure. 88 | 89 | #### Creating a Security Group 90 | 91 | We want to keep these two inbound ports, however we may need a few more for this service. We will create a new security group for this todos-service. 92 | 93 | 1. From the **EC2 Dashboard**, click on **Security Groups** in the left sidebar under **Network & Security**. 94 | 2. Click **Create Security Group**. 95 | 3. Give it a **name**, **description**, leave the default **VPC** 96 | 4. Add Rules and Create 97 | 1. **Type:** SSH, **Source:** Anywhere 98 | 2. **Type:** Custom, **Port Range:** 2376, **Source:** Anywhere 99 | 3. **Type:** Custom, **Port Range:** 3000, **Source:** Anywhere 100 | 101 | The third rule will allow us to connect to our application from the outside once deployed. 102 | 103 | Now go back to the list of EC2 instances and select the todos-service, dropdown **Actions > Networking > Change Security Groups**. Select the newly created group and click **Assign Security Groups**. 104 | 105 | As you might notice, you can attach to multiple security groups so that permissions don't have to be repeated. I have created a new security group so that in the future when I need to spin up more of these services, I can do so from the command line by specifying the `--amazonec2-security-group` option. 106 | 107 | 108 | 109 | ## Deploying to Docker Machine in AWS 110 | 111 | With everything set up on the AWS side, it will be relatively simple for us to deploy our Todos API to our docker machine. Just in case you haven't configured your shell for this machine, let's run it again. 112 | 113 | ```shell 114 | eval $(docker-machine env todos-service) 115 | ``` 116 | 117 | From the project root of our todos app, we can run the same command we do locally to bring up our system remotely. 118 | 119 | ```shell 120 | docker-compose up 121 | ``` 122 | 123 | Our API service has port 3000 mapped to the docker-machine's port 3000. Since we opened up that port using our new security group, we should be able to access our api. In the **EC2 Dashboard**, select our instance and copy the **Public IP**. In our browser, we can connect. 124 | 125 | `http://[Public IP]:3000/todos` 126 | 127 | And we are fully deployed. -------------------------------------------------------------------------------- /terminal-fu.md: -------------------------------------------------------------------------------- 1 | # Terminal fu 2 | 3 | Some of my favorite little Unix command line zingers that make me more productive. 4 | 5 | It is great to appreciate the history of Unix, coming from when punching a key was kind of a pain in the ass. They had to really be curt and sort of creative to get their point across to massive mainframe machines somewhere in another building. The more you use your terminal emulator, the more you will appreciate how smart Unix can be. You will find yourself using the command line even for stuff that you can easily accomplish with a GUI. 6 | 7 | This will read with childish simplicity along the lines of "Moss grows so the soil that trees need doesn't erode!" I am just showing you some of _my_ use cases for some complex tools with multiple uses. I might say to you, "There is a swiss army knife in the drawer for opening packages from Amazon and also wine bottles." because that is 'what it is for' to me. Rather than get lost in the million uses of these tools, here is how I use them most frequently. Look at the man pages if you want an exhaustive rundown, also don't @ me. 8 | 9 | ----------- 10 | 11 | `!!` Basically ditto marks. This means "what I just ran last time". What is call is that you can use it in a new context. Classic case: 12 | 13 | ```shell 14 | $ rm -rf /important_stuff 15 | ** Error because of permissions ** 16 | $ sudo !! 17 | ** Works ** 18 | ``` 19 | 20 | --------------- 21 | 22 | Recusively search through history by hitting `Ctrl-r` and typing a substring of what you want to run again. Hit `ctrl-r` again to cycle through matches. 23 | 24 | ------------------ 25 | 26 | For a complete chronological history, just run `history`. Pairs well with `grep` 27 | 28 | ------------------- 29 | 30 | Save terminal output to a file with `>` 31 | 32 | Run something with output, `>`, then the name of the file you want to pour the output into. Example: 33 | 34 | ```shell 35 | $ ruby calcuations.rb > answer.txt 36 | # Classic example: 37 | $ pip freeze > requirements.txt 38 | ``` 39 | 40 | ------------ 41 | 42 | Grep lets you search through files. It's insanely useful. No joke, I used to literally open huge directories in a text editor to search through it for a substring. No more after I discovered grep. 43 | 44 | For example, this searches for the substring 'affilliation' anywhere inside the app/models directory 45 | 46 | ```shell 47 | $ grep -nri affiliation app/models 48 | ``` 49 | 50 | The n flag asks for line numbers. 51 | 52 | The r flag stands for recursive, meaning that it will continue searching in subdirectories. 53 | 54 | The i flag specifies case insensitive matches. 55 | 56 | -------------- 57 | 58 | The pipe (`|`) character lets you sort of string commands together, not just chronologically (that would be `&&`). Pipe means after you do x, then do y to the result. Here is an example: 59 | 60 | ```shell 61 | $ history 62 | # holy crap there is so much, I would like to pair down this output 63 | $ history | grep python 64 | # get my whole history, then go ahead and do the grep command on what results and search for "python" 65 | ``` 66 | 67 | -------------- 68 | 69 | Say goodbye to restarting your computer to kill a server that refused to die correctly. This happens all the time developing Rails projects locally but on a server, not a good option at all! `lsof` lets you see the processes that are running on an individual port. 70 | 71 | `lsof -i :3000` Reveals what is occupying port 3000 for instance. If you have that pid (process id) you can kill it if necessary. 72 | 73 | ------------ 74 | 75 | Kill? Yes KILL KILL KILL. 76 | 77 | `kill -9 ` 78 | 79 | So if the rouge process on port 3000 was 1234, do `kill -9 1234` to mercilessly end it. The 9 flag is one of a few options, that one just plain lays down the law rather than interrupt or try to quit. I have only ever used 9. 80 | 81 | ---------------- 82 | 83 | `netstat ` is another interesting one since it shows all of your active network connections. More useful when you are logged into a dev or production server. 84 | 85 | ------------- 86 | 87 | Need a server to just get some html or whatever into the browser? Run `python -m SimpleHTTPServer` inside a directory and boom. localhost:8000 is serving that directory the old school way. This is sometimes necessary if you are dealing with apis and whatnot that need to be run from a server for whatever reason rather than just opening a file in the browser. 88 | 89 | -------- 90 | 91 | All about trees man. This one is pretty awesome, but you have to brew install it on mac. Then go into a project directory and run `tree`. 92 | 93 | There it is. A tree. 94 | 95 | Node modules tho. Node modules buggin me out. Ignore directories with the `-I` flag. 96 | 97 | ------------- 98 | 99 | I hit `Cmd+k` on my Mac like all the time if you haven't noticed. I like to clear my head. I am a simple person. On linux you need to either mess around with the terminal settings to get that or just type `clear` and there you go. 100 | 101 | ----------- 102 | 103 | `cal` shows you a useless calendar. Cool. 104 | 105 | --------------- 106 | 107 | `dig` is a powerful dns tool that returns DNS records. Quite useful when you want to confirm that DNS changes (like setting up SSL or mail records) are propagated successfully 108 | 109 | -------- 110 | 111 | Aliases are crucial. I have a bunch! You can declare these like so: 112 | 113 | `alias l = 'ls -l'` 114 | 115 | Now if you typle `l` it fills out `ls -l` for you. 116 | 117 | I have a ton of these that I use with varying frequency, but there are so many that I can't live without. This is why I encourage all of you to put your dotfiles in a repo. Maybe we will do a lesson on that soon. 118 | 119 | ---------- 120 | 121 | `df` Shows free disk space. More useful on servers, obviously. 122 | 123 | -------- 124 | 125 | `uptime` shows just how long a machine has been on. Sometimes good to know. 126 | 127 | --------- 128 | 129 | Man oh man there is no way to remember all these commands and all these flags for them. This is where man pages come in. No, these aren't pages full of football stuff and buffalo wings, it is short for manual. For instance run `man grep`. All of these unix programs of yore have extensive, useful man pages. 130 | 131 | This drops you into "less" which is sort of like a text displaying program. Unlike vim and whatnot, this is not for text editing. You scroll with the `enter` key, quit with the `q` key. If you want to search, say, search for what the `-R` flag does, you type a `/` and the string that you want to search for, then enter. To show the next result, you hit `n`. 132 | 133 | 134 | 135 | 136 | 137 | # ZSH 138 | 139 | - one year younger than bash 140 | - Bash version on mac is old af 141 | - You can just jump in and use zsh without further instruction. You don’t have to learn it to use it. 142 | - Oh my zsh 143 | - Cd tab completion 144 | - Git tab completion 145 | - Up arrow completion 146 | - Path expansion: Cd x y replaces matching x part of path in working directory -------------------------------------------------------------------------------- /databases/README.md: -------------------------------------------------------------------------------- 1 | # Database Architectures 2 | 3 | There are four properties that define databases based on the main interface (transactions). Relational databases abide by all four of these properties, known as **ACID**. 4 | 5 | #### Atomicity 6 | 7 | All or nothing transactions. Transactions are seen as one operations and will fail to write or succeed. There is no partial success of a transaction. 8 | 9 | #### Consistency 10 | 11 | A transaction will bring the former valid state to a new valid state based on any constraints or rules. Referential integrity is a key part of this consistency as it requires primary and foreign keys to be valid when doing insert or update operations. 12 | 13 | #### Isolation 14 | 15 | Transactions are isolated until the database enters another valid state. One transaction is entirely unaware of any incomplete transactions currently executing. 16 | 17 | #### Durability 18 | 19 | If we lose power, we do not lose any committed transactions. Failover is to write to non-volatile memory in case of crash. 20 | 21 | 22 | 23 | ##Relational Databases 24 | 25 | We all use them. Relational databases normalize data to keep data consistent. The architecture usually has a master node that takes care of all update/write transactions and replicas for reading that data. Relational databases are a great way to organize your information but it requires a rigid structure which can be costly when data grows to large amounts. Joins require a lot of processing and tables are not always optimized for specific queries. Unless your data is massive or requires very quick delivery, these databases are the best to work with. 26 | 27 | ## Non-Relational Databases (NoSQL) 28 | 29 | There are many NoSQL database options available to us. Each one has pro and con. 30 | 31 | | NRDB | MongoDB | Cassandra | Redis | Hadoop | 32 | | ------------------------ | ---------------------------------- | ---------------------------------------- | ---------------------------------------- | ---------------------------------------- | 33 | | **Storage Type** | Documents | Wide Columns | Hashes | Distributed File System | 34 | | **Ease of Use** | Very Easy | Moderate | Difficult | Difficult | 35 | | **Architecture** | Master/Slave | Distributed Clusters | Master/Slave | Distributed Clusters | 36 | | **Speed** | Fast reads, slower writes at scale | Constant write time, still fast at scale | The fastest, reads and writes at the same rate | Very slow unless using huge datasets | 37 | | **What is it good for?** | Great for beginners trying NoSQL | Performs very well at scale for reading AND writing data | In memory database can perform complex queries very quickly | Storing huge sets of data for batch analysis | 38 | 39 | 40 | 41 | ###Intro to Cassandra 42 | 43 | Cassandra is a distributed database. Unlike Relational Databases where the architecture has a centralized Master node, and slave or replica nodes, cassandra focuses on distributing data to nodes. When a transaction is written, the data is written to a node. While the same row may exist on another node, which has not been updated, cassandra uses a compaction process to compile all versions of the row when read. At this stage, the rows are compared by timestamp to return the latest version, and the outdated versions are updated accordingly. 44 | 45 | Cassandra database models are heavily geared toward a one query per table approach. This means that each table is optimized for specific queries to increase performance. This is mainly done through partition keys and clustering keys. 46 | 47 | ##### Partition keys 48 | 49 | Either a part of a composite key, which is made up of multiple columns, or a single primary key on a single field key table, usually a by_id table. Partition keys are responsible for distributing data across nodes. This is where the index optimization comes from. 50 | 51 | ##### Clustering keys 52 | 53 | These keys are responsible for sorting data per partition. This results in the ability to sort by other columns while maintaining a primary or composite key that stores the data on certain nodes. 54 | 55 | 56 | 57 | **Example:** 58 | 59 | First we have to create a keyspace 60 | 61 | ```CQL 62 | CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'} 63 | ``` 64 | 65 | ```CQL 66 | USE test; 67 | ``` 68 | 69 | Let's say we are creating an app that attempts to identify all stray cats in new york city, through crowd sourced information. We want to collect which block a cat has been spotted on, as well as some additional information about the cat. Each cat spotting can be seen in our app, block by block, and breed by breed. This gives us an idea about the composite keys we may use. To further help the identification process, we want to sort by color. 70 | 71 | ```CQL 72 | CREATE TABLE cats ( 73 | block_id uuid, 74 | breed text, 75 | color text, 76 | short_hair boolean, 77 | PRIMARY KEY ((block_id, breed), color) 78 | ); 79 | ``` 80 | 81 | Now let's insert some sample user data: 82 | 83 | ```CQL 84 | INSERT INTO cats (block_id, breed, color, short_hair) 85 | VALUES (62c36092-82a1-3a00-93d1-46196ee77204, 'siberian', 'grey', false); 86 | ``` 87 | 88 | When we try to query this particular table by only the breed, something happens: 89 | 90 | ```CQL 91 | SELECT * FROM cats WHERE breed = 'siberian' 92 | ``` 93 | 94 | ```red 95 | InvalidRequest: Error from server: code=2200 [Invalid query] message="Cannot execute this query as it might involve data filtering and thus may have unpredictable performance. If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING" 96 | ``` 97 | 98 | This error message shows us why our composite key is important in the WHERE clause of our query. Cassandra has optimized our table to store data per node by breed AND block_id. Since our app service allows us to see cat spottings by blocks, this is a perfect query table, but we must include a block_id in our WHERE clause. Find all the siberian alley cat spottings on my block! 99 | 100 | ```CQL 101 | SELECT * FROM cats 102 | WHERE breed = 'siberian' AND block_id = 62c36092-82a1-3a00-93d1-46196ee77204; 103 | ``` 104 | 105 | Now we get our result. 106 | 107 | 108 | 109 | ##### How to handle data consistency 110 | 111 | One thing that cassandra is missing is consistency through key integrity, making sure all states of the database are valid. This requires much more attention to detail on the software side, to ensure that data across tables is consistent. 112 | 113 | Say we have a song app and a playlists table. 114 | 115 | ```CQL 116 | CREATE TABLE playlists ( 117 | id uuid, 118 | song_order int, 119 | song_id uuid, 120 | title text, 121 | album text, 122 | artist text, 123 | PRIMARY KEY (id, song_order) 124 | ); 125 | ``` 126 | 127 | This table allows us to easily display a playlist ordered by song. Say we would also like to create a table to search for every playlist by title. We need another table of course. 128 | 129 | ```CQL 130 | CREATE TABLE playlists_by_title ( 131 | id uuid, 132 | song_order int, 133 | song_id uuid, 134 | title text, 135 | album text, 136 | artist text, 137 | PRIMARY KEY (title) 138 | ); 139 | ``` 140 | 141 | Now that we have two tables that share many columns, we need to ensure that we update both when making any change to a playlist. 142 | 143 | To do this a lot of our updates will need to contain multiple update statements. 144 | 145 | ```CQL 146 | BEGIN BATCH 147 | INSERT INTO playlists (id, song_order, song_id, title, artist, album) 148 | VALUES (62c36092-82a1-3a00-93d1-46196ee77204, 4, 7db1a490-5878-11e2-bcfd-0800200c9a66, 'Ojo Rojo', 'Fu Manchu', 'No One Rides for Free'); 149 | INSERT INTO playlists_by_title (id, song_order, song_id, title, artist, album) 150 | VALUES (62c36092-82a1-3a00-93d1-46196ee77204, 4, 7db1a490-5878-11e2-bcfd-0800200c9a66, 'Ojo Rojo', 'Fu Manchu', 'No One Rides for Free'); 151 | APPLY BATCH 152 | ``` 153 | 154 | Now we have updated both tables ready to be queried by all services. -------------------------------------------------------------------------------- /typescript/typescript.md: -------------------------------------------------------------------------------- 1 | # Introducing TypeScript 2 | 3 | ## What is TypeScript? 4 | 5 | TypeScript is an open-source language created by Microsoft just 5 years ago. It comes with a _compiler_ which crunches your TypeScript source code down to ES5 code for you. 6 | 7 | Javascript IS Typescript, but not vice versa. This is the best part about TypeScript. You don't need to know or use all of the features of TypeScript to get started! 8 | 9 | ### Why static typing? 10 | 11 | Static (AKA "strong") typing is a familiar feature to those of you who have messed around with langauges like Java and Go. In a statically typed language, declaring a variable involves declaring what type of data the variable will hold. If you are about to reserve some space in a computer's memory, you have to have some preordained notion of what it will hold, and you have to stick to it. 12 | 13 | Most languages that you are all used to, like JavaScript, Ruby, and Python are dynamically typed. You can do something like this: 14 | 15 | ```javascript 16 | var name = "Vincent"; 17 | name = 99; 18 | 19 | // or this even 20 | 21 | var age; // age is just ¯\_(ツ)_/¯ 22 | age = 33; // ok cool its a number now 23 | age = {years: "33", months: 398} // hold my beer 24 | ``` 25 | 26 | Most modern languages went this route because it is easier and more flexible than the rigid old ways of Java and such. TypeScript comes along and basically says… 27 | 28 | ![y tho](https://pbs.twimg.com/media/DKBXnDDWAAA67rB.jpg:small) 29 | 30 | Seriously, when would you ever need that? 31 | 32 | At what point would it make sense for `age` to be a number and then later, an object? Other code acting on the number, like addition etc will obviously break like this. This is a bug waiting to happen. Collections in JavaScript et al are also capable of including multiple types. But really, when would you need a collection of things that are fundamentally unalike? Practically 90% of programming is about doing stuff to bunches of data. Why would you want a spanner in the works like that? 33 | 34 | Talking to other developers that convinced me to try TypeScript after many long years of dynamically typed languages, most cited the following reason: it's way easier to catch errors at compile time than at runtime. 35 | 36 | I balked at TypeScript for a while because I was so frustrated with having to compare some code that actaully runs in the browser and the totally different CoffeeScript source code. Finding bugs was like three dimensional chess. I'd write some code, refresh, and see absolutely nothing happen since the compiler barfed, and any information it gave me was not helpful. Just had to figure it out without a clue like line number or whatever. 37 | 38 | Typescript is far and away better. In [this year's Stack Overflow survey](https://insights.stackoverflow.com/survey/2017) developers voted TypeScript as the third most loved and CoffeeScript as the third most dreaded. Rather than making JavaScript look like Python, TypeScript makes JavaScript developers catch bugs as they hit the keys, and it results in more robust, less buggy code. 39 | 40 | Typing is also _optional_. With CoffeeScript, I would have to look up how to do somethign in the docs, paste it into my code, manually translate into actual JavaScript, and hope to god that it transpiles into the same JavaScript that I just copied. If you don't feel like doing things the TypeScript way, you don't have to, as we will see. 41 | 42 | Another cool "feature" if you will is the wicked awesome productivity tools that you get when you embrace static typing. For instance if you have a Person object called `alice`, rather than autocompleting whatever starts with 'a' elsewhere in the file if you start typing `alice.a` it will autocomplete with more sensible things like, oh, I don't know, properties of a Person type that start with 'a'. It just ends up making so much more sense. Visual Studio Code, my favorite editor for nearly a year now, has incredle tooling for TypeScript. It is in fact written in TypeScript! 43 | 44 | Another good reason to try TypeScript is that there are some big heavy hitting companies behind it. Microsoft, is of course all about this nice little language that it made. Google is making a huge push to make TypeScript a first-class citizen in all of its web apps. Google worked with Microsoft to make TypeScript _the_ way to write JavaScript in Angular 2+ applications. Slack also has a [great article](https://slack.engineering/typescript-at-slack-a81307fa288d) about their move to TypeScript for its desktop client that I recommend reading. 45 | 46 | ## Enough, let's get to the code already 47 | 48 | First, we need to install TypeScript. Open your terminal and just key in 49 | 50 | ```shell 51 | npm install -g typescript 52 | # latest version of typescript installs.... 53 | tsc --version 54 | # should tell you the version that you have globally 55 | ``` 56 | 57 | `tsc` is the TypeScript Compiler. That's the program that crunches your source code into JavaScript that uncultured browsers can understand. 58 | 59 | ## Your first super basic TypeScript program 60 | 61 | Let's make a directory to mess around in. 62 | 63 | ```shell 64 | mkdir hello-typescript 65 | cd hello-typescript 66 | touch main.ts 67 | # open in the one true text editor 68 | code . 69 | ``` 70 | 71 | Now let's just tap in some basic meat and potatoes JavaScript like our ancestors used to write, but let's make the file extension `.ts` instead of `.js`. 72 | 73 | ```typescript 74 | function sayHi(name){ 75 | console.log("hello" + name); 76 | } 77 | var myName = "Vincent"; 78 | 79 | sayHi(myName); 80 | ``` 81 | 82 | Nothing to see here, move along people. 83 | 84 | Let's compile the code. In your terminal: 85 | 86 | ```shell 87 | tsc main.ts 88 | ``` 89 | 90 | Now let's see what we have here…. hark! A JavaScript file with the same stuff in it! Success! 91 | 92 | Okay I know that isn't impressive but we are just getting started. 93 | 94 | To recap, the TypeScript Compiler is the intended audience for the `.ts` file, just as the runtime environment (either node or a browser basically) is the audience for JavaScript. 95 | 96 | ## Strong typing in action 97 | 98 | I am going to start moving the examples into ES6+ since TypeScript incorporates some of the same stuff into it's compiler, kind of like Babel. By default, TypeScript comiles to ES5 so you can be sure that any browser basically can deal with your resulting JavaScript. 99 | 100 | Let's try messing around with types. 101 | 102 | ```typescript 103 | let v; 104 | // hover over a to see the type 105 | let str = "thermidor"; 106 | 107 | ``` 108 | 109 | Now hover over the declaration `str` and witness. 110 | 111 | TypeScript and VS code infers that the variable str has the type of string. Note the comma. 112 | 113 | Now throw in: 114 | 115 | ```typescript 116 | str = 999; 117 | ``` 118 | 119 | Behold the squiggly line of _catching errors without having to actually run your code_. 120 | 121 | Even if you were using nano or some other text editor that doesn't help you much, the TypeScript compiler will flag errors like this at compile time. With our squiggly line still there, go ahead and run: 122 | 123 | ```shell 124 | tsc main.ts 125 | ``` 126 | 127 | The compiler will put out the same error we saw in the the text editor. Very helpful, but it also faithfully compiles a `main.js` file anyway! If you look at the JavaScript code that results, you will see some old school ES5 code. The benefit is that you were at least warned that something was screwy. As a consenting adult, you can carry on with your bug-friendly code and deal with the consequences later. 128 | 129 | If you do the same skullduggery with the variable `v`, which you might remember, TypeScript regards as a free for all type of `any`, the tsc will not complain. 130 | 131 | ### Let's be more civilized and declare types ahead of time 132 | 133 | Let's edit that file so it actually uses TypeScript's features and declare some variables with types. 134 | 135 | ```typescript 136 | let v: string; 137 | 138 | let str: string = "thermidor"; 139 | 140 | str = 55; 141 | ``` 142 | 143 | The colon in TypeScipt can be used to declare the type that will be assigned to a variable whether it is initialized with data or not. If you try to compile this, you will be successful again, but as a conscientious programmer, why would you just carry on with errors in your code comilation like that? 144 | 145 | You can also specify the type of numbers in an array like so: 146 | 147 | ```typescript 148 | let ids: number[] = [1,2,3]; 149 | ``` 150 | 151 | You can always emphatically set something to `any` type as well. 152 | 153 | #### Enums 154 | 155 | TypeScript brings finite sets or "enums" to JavaScript as well. For example: 156 | 157 | ```typescript 158 | enum LanguageCodes {English = "EN", "Spanish" = "ES", German = "DE"}; 159 | 160 | let userLanguage = LanguageCodes.E //watch as intellisense gives me a dropdown since Enums signify that it has to be one of these. 161 | ``` 162 | 163 | Using enums lets you rely less on primitive types to get things done! 164 | 165 | 166 | 167 | ## Type assertions 168 | 169 | The compiler and intellisense is basically the same thing. 170 | 171 | Look at how cool this is in VS Code: 172 | 173 | ```typescript 174 | let name = "Vincent"; // string duh 175 | 176 | let hasLongName = name. // nice! it's autocompleting with properties of type string. 177 | 178 | ``` 179 | 180 | Let's say you need to declare a type later on after initializing it with the "any type". 181 | 182 | ```typescript 183 | let age; //implicitly 'any' 184 | age = 33; 185 | age. // nothing pops up this way 186 | 187 | (age). //dropdown city baby. 188 | 189 | //or 190 | (age as number).toString(); //for example 191 | ``` 192 | 193 | In your application you will come across needs that basic typescript hasn't provided for, of course. Like "User", "Post", "Todo", "Friendship" etc. You can declare types for these, sort of like how you create classes in real object oriented programmign languages. 194 | 195 | 196 | 197 | ## Custom Interfaces 198 | 199 | When a thing is like a coherent discreet thing, we naturally tend to group the properties thereof as a JavaScript object. This is especially handy when writing a function with parameters: 200 | 201 | ```javascript 202 | // crappy way 203 | let name = "vincent"; 204 | let age = 33; 205 | let location = "Brooklyn"; 206 | 207 | let introduce = (name, age, location) =>{ 208 | console.log(`Hi, I am ${name} from ${location}, I am ${age} years old.`); 209 | } 210 | 211 | introduce(name, age, location); 212 | // better way 213 | 214 | let me = { 215 | name:"vincent", 216 | age: 33, 217 | location: "Brooklyn" 218 | } 219 | 220 | let introduce = (person) =>{ 221 | console.log(`Hi, I am ${person.name} from ${person.location}, I am ${person.age} years old.`); 222 | } 223 | 224 | introduce(me); 225 | 226 | ``` 227 | 228 | 229 | 230 | Interfaces are much better because unlike a free flowing object, you can define the shape of it and the types therein. 231 | 232 | Try this: 233 | 234 | ```typescript 235 | interface Person { 236 | name: string, 237 | age: number, 238 | location: string 239 | } 240 | 241 | let introduce = (person: Person) =>{ 242 | console.log(`Hi, I am ${person.name} from ${person.location}, I am ${person.age} years old.`); 243 | ``` 244 | 245 | Sometimes you will want to reach for a class, sometimes for an interface. That example above shows some cohesion between the interface and the function. That function could be a method on a person class instead. Here is the same thing as a class: 246 | 247 | ```typescript 248 | class Person{ 249 | constructor(name: string, age: number, location: string){ 250 | this.name = name 251 | this.age = age 252 | this.location = location 253 | } 254 | introduce () =>{ 255 | console.log(`Hi, I am ${this.name} from ${this.location}, I am ${this.age} years old.`); 256 | } 257 | 258 | me = new Person('vincent', 33, 'brooklyn'); 259 | 260 | me. // look at the intellisense! 261 | 262 | 263 | ``` 264 | 265 | 266 | 267 | If you want to make constructor parameters optional, you can add a `?` right after the variable. You can also make some fields unsettable after like so 268 | 269 | ```typescript 270 | class Person{ 271 | constructor(name?: string, age?: number, location?: string){ 272 | this.name = name 273 | this.age = age 274 | this.location = location 275 | } 276 | introduce () =>{ 277 | console.log(`Hi, I am ${this.name} from ${this.location}, I am ${this.age} years old.`); 278 | } 279 | 280 | 281 | me = new Person('vincent', 33, 'brooklyn'); 282 | 283 | 284 | 285 | ``` 286 | 287 | All attributes of the class are public (gettable and settable outside of the class itself) unless you prefix the constructor param with `private`. 288 | 289 | -------------------------------------------------------------------------------- /testing/API_and_unit_tests.md: -------------------------------------------------------------------------------- 1 | # Sorting Algorithms with Test Driven Development 2 | 3 | Today we are going to try some test-driven development (TDD) with Jest and plain old vanilla JavaScript. 4 | 5 | Please take this lesson as an _experience_ with a process rather than a proscribed best practice. TDD is a somewhat controversial topic. Some developers feel like its the only way to code, others see it as too time consuming or counter intuitive. Whether you adopt this habit really comes down whether it works for you or your team and the processes that your organization cares about. 6 | 7 | Another disclaimer is that [TDD is not testing](http://david.heinemeierhansson.com/2014/tdd-is-dead-long-live-testing.html). You are testing your code always, whether you adopt TDD or not. We test because if we didn't we would go completely insane. I personally prefer to write my tests after the thing is working and often it helps me see edge cases that I didn't account for or potential bugs. I can update unrelated code or dependencies tomorrow with the confidence that the code that I wrote today won't break, or at least if there was a breaking change we would know it before we deploy. Manual QA of your entire application gets really boring really quickly. Running your tests, not so much. 8 | 9 | ## The TDD flow 10 | 11 | We are going to perform a unit test for a sorting algorithm, then incorporate that into an Express API and do an integration test on that endpoint. A unit test tests individual functions in isolation. These are generally seen as inferior to the much more expensive integration tests or UI tests, which make sure that everything that you would expect a user to be able to do can still be done and result in the correct outcome. 12 | 13 | In the future I would like to introduce [Puppeteer](https://github.com/GoogleChrome/puppeteer), which is a Node package for controlling a headless Chrome browser for UI tests, but today we will keep it stupidly simple. 14 | 15 | In TDD, your tests drive your code design, not the other way around. Rather than writing working software and retroactively writing tests to uncover bugs (and prevent future bugs from slipping by unnoticed), you write the tests first. 16 | 17 | This is helpful for many because the time that you invested in describing your software leaves you with a helpful todo list for what to look out for when you actually code. 18 | 19 | You look at the requirements for the feature that you are working on and translate that into code, run the tests to just confirm that your testing code is free of syntax errors. They should all be failing in such a way that the output clearly tells you what to do next. We call this the **red** stage of TDD. 20 | 21 | Next, you write _only_ the necessary code to get that test to pass. This incremental approach makes sure that every branch of your code is tested and covers edge cases. It also gives your teammates a legible list of things that the program is supposed to do. Sometimes we call tests 'specs' short for specifications. Your tests are living breathing documentation as well as a guard against bugs. We call this the **green** stage. 22 | 23 | Now that you have solid tests in place, you can freely **refactor** your code with abandon, knowing that any regressions that you unknowingly introduce will be caught. You can improve your code without fear. 24 | 25 | ## Our assignment 26 | 27 | You have a client that needs some arrays of numbers sorted. This kind of thing seems to happen a lot in the world of business. 28 | 29 | We are going to declare some tests to define done. and make sure that our sorting algorithm is producing the correct output. 30 | 31 | Let's write the tests first and set up a skeleton for testing. 32 | 33 | ```shell 34 | % mkdir sorter && cd sorter 35 | % npm init # (press enter a bunch of times) 36 | % git init 37 | % npm i --save express body-parser 38 | % npm i --save-dev jest supertest 39 | % touch sorter.js test.js .gitignore 40 | ``` 41 | 42 | Jest looks for files named with the pattern `*test.js` by default. 43 | 44 | Let's just set up the skeleton with our sorting function in index.js and the test block in test.js: 45 | 46 | ### sorter.js 47 | 48 | ```javascript 49 | const sorter = ((arr) => { 50 | return arr; 51 | }); 52 | 53 | module.exports = sorter; 54 | ``` 55 | 56 | ### sorter.test.js 57 | 58 | 59 | 60 | Just to make sure that we didn't insert any errors, let's insert a smoke test as a sanity check: 61 | 62 | ```javascript 63 | describe('sanity check', () => { 64 | it('can do obvious stuff', () => { 65 | expect(2 + 2).toBe(4); 66 | }); 67 | }); 68 | ``` 69 | 70 | 71 | 72 | This pattern will become very familiar. `describe` is part of the [Jest Domain Specific Language (DSL)](https://facebook.github.io/jest/docs/en/using-matchers.html). It expects a string as a description, then a function, which is anonymous in this case. Test does the same thing. `expect` takes in any statement, in this case `2 + 2`. `toBe` is also part of the DSL. Pretty obvious what it's doing here. 73 | 74 | Just a little more configging in `package.json` 75 | 76 | ```json 77 | "scripts": { 78 | "test": "jest --watchAll" 79 | }, 80 | "jest": { 81 | "verbose": true 82 | }, 83 | ``` 84 | 85 | This lets us test our code continuously, kind of like hot reloading. Jest keeps an eye on the working directory and re-runs tests every time I save, which is handy for TDD since I can see red or green continuously. The verbose config also makes things more blindingly obvious, which I really like. I'm going to keep that window visible on the left with my code editor on the right. 86 | 87 | In my terminal I am going to run: 88 | 89 | ```shell 90 | % npm test 91 | ``` 92 | 93 | And it looks like everything is in order, we are in a good state. 94 | 95 | Right now we will introduce the test case. The definition of 'working'. 96 | 97 | I will just declare a constant at the top of the file for our unsorted array, and then a constant for the expected order for it when it is sorted. 98 | 99 | This is where you would want to maybe start thinking about edge cases. Assuming a uniform data type, we can throw some curve balls in there such as negative numbers, decimals, expressions, the Number keyword, fractions, etc. 100 | 101 | Here is what I came up with: 102 | 103 | ```javascript 104 | const unsorted = [100.5, -40.7, 40000, -160, 30, 7, (3 / 5), Number((6 / 7).toPrecision(2)), -0, 6e2]; 105 | ``` 106 | 107 | Feel free to write your own balderdash numbers. Now I can manually determine the correct sorted order: 108 | 109 | ```javascript 110 | const sorted = [-160, -40.7, 0, 0.6, 0.86, 7, 30, 100.5, 600, 40000,]; 111 | ``` 112 | 113 | I will now complete the test block like so: 114 | 115 | ```javascript 116 | describe('Bubble sort', () => { 117 | it('sorts an array', () => { 118 | expect(sorter(unsorted)).toEqual(sorted); 119 | }); 120 | }); 121 | ``` 122 | 123 | Once you save, you will see Jest complaining in the terminal window. 124 | 125 | You can also get rid of the sanity check block. 126 | 127 | We are red! Time to go green! 128 | 129 | ------------------ 130 | 131 | ## Implementing bubble sort 132 | 133 | First we are going to use the bubble sort approach to sort arrays. This algorithm is the easiest to implement. Bubble sort is kind of ham fisted but has a O(n^2). If we are dealing with a large data set, this might be a bad idea, but let's give it a go anyway. 134 | 135 | With bubble sort, you basically go through each item in the array and see if it is larger or smaller than its neighbor to the right, if it is larger, and so on through the array. It's pretty crude but it works. 136 | 137 | Go ahead and take a stab on your own! 138 | 139 | ---------------- 140 | 141 | Solution: 142 | 143 | ```javascript 144 | for (let i = 0; i < arr.length; i++) { 145 | for (let j = 0; j < (arr.length - i - 1); j++) { 146 | if (arr[j] > arr[j + 1]) { 147 | //switch spaces if the earlier one is bigger than the later one 148 | console.log(`switching ${arr[j]} for ${arr[j + 1]}`) 149 | const smaller = arr[j + 1]; 150 | arr[j + 1] = arr[j]; 151 | arr[j] = smaller; 152 | } 153 | } 154 | } 155 | ``` 156 | 157 | As it comes together, you should see your test pass to green. 158 | 159 | ### Refactoring what we got 160 | 161 | Let's continue test driving and make things more interesting. 162 | 163 | Say we want to use the bubble sort algorithm to conditionally return the array in ascending or descending order. 164 | 165 | Test first! Take a moment to to write another it block to try it yourself. 166 | 167 | ```javascript 168 | it('sorts an array in descending order', () => { 169 | let descending = sorted.reverse(); 170 | expect(sorter(unsorted, 'DESC')).toEqual(descending); 171 | }); 172 | ``` 173 | 174 | Aaaand we are red. Get it green (no using `Array.prototype.reverse()`)! 175 | 176 | My solution: 177 | 178 | ```javascript 179 | const sorter = ((arr, direction = 'ASC') => { 180 | for (let i = 0; i < arr.length; i++) { 181 | for (let j = 0; j < (arr.length - i - 1); j++) { 182 | if (arr[j] > arr[j + 1]) { 183 | //switch spaces if the earlier one is bigger than the later one 184 | const lesser = arr[j + 1]; 185 | arr[j + 1] = arr[j]; 186 | arr[j] = lesser; 187 | } 188 | } 189 | } 190 | if (direction === 'DESC') { 191 | let reversed = []; 192 | for (let k = arr.length; k > 0; k--) { 193 | var end = arr.pop(); 194 | reversed.push(end); 195 | } 196 | arr = reversed; 197 | } 198 | return arr; 199 | }); 200 | ``` 201 | 202 | 203 | 204 | # "Super" basic API integration tests 205 | 206 | Let's move on to testing an actual interface for a back end web server. We will use the sorter function, which is already tested in isolation with a unit test. We will complete an Express API with at least one POST endpoint which accepts some numbers in an array and responds with the sorted array. 207 | 208 | Before we do that actual feature, let's do a smoke test by setting up a basic express server and a test. 209 | 210 | Note that we installed `supertest` earlier which provides a succinct interface for testing HTTP. We will be using some of the SuperTest `request` DSL in conjunction with the Jest test runner. 211 | 212 | First add the blank files: 213 | 214 | ```shell 215 | % touch server.js app.js app.test.js 216 | ``` 217 | 218 | Remember we are practicing TDD today, so let's write our tests first and let them fail: 219 | 220 | app.test.js 221 | 222 | ```javascript 223 | const app = require('./app'); 224 | 225 | describe('Test the root path', () => { 226 | it('should respond to the GET method at root with important array', (done) => { 227 | request(app).get('/').then((response) => { 228 | expect(response.statusCode).toBe(200); 229 | expect(response.body.todos).toEqual(['shower', 'feed cat']); 230 | done(); 231 | }); 232 | }); 233 | }); 234 | ``` 235 | 236 | Two things to call out here. An API request in real life takes non zero time, thus we need to test the API methods asynchronously. Jest provides a `done` function to [help out with this](https://facebook.github.io/jest/docs/en/asynchronous.html). Jest will wait for the done function to run before finishing the test. If the test fails, done will never be called. Makes sense. 237 | 238 | Your test should be red. Let's get it green! 239 | 240 | app.js 241 | 242 | ```javascript 243 | const express = require('express'); 244 | const app = express(); 245 | 246 | app.get('/', (req, res) => { 247 | res.status(200).send({ todos: ["shower", "feed kitten"] }); 248 | }); 249 | ``` 250 | 251 | There! We are red again because I intentionally added a boo boo. Fix it and get us green for real. 252 | 253 | ## Sorting as a service 254 | 255 | Looks like we are doing great, we have a web server and it is tested, now it is just a matter of extending this to use our bubble sort util to do its magic on some JSON in a request body. (We have already installed the `body-parser` middleware which makes this possible.) 256 | 257 | Let's start with our test since we are in TDD land today. 258 | 259 | ```javascript 260 | const request = require('supertest'); 261 | 262 | .... 263 | 264 | describe('Test the sort path', () => { 265 | it('should response the post method', (done) => { 266 | request(app).post('/sort') 267 | .send({ "numbers": [2, 3, 1] }) 268 | .then((response) => { 269 | expect(response.statusCode).toBe(201); 270 | expect(response.body.sorted).toEqual([1, 2, 3]); 271 | done(); 272 | }); 273 | }); 274 | }); 275 | ``` 276 | 277 | This should fail big time. Let's build out the actual method now to get it green. 278 | 279 | Here is my finished product: 280 | 281 | ```javascript 282 | const express = require('express'); 283 | const app = express(); 284 | const bodyParser = require('body-parser'); 285 | const { sorter } = require('./sorter'); 286 | 287 | app.use(bodyParser.urlencoded({ extended: false })); 288 | app.use(bodyParser.json()) 289 | 290 | 291 | app.get('/', (req, res) => { 292 | res.status(200).send({ todos: ["shower", "feed cat"] }); 293 | }); 294 | 295 | app.post('/sort', (req, res) => { 296 | const unsorted = req.body['numbers']; 297 | const responseJSON = { 298 | unsorted, 299 | sorted: sorter(unsorted) 300 | }; 301 | res.status(201).send(responseJSON); 302 | }); 303 | 304 | module.exports = app; 305 | ``` 306 | 307 | At this point, we have passing tests, we are confident that it works, and yet we _never once started the node server_. Manual tests are for chumps. True, we spent way more time writing tests than actual server code but it saved us a lot of trouble! 308 | 309 | ## Organizing your app slightly better 310 | 311 | This is a mess. 312 | 313 | ```shell 314 | % mkdir utils test 315 | ``` 316 | 317 | Move the test files into `test` and the sorter module into `utils`. 318 | 319 | Muuuch better. 320 | 321 | My finished version of the app is [here](https://github.com/trivett/tdd_sorting_lesson). 322 | -------------------------------------------------------------------------------- /testing/rails_api.md: -------------------------------------------------------------------------------- 1 | # Testing APIs: Rails edition. 2 | 3 | In the last few classes, we explored testing Node/Express APIs with Jest, the favored framework for unit and end to end testing from that company that makes React. 4 | 5 | Just a show of hands, how many of you have the words "ruby" or "rails" on your resumes? And how many of you are comfortable working with a Rails stack? I bring this up not to shame you into taking stuff off your list of skills (though you should be prepared to talk in a non-BS way about _anything_ that you claim on your CV - I've done this to interviewees and it's surprising how often people just try to wing it) but because it is important to have _diverse_ skills as a developer to broaden the range of jobs you are eligible for and simply make yourself a better programmer. 6 | 7 | Today we are going to work on creating an API with multiple endpoints and test the whole way through. Phillip and I can tell you, a developer's life is like 60% testing. It behoves you to get used to that. 8 | 9 | ## What we will use 10 | 11 | ### Rails API 12 | 13 | Ruby on rails is the 'omakase' platter of web frameworks. It used to be more opinionated, driving Rails developers towards the 'Rails way' but it is more flexible now. For instance, you can pass a single argument and come out with a no-nonsense API application with no HTML or JavaScript files generated. This is an example of rails getting more modern in its approach, but still maintains the ease of use that makes rails such a popular stack, especially for startups in New York. 14 | 15 | Since we have all worked extensively with Node, we know that testing and managing environments between dev, testing, and production can be a pain. Rails does this so seamlessly you almost forget that it's happening. 16 | 17 | ### Rspec 18 | 19 | According to a [recent survey](https://www.jetbrains.com/research/devecosystem-2017/ruby/) a whopping 3/4 of Rails developers use Rspec as their test framework. It is undeniably the community favorite and the most likely to find on the job, but for some reason not the default for Rails, so we will have to fix that. 20 | 21 | ### Factory Bot 22 | 23 | Formerly Factory Girl, this lets us test with reliable and simple mock data. 24 | 25 | ### Jbuilder 26 | 27 | This is the inspiration for this particular lesson. In our Slack, we had a little exchange regarding which RDBMS would be best for GeoJSON. This should not be a concern. The way your underlying data is structured should not dictate the delivery of that data. JBuilder is not the fastest but definitely the easiest way to manage a JSON view layer in Ruby. 28 | 29 | ## Setup 30 | 31 | Just make sure that you have PostgreSQL, Rails 5, and Ruby 2.3+ installed on your machine. 32 | 33 | ```shell 34 | $ which ruby 35 | /Users/vincent/.rbenv/shims/ruby 36 | $ ruby -v 37 | ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16] 38 | $ rails -v 39 | Rails 5.2.0 40 | $ which psql 41 | /usr/local/bin/psql 42 | ``` 43 | 44 | 45 | 46 | Let's get started! 47 | 48 | In whatever directory you like, run the following command: 49 | 50 | ```shell 51 | rails new msg --api --database=postgresql --skip-test 52 | ``` 53 | 54 | `rails` is a command line program that generates code for you. It creates a directory called `msg` with lots of goodness inside. The `—api` flag instructs Rails to make a sort of bare bones app for more modern development rather than the usual soup to nuts approach. We prefer postgres in dev rather than the default of SQLite. Since we want to use Rspec, we have to skip the Rails default test framework, we tell it to not bother making tests for us out of the box. As opinionated as Rails is, it can be flexible as you can see. 55 | 56 | This is also a good time for a commit. 57 | 58 | ```shell 59 | git commit -m 'initial commit' 60 | ``` 61 | 62 | 63 | 64 | Now we will set up our postgres database by running `rails db:create` 65 | 66 | You should see that a development and test database was created for your postgresql installation. Rails has three environments: dev, test, and production. Each has it's own siloed database. You certainly don't want to cross pollinate here. 67 | 68 | #### Installing Rspec 69 | 70 | Before we even create a message model, we should install the gems for our testing suite. Open `Gemfile` and update the test/dev block: 71 | 72 | ```ruby 73 | group :development, :test do 74 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 75 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 76 | gem 'rspec-rails' 77 | gem 'factory_bot_rails' 78 | end 79 | ``` 80 | 81 | This is a little bit like your package.json block for —dev dependencies. We don't need this in the production bundle. While we are here, uncomment the `jbuilder` gem in the main block (should be about line 13 or so). 82 | 83 | Now run `bundle` from the command line to install, then `rails generate rspec:install` to let rspec create it's test directory. 84 | 85 | As always, let's make a sanity check (this is from the Rspec rails documentation) 86 | 87 | ```shell 88 | rails generate scaffold Person name:string 89 | rake db:migrate && rake db:test:prepare 90 | rspec spec --format documentation 91 | ``` 92 | 93 | This just generated well… a lot of code. It's pretty crazy and of **course** you would not use generated scaffolding in production. This is just for demonstration. The Rails scaffold generator creates a model, a database table, a controller with the 7 most common actions on it, all seven restful routes to match. It also creates `jbuilder` templates for JSON views. 94 | 95 | The scaffold provides us with a nice file structure out of the gates, as well as something to crib from for examples of well-written tests. When we do this for real, we can add just one at a time of course. Before we look at the tests, let's review the MVC application architecture through the generated code. 96 | 97 | ## Models 98 | 99 | These are the 'nouns' in your application. These correspond to rows in database tables. In our application we will make User and Message models. In the generator above we passed `name: string`. In Rails, database columns correspond to methods on the model. For any Person object, you can call it's name. Additional behavior can be added as columns on the DB or as methods on the model class. 100 | 101 | ## Routes 102 | 103 | Routes are the HTTP endpoints that your application will respond to. Each route corresponds to controller action. If you look at `config/routes.rb` you will see a single line `resources: widgets`. This generates the seven main routes most applications use. You can see which endpoints and which controller actions that they call by running `rails routes`. This differs a bit from Express where the functions that declare routes have their actions connected directly to them. 104 | 105 | ## Controllers 106 | 107 | These are the actions in your application. When an HTTP endpoint is hit, the controller code that corresponds to it is run. Generally you want the controllers to have the bare minimum of code and functionality and put more of that on the model level. 108 | 109 | ## Views 110 | 111 | Normally, in a 'regular' Rails application, you would have a bunch of html templates here, one for each controller action. There will be `index.html.erb`, `show.html.erb` etc. These would be the HTML files and partials that are generated on the fly and sent to the client. But we are making an API that we can use to serve data to a React, Angular, or Vue application. 112 | 113 | Instead of HTML, we will use [Jbuilder](https://github.com/rails/jbuilder), which let's us create templates that turn Ruby code into JSON. It is best practice to not let your underlying data, your database, dictate how you serialize data that you send to the client or to separate services. Jbuilder helps a lot with this. It has a nice and intuitive DSL (domain specific language) for structuring your JSON that is easy to get the hang of. The docs on their Github page is the best place to learn more or look stuff up. 114 | 115 | # Tests 116 | 117 | Since we have installed `rspec` and `factory_bot` it also generated test files to test the generated code. 118 | 119 | ## Factories 120 | 121 | A factory is a sort of a mock fixture that can easily serve as the stand in for a real instance of the model class (a person, a message, a widget, whatever). The factory syntax is easy to get a hang of on [thoughtbot's documentation on GitHub](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md). Let's make our example Person have the name `jessica jones`. 122 | 123 | ```ruby 124 | #spec/factories/people.rb 125 | FactoryBot.define do 126 | factory :person do 127 | name "jessica jones" 128 | end 129 | end 130 | 131 | ``` 132 | 133 | ## Models 134 | 135 | The factory objects are used in the model tests to provide an easy abstraction of that class. The scaffolding command doesn't generate any model tests for us so let's go through one. 136 | 137 | Say we want the API to return a Person with their name formatted properly. Here is what our test would look like: 138 | 139 | ```ruby 140 | # /spec/models/person_spec.rb 141 | 142 | require 'rails_helper' 143 | 144 | RSpec.describe Person, type: :model do 145 | describe "Display of proper name" do 146 | it 'returns its name in title case' do 147 | person = create(:person) 148 | expect(person.capitalized_name).to eq('Jessica Jones') 149 | end 150 | end 151 | end 152 | 153 | ``` 154 | 155 | 156 | 157 | Here we see the value of factory_bot. Here, `create(:person)` just churns a Person out of the factory according to the structure that you determine in the factory file. This keeps the mock setup separate from the test. 158 | 159 | For those of you who were here for the node/express version of testing APIs class with me, this might look familiar. 160 | 161 | A unit test is usually structured just like this whether you are using Jasmine, Mocha, or Jest in JavaScript, Pytest or UnitTest in Python, or Rspec or Minitest in Ruby. You have some language-specific setup, then a describe block to logically group functionality, an it block which tests a specific atom of functionality, and an expectation that given a certain input, you will get a certain output. 162 | 163 | In Rspecs' case, [the syntax for this is fairly complex](https://relishapp.com/rspec/rspec-rails/v/3-7/docs), so we will only touch on the most go-to expectations. Since we just wrote our first test today (on our own that is) we can run the thing and watch it fail! Whoo! 164 | 165 | ```shell 166 | rspec spec --format documentation 167 | ``` 168 | 169 | FYI `rspec` invokes the rspec binary. You _might_ have to run it as `bundle exec rspec spec` if you don't have the same setup as me. spec is the directory in which you recursively run the tests. If you are working in a narrow part of the app like just in models, you can run `rspec spec/models` to make things run more quickly. Adding `—format documentation` makes your tests verbose and all which I like. Feel free to omit if you prefer succinct output. 170 | 171 | Now your test should be failing, so we need to write the code to make it actually work. 172 | 173 | ```ruby 174 | # app/models/person.rb 175 | class Person < ApplicationRecord 176 | 177 | def capitalized_name 178 | self.name.titleize 179 | end 180 | 181 | end 182 | ``` 183 | 184 | Just in case you are wondering, Ruby supports an implicit return, meaning that the last thing to evaluate in a method gets returned from a method. 185 | 186 | Now if we run our tests, the model test that we added should work. 187 | 188 | ## Controller tests 189 | 190 | Controller tests were generated by the scaffolding command, but they aren't super useful since well, they are scaffolding tests created for demonstration purposes. Let's knock them out so we can use factory_bot and something that resembles a proper test. 191 | 192 | Make your `people_controller_test.rb` look like so: 193 | 194 | ```ruby 195 | require 'rails_helper' 196 | 197 | RSpec.describe PeopleController, type: :controller do 198 | render_views 199 | end 200 | ``` 201 | 202 | Let's write some detailed tests for the index and create endpoints at least. 203 | 204 | 205 | 206 | End result: 207 | 208 | 209 | 210 | ```ruby 211 | require 'rails_helper' 212 | 213 | RSpec.describe PeopleController, type: :controller do 214 | render_views 215 | 216 | describe "GET #index" do 217 | 218 | it "returns a success response" do 219 | create(:person, name: 'daredevil') 220 | create_list(:person, 3) 221 | 222 | get :index, format: :json 223 | 224 | res = JSON.parse(response.body) 225 | 226 | expect(response).to be_success 227 | expect(res[0]['name']).to eq('daredevil') 228 | expect(res.length).to eq(4) 229 | end 230 | end 231 | 232 | 233 | describe "POST #create" do 234 | it "creates a new Person" do 235 | expect { 236 | post :create, params: {person: {name: 'luke cage'}}, format: :json 237 | }.to change(Person, :count).by(1) 238 | end 239 | 240 | it "renders a JSON response with the new person" do 241 | post :create, params: {person: {name: 'iron fist'}}, format: :json 242 | res = JSON.parse(response.body) 243 | 244 | expect(response).to have_http_status(:created) 245 | expect(response.content_type).to eq('application/json') 246 | expect(res['name']).to eq('iron fist') 247 | end 248 | end 249 | end 250 | ``` 251 | 252 | 253 | 254 | ## Views 255 | 256 | Our View layer is just JSON since this is a nice simple API. We are also _implicitly_ testing the view layer since the controllers are wired to return templates with the jbuilder-created JSON. In a normal Rails application, this would require end-to-end tests with Capybara or some other similar acceptance test framework. We will make updates to the jbuilder views and test it in the controller tests. 257 | 258 | Let's first create a Person in dev and see what it looks like in Postman. 259 | 260 | At the View layer, we can customize the JSON response. View files that start with an underscore are **partials** which can be reused in various templates. Let's open `app/views/people/_person.json.jbuilder`, which is reused in the show and index views. 261 | 262 | Here, we can use the Person model method that we created. We can alter the partial as such. 263 | 264 | ```ruby 265 | json.name person.capitalized_name 266 | json.birthday person.created_at.strftime('%B %d') 267 | json.url person_url(person, format: :json) 268 | ``` 269 | 270 | Now we have a friendlier looking name and created_at rather than just spitting back exactly what is in the database. We can see this change in Postman. 271 | 272 | But our tests! I trust you can fix them. 273 | 274 | # Solo work 275 | 276 | Generate a new model the proper way: 277 | 278 | ```shell 279 | rails generate model Message text:string 280 | rails db:migrate 281 | ``` 282 | 283 | 284 | 285 | Make a message index json response only show the first 200 characters. Write a test to make sure that this will work. Make the show view show the full text of the message. 286 | 287 | Don't use the scaffold generator! After the generator above you can create the controller and such on your own from scratch. Challenge yourself to figure out how to test all seven RESTful routes. ÷ 288 | 289 | # Takeaways 290 | 291 | **Environments** 292 | 293 | Rails enforces separation of the dev, test, and production environments out of the box. This is best practice, and Rails just makes it easier. You can implement the same in any language. 294 | 295 | **Data doesn't need to be the same as your view** 296 | 297 | Jbuilder is just one solution for serializing data from the database for consumption by the view or frontend application. The application layer of the stack should take care of disconnects between the database and the view. 298 | 299 | **Testing** 300 | 301 | I cannot harp on this enough. As a developer you will spend more than half of your time testing. An applicant that has no experience testing is an easy no thank you. Once you get the hang of your testing framework, it's not too hard. For future projects to showcase to employers, you should start testing from the beginning. Nothing says 'job ready' than someone who has considerable coverage even for her personal projects. 302 | 303 | 304 | 305 | --------------------------------------------------------------------------------