Skip to content

Instantly share code, notes, and snippets.

@amysimmons
Last active December 26, 2017 06:48
Show Gist options
  • Select an option

  • Save amysimmons/8d41cb46845753bb66bb to your computer and use it in GitHub Desktop.

Select an option

Save amysimmons/8d41cb46845753bb66bb to your computer and use it in GitHub Desktop.
WDI Week 10 Notes

#WDI Week 10 Notes

#Monday

##Burning Airlines demos

https://github.com/amysimmons/burning-airlines

##Closures


<button>Click me</button>

$(document).ready(function(){
    
    var msg = "hello world";
    
    var counter = 0
    
    console.log(msg);    
    
    $('button').on('click', function(){
         $('body').append(msg);   
        counter++;
        console.log(counter);
    });
});

console.log(msg);

the last console.log msg is undefined here because the whole function doesnt run until the document is ready, whereas the console.log will run straight away they're also in different function scopes

to fix you could do var msg outside, above the function, and just use msg = 'hello world' inside the function



var greeter = function(greeting, name){
    console.log(greeting + ' ' + name + '!');
};

var greeterMaker = function(greeting){
    var customGreeting = greeting;
    var greeter = function(name){
         console.log(customGreeting + ' ' + name + '!');
    };
    return greeter;
};

greeter('Howdy', 'Barry');

greeterMaker('Gday');

var gdayGreeter = greeterMaker('Gday');
var howdyGreeter = greeterMaker('Howdy');

gdayGreeter('Norm');
howdyGreeter('Barry');

by defining the greeter function within another function and returning it, i can access the variables in customGreeting

first i create a factory that knows how to make other functions

greetermake itself returns another function

and now i can call each of those functions as htough i defined them that way in the first place


var idMaker = function (prefix) {
    var id = 0;
    return function () {
        return prefix + id++;
    };
};

var projectID = idMaker('PJ');
var itemID = idMaker('IT');

console.log(projectID());
console.log(itemID());

projectID();
projectID();
projectID();

console.log(projectID());
console.log(itemID());

Functional JavaScript:

http://shop.oreilly.com/product/0636920028857.do

This covers closures really well in the first chapter.

##Regexp

anything in between / and / is considered to be the regular expression

if you think about all the possible strings in the world, all a regular expression does it divides these strings into two groups

either they match, or they don't match

/at/

if i took the string hat, it does match this patters, but if i took the string ab or b, these don't match the above pattern

Ruby is really good at regular expressions

Regular Expressions are incredibly powerful and you can get a lot done without much code, despite that, lots of people don't know how to do them

With Ruby, regular expressions are literals

In other languages you have to put quotes around your regular expressiona nd pretend it is a string, but not with Ruby, same with JavaScript

[1] pry(main)> /at/
=> /at/

Does the pattern /at/ match anywhere here?

[2] pry(main)> "cat" == /at/
=> false
[3] pry(main)> "cat" =~ /at/
=> 1
[4] pry(main)> "panama" =~ /at/
=> nil
[5] pry(main)> /at/ =~ 'cat'
=> 1

If you get back a number, it measn it matches, if you get back a nil, it means it doesn't match.

The regular expressions are case sensitive.

So we have a nice way of saying, does a string contain another string?

[6] pry(main)> 'your majesty' =~ /maj/
=> 5

Reuturns the index of the letter where the match begins.

But this is the same as Ruby's index method

[8] pry(main)> "your majesty".index("maj")
=> 5

Where regular expressions are most powerful is

[9] pry(main)> "cat" =~ /.t/
=> 1
[10] pry(main)> "tea" =~ /.t/
=> nil

This regular expression says there has to be any character in front pf assword for it to return a match:

[11] pry(main)> "gimme your password" =~ /password/
=> 11
[12] pry(main)> "gimme your password" =~ /.assword/
=> 11
[13] pry(main)> "assword" =~ /.assword/
=> nil

The first example below returns nil because there is no character in the string, whereas in the second example the full stop is the character:

[14] pry(main)> "" =~ /./
=> nil

[15] pry(main)> "." =~ /./
=> 0

The escape character:

[16] pry(main)> "hello Mr. Giggles" =~ /Mr./
=> 6
[17] pry(main)> "hello Mrs. Giggles" =~ /Mr./
=> 6
[18] pry(main)> "hello Mrs. Giggles" =~ /Mr\./
=> nil
[19] pry(main)> "hello Mr. Giggles" =~ /Mr\./
=> 6

