├── 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 | 
--------------------------------------------------------------------------------
/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 | 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.
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 | 
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 | 
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 `
53 |
54 | Open a new file in vim
55 | `:e `
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 | 
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 :echoe "Use h"
106 | nnoremap :echoe "Use l"
107 | nnoremap :echoe "Use k"
108 | nnoremap :echoe "Use j"
109 | ```
110 |
111 | Leader key:
112 |
113 | `let mapleader = " "`
114 |
115 | `nmap 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 |
34 |
35 |
36 |
37 | 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 | 
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 | 
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 | 
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 | 
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 |  | 
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 | 
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 |
--------------------------------------------------------------------------------