Metacharacters:

  • . any character

Quantifiers:

? 0 or 1

+ 1 or more 

* 0 or more 

The quantifier applies to the character immediately before it.

BamBam example:

[20] pry(main)> "BamBam" =~ /BamBam/
=> 0
[21] pry(main)> "Bam-Bam" =~ /Bam-Bam/
=> 0
[22] pry(main)> "Bam-Bam" =~ /Bam.Bam/
=> 0
[23] pry(main)> "BamBam" =~ /Bam.Bam/
=> nil
[24] pry(main)> "Bam+Bam" =~ /Bam.Bam/
=> 0

What I want to do is to tsay that the dash chracter is optional...

So this regular expression says that the dash can appear 0 or 1 times.

[25] pry(main)> "Bam-Bam" =~ /Bam-?Bam/
=> 0
[26] pry(main)> "BamBam" =~ /Bam-?Bam/
=> 0

Note that this doesn't match Bam--Bam:

[27] pry(main)> "Bam--Bam" =~ /Bam-?Bam/
=> nil

So I can use the 0 or more quantifier:

[28] pry(main)> "Bam--Bam" =~ /Bam-*Bam/
=> 0

[29] pry(main)> "Bam------------Bam" =~ /Bam-*Bam/
=> 0

But this won't work if there is any random char inside the dashes:

[30] pry(main)> "Bam------=------Bam" =~ /Bam-*Bam/
=> nil

So the way to solve this is to use this quantifier, which says Bam followed by any character any number of times, they can be the same character or completely different characters.

[31] pry(main)> "Bam------=------Bam" =~ /Bam.*Bam/
=> 0
[32] pry(main)> "BamBam" =~ /Bam.*Bam/
=> 0
[33] pry(main)> "Bam-Bam" =~ /Bam.*Bam/
=> 0

Note that this only works if there is two Bams, and any old junk inbetween:

[34] pry(main)> "Bam" =~ /Bam.*Bam/
=> nil

This says I am looking for one or more digits. This says match be the number, rather than just returning the index.

[41] pry(main)> m = ' $ 890 or best offer'.match(/\d+/)
=> #<MatchData "890">

You can use the OR pipe in your expression:

[42] pry(main)> /Fred|Wilma/ =~ "I hate you Fred"
=> 11
[43] pry(main)> /Fred|Wilma/ =~ "I killed Wilma"
=> 9
[44] pry(main)> /Fred|Wilma/ =~ "I killed Wilma and Fred"
=> 9
[45] pry(main)> /Fred|Wilma|Pebbles/ =~ "I abducted Pebbles and ate her skin"
=> 11

You can look for a pipe in your string:

[46] pry(main)> "some|string|with|pipes" =~ /\|/
=> 4

Looking for upper and lower case or both:

[47] pry(main)> "mr hummerdunner" =~ /[mM]r/
=> 0
[48] pry(main)> "Mr hummerdunner" =~ /[mM]r/
=> 0
[49] pry(main)> "MR hummerdunner" =~ /[mM]r/
=> nil
[50] pry(main)> "MR hummerdunner" =~ /[mM][rR]/
=> 0
[51] pry(main)> "Mr hummerdunner" =~ /[mM][rR]/
=> 0
[52] pry(main)> "mR hummerdunner" =~ /[mM][rR]/
=> 0
[53] pry(main)> /[a-z]r/ =~ "Tr"
=> nil
[54] pry(main)> /[a-z]r/ =~ "rr"
=> 0
[55] pry(main)> /[A-Z]r/ =~ "RR"
=> nil
[56] pry(main)> /[A-Za-z]r/ =~ "pr"
=> 0
[57] pry(main)> /[A-Za-z]r/ =~ "7r"
=> nil
[58] pry(main)> /[0-9]r/ =~ "7r"
=> 0
[59] pry(main)> /[0-9]r/ =~ "Xr"
=> nil

Back references:

This is called a capturing group, with brackets around whatever you want...

[64] pry(main)> "mat mat" =~/(.at) \1/
=> 0
[65] pry(main)> "cat mat" =~/(.at) \1/
=> nil

The 1 is telling you which group of brackets to use

it refers to something that you have found earlier

so with this simple example im saying anything followed by at, followed by that simple patter again

Regular expressions practice:

https://gist.github.com/wofockham/e3db2d0e21e7201f76a0

http://rubular.com/

http://jessicaaustin.github.io/regexquest/game/

https://regex.alf.nu/

http://regexcrossword.com/

http://www.amazon.com/Mastering-Regular-Expressions-Jeffrey-Friedl/dp/0596528124

##Rake

###RakeFiles

rake => Ruby make

# create directory
directory "tmp"

# the task of creating my hello.tmp file depends on this tmp folder
file "tmp/hello.tmp" => "tmp" do
  # shell command, tells it to echo hello and append it to tmp/hello.tmp 
  # because this taks depends on having tmp folder exist, 
  # it will create the tmp folder for me first
  sh "echo 'Hello' >> 'tmp/hello.tmp'"
end

# in the terminal, we run rake hello.tmp
# =>mkdir -p tmp
# =>echo 'Hello' >> 'tmp/hello.tmp'
#now a tmp folder is created with a file hello.tmp and the file contains the word Hello
# now all these tasks are going to be prefixed by morning
# eg morning:turn_off alarm
namespace :morning do 

  desc "Turn off the goddam alarm"
  task :turn_off_alarm do 
    puts "Turning off the alarm.."
  end

  desc "make myself pretty"
  task :groom_self do 
    puts "Washing hair.."
    puts "Brushing teeth.."
    puts "Showering.."
  end

  #to make cups dynamic, we can use environment variables
  desc "love coffee"
  task :make_coffee do 
    cups = ENV["COFFEE"] || 2
    puts "Making #{(cups)} cups of coffee."
  end

  desc "hate little dogs"
  task :walk_dog do 
    puts "Walking horrible little dog..."
  end

  # get ready now depends on these other tasks, so it will run all the others first, then
  # run get_ready
  desc "another miserable day"
  task :get_ready => [:turn_off_alarm, :groom_self, :make_coffee, :walk_dog] do
    puts "oh god, ready to face the day"
  end

end

#this sets the default for rake
#now i can just run rake and it will perform this task
task :default => 'morning:get_ready'

#not brushing beard will be included in my groom_self method
#so you can extend a task if you want by adding additional stuff to an existing one
#this is useful if you don't have access to a method
namespace :morning do
  task :groom_self do
    puts 'Brushing beard...'
  end
end

namespace :afternoon do
  desc "making more coffee later in day"
  task :make_coffee do
    #task obejct inside of rake namespace
    #invoke will cause the code to be triggered
    # in the afternoon i can make coffee, 
    # and it will find the morning task and run that for me
    Rake::Task['morning:make_coffee'].invoke
    puts 'adding some rum...'
    puts 'ready for the afternoon...'
  end
end

# Additional notes:

# in the console run rake turn_off_alarm
# => Turning off the alarm..

# file_example $ rake make_coffee
# Making 2 cups of coffee.

# file_example $ rake get_ready
# Turning off the alarm..
# Washing hair..
# Brushing teeth..
# Showering..
# Making 2 cups of coffee.
# Walking horrible little dog...
# oh god, ready to face the day

# [1] pry(main)> ENV['USER']
# => "amysimmons"
# [2] pry(main)> ENV['RUBY_VERSION']
# => "ruby-2.1.4"

# coffee now dynamic:

# file_example $ COFFEE=5 rake get_ready
# Turning off the alarm..
# Washing hair..
# Brushing teeth..
# Showering..
# Making 5 cups of coffee.
# Walking horrible little dog...
# oh god, ready to face the day

# export COFFEE=23 would save the variable for every time you run rake get_ready, 
# you wouldn't need to set it again each time like this COFFEE=5 rake get_ready

# What the desc line does:
# if I run rake -T it will now list all the descriptions for each method

# file_example $ rake afternoon:make_coffee
# Making 2 cups of coffee.
# adding some rum...
# ready for the afternoon...

##Fish eater and Factory Girl and Faker

have a system that loads tweets from twitter

write a rake task that will populate this with actual tweets

you want to have a rake command that will be something like rake tweet:seed(Nike)

so when the front end poeple hook it up, all the fish in the tank will have tweets with Nike in in them

If you need to reseed the database, you could swap out the word Nike for Rebock and it would still work

Testing the rships in pry:

Last login: Mon Mar 16 14:14:40 on ttys003
You have new mail.
Hi Amy, welcome to the terminal!
tweeteater $ rails c
Loading development environment (Rails 4.2.0)
2.1.4 :001 > u = User.create :name => 'george', :email => '[email protected]'
   (0.3ms)  begin transaction
  SQL (1.6ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "george"], ["email", "[email protected]"], ["created_at", "2015-03-16 04:57:31.465764"], ["updated_at", "2015-03-16 04:57:31.465764"]]
   (1.3ms)  commit transaction
 => #<User id: 1, name: "george", email: "[email protected]", created_at: "2015-03-16 04:57:31", updated_at: "2015-03-16 04:57:31"> 
2.1.4 :002 > t = Tweet.create :post => "twett post yes"
   (0.1ms)  begin transaction
  SQL (1.0ms)  INSERT INTO "tweets" ("post", "created_at", "updated_at") VALUES (?, ?, ?)  [["post", "twett post yes"], ["created_at", "2015-03-16 04:58:23.790412"], ["updated_at", "2015-03-16 04:58:23.790412"]]
   (1.2ms)  commit transaction
 => #<Tweet id: 1, user_id: nil, post: "twett post yes", created_at: "2015-03-16 04:58:23", updated_at: "2015-03-16 04:58:23"> 
2.1.4 :003 > u.tweets << t
   (0.2ms)  begin transaction
  SQL (0.5ms)  UPDATE "tweets" SET "user_id" = ?, "updated_at" = ? WHERE "tweets"."id" = ?  [["user_id", 1], ["updated_at", "2015-03-16 04:58:44.582788"], ["id", 1]]
   (1.1ms)  commit transaction
  Tweet Load (0.3ms)  SELECT "tweets".* FROM "tweets" WHERE "tweets"."user_id" = ?  [["user_id", 1]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Tweet id: 1, user_id: 1, post: "twett post yes", created_at: "2015-03-16 04:58:23", updated_at: "2015-03-16 04:58:44">]> 
2.1.4 :004 > u.tweets
 => #<ActiveRecord::Associations::CollectionProxy [#<Tweet id: 1, user_id: 1, post: "twett post yes", created_at: "2015-03-16 04:58:23", updated_at: "2015-03-16 04:58:44">]> 
2.1.4 :005 > 

Created a factories.rb file in test folder

FactoryGirl.define do 

  factory :user do |f|
    name "George Best"
    email "[email protected]"
     # this will give a unique custom email 
    f.sequence(:email) {|n| "george#{n}@best.com"}
  end

end

then run n = FactoryGirl.create :user in the console

Gems for this lab:

Factory Girl gem - https://github.com/thoughtbot/factory_girl_rails

Faker gem - https://github.com/stympy/faker

Once Faker is installed, you can run this in the console

Faker::Name.name and Faker::Internet.email

and it will generate a random name or a random email

Faker::Lorem.sentence will generate a random lorem ipsum sentence

Faker::Hacker.say_something_smart generates random geek sentences

Generating random name, email and post data with Faker:

FactoryGirl.define do 

  factory :user do |f|
    f.sequence(:name) {Faker::Name.name}
    f.sequence(:email) {Faker::Internet.email}

    # creates a user, and creates tweets, and associates them with the user
    factory :user_with_tweets do |f|
      after(:create) do |u|
        #after creating the user, it will create a tweet, a random number of tweets
        # and all of these tweets are associated with the user
        FactoryGirl.create_list :tweet, Random.rand(10..100), :user => u
      end
    end
    
  end

  factory :tweet do |f|
    f.sequence(:post) {Faker::Hacker.say_something_smart}
  end

end

# Commands in the console
# FactoryGirl.create :user
# FactoryGirl.create :tweet
# FactoryGirl.create :user_with_tweets

#Tuesday

##Warmup

##Tweeteater Demos

##REGEXP II

Metacharacters:

[a-z] - character in this range

[^a-z] - char not in this range

[0-9] => \d

[^0-9] - any char that isn't a digit

Quantifiers:


* 0 or more
? 0 or 1
+ 1 or more

"AUD 12.78"

.match /[A-Z][A-Z][A-Z]/

is the same as 

.match /[A-Z]{3}

Capturing:

[19] pry(main)> if (m = line.match /([A-Z]{3})\s+(\d+\.\d+)/)
[19] pry(main)*   puts "#{m[1]} trades at #{m[2]}"
[19] pry(main)* end
[2] pry(main)> "Fred and Wilma".match /wilma/i
=> #<MatchData "Wilma">
[3] pry(main)> "AUD GBP and JPN and USD".match /[A-Z]{3}/
=> #<MatchData "AUD">
[35] pry(main)> pattern = /
[35] pry(main)* (?<currency>[A-Z]{3}) # matches a three letter currency code
[35] pry(main)* \s+ # followed by one or more spaces
[35] pry(main)* (?<amount>\d+\.\d+) # the dollar amount, as a decimal
[35] pry(main)* /x
=> /
(?<currency>[A-Z]{3}) # matches a three letter currency code
\s+ # followed by one or more spaces
(?<amount>\d+\.\d+) # the dollar amount, as a decimal
/x
[36] pry(main)> line
=> " USD    0.000124 "
[37] pry(main)> line.match pattern
=> #<MatchData "USD    0.000124" currency:"USD" amount:"0.000124">
[38] pry(main)>

##Interview Qs

##Portfolio

##Homework

##JS Hangman

#Wednesday

##Warmup

##Hangman demos

https://github.com/amysimmons/wdi8_homework/tree/master/amy/hangman

##RSPEC with Rails

###Fruitstore

rails new fruitstore_app -T

Included rpsec gem in development

rails generate rspec:install

rails generate model Fruit

there should be a spec folder with an rspec file for the fruit model

to run rspec we sometimes need to remove warnings from the .rspec file

it's normal to get an error saying we need to run migrations

delete line about pending in fruit_spec.rb and write some tests

require 'rails_helper'

RSpec.describe Fruit, type: :model do

  describe "An apple" do
    before do 
      @apple = Fruit.new
    end

    it 'should not be squishy' do 
      expect(@apple.squishy?).to == false
      # other ways of writing the test:
      # expect(@apple.squishy?).to be_false
      # expect(@apple.squishy?).to eq(false)
    end
  end

  describe "A pear" do 
    before do 
      @pear = Fruit.new
    end

    it 'should be kind of squishy' do 
      expect(@pear.squishy?).to == true
      # other ways of writing the test:
      # expect(@apple.squishy?).to be_true
      # expect(@apple.squishy?).to eq(true)
    end
  end

end

to run a rest, you can either run rspec, which will run all the tests, or rspec spec/models/fruit_spec.rb

created separate models for pear and apples

in the console:

fruitstore_app $ rails c
Loading development environment (Rails 4.2.0)
2.1.4 :001 > pear = Pear.new :name => 'Gnashi'
 => #<Pear id: nil, created_at: nil, updated_at: nil, name: "Gnashi"> 
2.1.4 :002 > apple = Apple.new :name => 'golden delicious'
 => #<Apple id: nil, created_at: nil, updated_at: nil, name: "golden delicious"> 
2.1.4 :003 > apple.class
 => Apple(id: integer, created_at: datetime, updated_at: datetime, name: string) 
2.1.4 :004 > pear.class
 => Pear(id: integer, created_at: datetime, updated_at: datetime, name: string) 
2.1.4 :005 > pear.is_a? Pear
 => true 
2.1.4 :006 > apple.is_a? Apple
 => true 
2.1.4 :007 > pear.is_a? Apple
 => false 
2.1.4 :008 > pear.save
   (0.3ms)  begin transaction
  SQL (1.0ms)  INSERT INTO "fruits" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "Gnashi"], ["created_at", "2015-03-18 00:24:11.539751"], ["updated_at", "2015-03-18 00:24:11.539751"]]
   (9.3ms)  commit transaction
 => true 
2.1.4 :009 > apple.save
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "fruits" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "golden delicious"], ["created_at", "2015-03-18 00:24:13.629400"], ["updated_at", "2015-03-18 00:24:13.629400"]]
   (8.9ms)  commit transaction
 => true 

But Pear.all will give me back the apple, and Apple.all will give me back the pear

So we want to use STI - Single Table Inheritance

rails generate migration AddTypeToFruits type:string

this is the only time you can use type in your table


2.1.4 :002 > p = Pear.new :name => 'gnashi'
 => #<Pear id: nil, created_at: nil, updated_at: nil, name: "gnashi", type: "Pear"> 
2.1.4 :003 > a = Apple.new :name => 'golden delicious'
 => #<Apple id: nil, created_at: nil, updated_at: nil, name: "golden delicious", type: "Apple"> 
2.1.4 :004 > p.save
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "fruits" ("type", "name", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["type", "Pear"], ["name", "gnashi"], ["created_at", "2015-03-18 00:29:04.454557"], ["updated_at", "2015-03-18 00:29:04.454557"]]
   (8.6ms)  commit transaction
 => true 
2.1.4 :005 > a.save
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "fruits" ("type", "name", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["type", "Apple"], ["name", "golden delicious"], ["created_at", "2015-03-18 00:29:05.513830"], ["updated_at", "2015-03-18 00:29:05.513830"]]
   (9.5ms)  commit transaction
 => true 
2.1.4 :006 > 

Now we can say Pear.all and it will only get me the ones where the type is equal to pear

Same for Apple.all

And Fruit.all will return everything

So we can have one table for all our fruits, which will autmoaticaally add the type when saved as pear, or apple, etc

2.1.4 :009 > p = Pear.first
  Pear Load (0.4ms)  SELECT  "fruits".* FROM "fruits" WHERE "fruits"."type" IN ('Pear')  ORDER BY "fruits"."id" ASC LIMIT 1
 => #<Pear id: 3, created_at: "2015-03-18 00:29:04", updated_at: "2015-03-18 00:29:04", name: "gnashi", type: "Pear"> 
2.1.4 :010 > p.class
 => Pear(id: integer, created_at: datetime, updated_at: datetime, name: string, type: string) 
2.1.4 :011 > p.is_a? Pear
 => true 
2.1.4 :012 > p.is_a? Apple
 => false 
2.1.4 :013 > f1 = Fruit.first
  Fruit Load (0.3ms)  SELECT  "fruits".* FROM "fruits"  ORDER BY "fruits"."id" ASC LIMIT 1
 => #<Pear id: 3, created_at: "2015-03-18 00:29:04", updated_at: "2015-03-18 00:29:04", name: "gnashi", type: "Pear"> 
2.1.4 :014 > f1.class
 => Pear(id: integer, created_at: datetime, updated_at: datetime, name: string, type: string) 
2.1.4 :015 > 

add gem 'shoulda-matchers' to development group

bundle

rails generate model Shelf

now we can add associations in our shelf and fruit spec files

it {should belong_to :shelf}

it {should have_many :fruits}

rails generate migration addShelfIdToFruits shelf_id:integer

the id will go on the fruit so multiple fruits can have a shelf

then add the associations to the models

the tests should now pass because our associations exist, even though we haven't created any associations yet

rake stats will tell me the tests and my lines of code

100 per cent of my lines of code should be covered by tests

add this to gemfile and rebundle

gem 'simplecov', :require => false, :group => :test

https://github.com/colszowka/simplecov

add this to spec helper.rb

require 'simplecov'

SimpleCov.start

now if we run spec, everything should be as it was before, but simplecov is also running behind the scenes watching these tests pass or fail, and it will generate a report for us

we can run open coverage/index.html to see our test coverage

###Scaffy

rails new scaffy -T

add rspec gem to gemfile

bundle

rails generate rspec:install

rails generate scaffold Post title:string content:text

this gives you an example of the testing that rails can write for you

##Cucumber

##SydJS

#Thursday

##Warmup

##SYDjs Recap

##RSPEC Functional Testing

Fruit.all.to_sql

The first line here makes the database do the work, and let's Ruby be lazier:

    @fruits = Fruit.order('id DESC')
    # @fruits = Fruit.all.reverse

we want to reverse the data at the sql level, not the ruby level, which is what the second line does.

##1pm guest speaker Lawrence

##Bonus Reviews

##Interview questions

##G from Lookahead

##Homework

use jquery animate for the slider

and a jquery method called stop with special parameters to get it to finish the old animation before it starts the next one

another problem is what to do when you get to the last image

it can also go get a copy of the first image and move it to the end, so it's like its on a loop, you can create a new element or do it with the existing one by changing the css

#Friday

##Warmup

##Carousel Demos

##jQuery plugins

http://learn.jquery.com/plugins/basic-plugin-creation/

Read: Functional JavaScript book

##Backbone

###Secrets

Steps:

Secret model

content:string

scaffold

API

From the start:

rails new whisper -T

rails generate scaffold Secret content:text

rake db:migrate

annotate

go to http://localhost:3000/secrets

create some new secrets

i can go to http://localhost:3000/secrets.json to see the new secrets in json format

they have ids, content, etc

what we want to make realtime about this is to load the page once, and continue requesting from the server saying can you give me the new secrets here

so when you visit this url / it will load everything, and talk behind the scenes to the /secrets urls

in congig routes, set the root to root :to => 'pages#index'

rails generate controller Pages index

now we can go to http://localhost:3000/ and the index page will be there

delete pages.coffee and index.coffee files

cd vendor/assets/javascripts

get backbone and underscore

curl http://underscorejs.org/underscore.js > underscore.js

curl http://backbonejs.org/backbone.js > backbone.js

(backbone development link)

we should now be able to go to http://localhost:3000/assets/backbone.js

we should now be able to go to http://localhost:3000/assets/underscore.js

to see the files

remove //= require turbolinks from application.js file

add backbone and underscore

//= require jquery
//= require jquery_ujs
//= require underscore
//= require backbone
//= require_tree .

create new whisper.js file

create folders for my other js files:

whisper $ cd app/assets/javascripts
javascripts $ mkdir models
javascripts $ mkdir collections
javascripts $ mkdir views
javascripts $ mkdir routers

dont forget to include handlebars in vendor/assets/javascripts

curl http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v3.0.0.js > handlebars.js

and require it

//= require jquery
//= require jquery_ujs
//= require underscore
//= require handlebars
//= require backbone
//= require_tree .

create secret.js in my javascript models folder

var whisper = whisper || {};

whisper.secret = Backbone.Model.extend({
  urlRoot: '/secrets',
  defaults: {
    content:''
  }

});

//= require models/secret in application.js

reload the homepage

the first thing this lets us do is have the ability to create secrets from the console

in the console run

s1 = new whisper.secret({content: 'Backbone created content'});

this means the secret is in the browser but it's not saved

backbone can solve this problem for me

if i create it here in the browser, i can then save it to the server

s1 = new whisper.secret({content: 'Backbone created content'});
child {cid: "c1", attributes: Object, _changing: false, _previousAttributes: Object, changed: Object…}
s1.attributes
Object {content: "Backbone created content"}
s1.save()
Object {readyState: 1, getResponseHeader: function, getAllResponseHeaders: function, setRequestHeader: function, overrideMimeType: function…}

so now i can create a secret and save it from the server in two lines of code

http://localhost:3000/secrets.json i can see my new secret


in my console i can do something like this to get my secrets

$.ajax('/secrets', {dataType: "json"}).done(function(results){
whisper.secrets = results;
});

but this is garbage ajax!

i dont want to have to write this every time i want to get new things from the server

so to make this work we want a collection.

so we make secrets.js in our js collections folder

var whisper = whisper || {};

whisper.Secrets = Backbone.Collection.extend({
  url: '/secrets',
  model: whisper.Secret
});

now we can go to http://localhost:3000/

and create a new instance of the secrets collection

and fetch all the secrets

whisper.secrets = new whisper.Secrets()
child {length: 0, models: Array[0], _byId: Object, constructor: function, url: "/secrets"…}
whisper.secrets.fetch()
Object {readyState: 1, getResponseHeader: function, getAllResponseHeaders: function, setRequestHeader: function, overrideMimeType: function…}
whisper.secrets
child {length: 4, models: Array[4], _byId: Object, constructor: function, url: "/secrets"…}

so if somebody goes and creates a new secret, if backbone every wants to get me all the things on the server

backbone can just run.fetch again

when i call fetch it goes and gets the updated list

the next thing we want to do is create a view

create AppView.js in the js views folder

in index.html.erb

<h1>Tell me all your secrets</h1>
<div id="main"></div>

<script type="text/x-handelbars-template" id="appView">
  <div id="new-secret"></div>
  <div id="secrets"></div>
</script>


so i will know that this view is working when these two divs appear in the main

the job of the render function is to take this script tag, get the content, and stick it in here

so add the following two lines to the render function

var whisper = whisper || {};

whisper.AppView = Backbone.View.extend({
  el: '#main',
  render: function(){
    // get some html from the dom
    var html = $('#appView').html();
    // shove it into the element associated with this view 
    this.$el.html(html);
  }
});

to get this showing on the page we create router.js in the js router folder

var whisper = whisper || {};

whisper.Router = Backbone.Router.extend({

  routes: {
    // as soon as i load the page, look at the index, and run the index function
    '': 'index'
  },
  index: function(){
    var appView = new whisper.AppView();
    appView.render();
  }

});

now we need to create our actual router, and start the hisotry

in whisper.js

$(document).ready(function(){

  // create new instance of the class
  whisper.router = new whisper.Router();
  Backbone.history.start();

});

create SecretsView.js

var whisper = whisper || {};

whisper.SecretsView = Backbone.View.extend({
  el: '#secrets',
  render: function(){
    console.log('rendering the secrets');
  }
});

go back to the AppView.js and add this:

    var secretsView = new whiseper.SecretsView();
    secretsView.render();

but this still won't work because we havent fetched the secrets at any point

so in whisper.js,

lets make the first thing we do get the secrets

so in the whisper.js file

$(document).ready(function(){

  whisper.secrets = new whisper.Secrets();
  whisper.secrets.fetch().done(function(){

    // create new instance of the class
    whisper.router = new whisper.Router();
    Backbone.history.start();

  });

});

in AppView.js we want to pass in our secrets collection to the new secretsView

var secretsView = new whisper.SecretsView({collection: whisper.secrets});

now in SecretsView.js we can render the collection

var whisper = whisper || {};

whisper.SecretsView = Backbone.View.extend({
  el: '#secrets',
  render: function(){
    console.log('rendering the secrets');
    // #the collection is the collection that we passed in in appview.js
    var view = this
    this.collection.each(function(secret){
      console.log('secret', secret);
      var $li = $('<li>').text(secret.get('content'));
      view.$el.prepend($li);
    })
  }
});

the next thing we want to do is have the form with the create new secret button

In our index.html.erb file we adda new template for a new secret

<script type="text/x-handelbars-template" id="newSecretTemplate">
  <form>
    <textarea placeholder="Confess your secret..."></textarea>
    <button>Shh...</button>
  </form>
</script>

so now in the js views folder, we want a new file called newSecretView.js

var whisper = whisper || {};

whisper.NewSecretView = Backbone.View.extend({
  el:'#new-secret',
  render: function(){
    // make the view available
    var html = $('#newSecretTemplate').html();
    this.$el.html(html);

  }

})

add this to appview.js

    // within the app view render the new secret form 
    var newSecretView = new whisper.NewSecretView();
    newSecretView.render();

now we need to handle the actual form submission

this.$('textarea') will get me the textarea within this view

this.$('textarea').val() will get me its value

so i can use this to create a new secret

in NewSecretView.js:

var whisper = whisper || {};

whisper.NewSecretView = Backbone.View.extend({
  el:'#new-secret',
  events: {
    'submit form': 'createNewSecret'
  },
  render: function(){
    // make the view available
    var html = $('#newSecretTemplate').html();
    this.$el.html(html);
  },
  createNewSecret: function(event){
    event.preventDefault();
    var userContent = this.$('textarea').val();
    var secret = new whisper.Secret({content: userContent})
    secret.save();
    
  }

})

now i can create a new secret, and reload the page, and it will appear

but i dont want to have to reload the page

to fix this i can add the following three lines to the NewSecretsView.js

    // makes the secret show on page without refresh
    whisper.secrets.add(secret);
    var secretsView = new whisper.SecretsView({collection: whisper.secrets});
    secretsView.render();

Note: we added this.$el.empty(); to the secretsView so the list clears each time a new li is added

Note: we also added this.$('textarea').val(''); to the NewSecretsView so the form clears after submission

At this point Joel got https://ngrok.com/

This is so he could make a port available to everyone in the class

The final problem to fix is polling of the secrets

If we add a secret, Joel can't see it without refreshing the page.

to do this, we make an initialize function in secrets.js

and we moved the rendering of the secrets collection from AppView

secrets.js changes:

var whisper = whisper || {};

whisper.Secrets = Backbone.Collection.extend({
  url: '/secrets',
  model: whisper.Secret,
  initialize:function(){
    this.on('sync', function(){
      console.log('sync successful');
      // within the app view render the secrets collection
      var secretsView = new whisper.SecretsView({collection: whisper.secrets});
      secretsView.render();
    });

    var secrets = this

    secrets.fetch();

    setInterval(function(){
      secrets.fetch();
    }, 3000);
  }
});

we also made the following changes to whisper.js

$(document).ready(function(){

    // create new instance of the class
    whisper.router = new whisper.Router();
    Backbone.history.start();

    whisper.secrets = new whisper.Secrets();

});

In NewSecretView.js we replaced these lines:

    // secret.save();
    // makes the secret show on page without refresh
    // whisper.secrets.add(secret);

with these lines:

   secret.save();
    whisper.secrets.add(secret);

We also added the ability to stop polling in the newSecretView file.

##Atlassian visit

##YSlow

http://yslow.org/

minimise bandwidth

small file sizes

reduce round trips

You can run this on any website, and it will give you pointers as to how to make it faster

Google's page speed insights does something similar

https://developers.google.com/speed/pagespeed/insights/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment