Code for Startup MVP (Ruby on Rails) Session 2
description
Transcript of Code for Startup MVP (Ruby on Rails) Session 2
Learning to Code for Startup MVP
Presented by Henry Shi
Agenda – Wednesday November 7
1. Review of Last Session
2. Ruby Basicso Syntax and semanticso Practice makes perfect
3. Rails Models (but no Bottles)o ORM and SQL introductiono Migrationso Making the User modelo Signup/Signin/Signout
Prework – Setup
• Windows (not recommended if possible):o http://railsinstaller.org/o Use Sublime Text for your text editor
• OSX:o http://railsinstaller.org/o This includes osx-gcc-installer (200mb)
• Linux:o http://blog.sudobits.com/2012/05/02/how-to-install-rub
y-on-rails-in-ubuntu-12-04-lts/
Prework - Git
Install git if not already included:http://www.git-scm.com/book/en/Getting-Started-Installing-Git
Configure Git:git config --global user.name "Your Name“git config --global user.email
Review of Last Session
1. The Web and How it Works
2. Git/Github
3. Rails and Ruby
4. Heroku
The Web - Overview
GIT/GITHUB
• What is GIT?• Distributed Version Control System (DVCS)
• Why should I care?o Never lose data or accidentally overwrite, delete fileso Collaborate with peers anywhere and stay in sync
automatically (no more _v1, _v2, _final, _final_final…)o Compare and track changes over time, and easily
revert changeso Deploy code to real web
Rails
• Ruby on Rails is an open-source web framework that’s optimized for programmer happiness and sustainable productivity.
• It lets you write beautiful code by favoring convention over configuration.
• 80/20 Rule =>great for Startup MVP
Heroku
What is Heroku?• a hosted platform built specifically for
deploying Rails and other web applications in 1 command
• Best thing since sliced bread
Ruby – Programmer’s Best Friend
• Ruby is a dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write.
• We will only cover the necessary syntax needed to create a rails app
• Thankfully, its not a lot
Interactive Ruby Shell
• For the following slides, you should follow along with the Interactive Ruby Shell (irb)
• Open a terminal, type irb and press enter
Ruby - Strings
• Characters (letters, digits, punctuation) surrounded by quotes
• Can perform operations on strings, concatenation, length, empty, etc
food = "chunky bacon"puts "I'm hungry for, #{food}!">> "I'm hungry for, chunky bacon!"
“Hello” + “World”>> “Hello World"“Henry”.empty?>> false
Ruby - Numbers
• Self Explanatory
• Can add different types of numbers directly
123.class>> Fixnum
(123.0).class>> Float
Ruby - Symbols
• Characters (letters, digits, punctuation) preceded by colon (:)
• Lightweight strings• immutable
food = :hello:asf3fasdf.class>> Symbol
Ruby - Array
• List surrounded by square brace and separated by commas, zero indexed
• Can perform operations on arrays, add, remove, reverse etc
a = [1, 2, 3]b = ('a'..'e').to_a # ["a", "b", "c", "d", "e"]c = %w[foo bar baz quux] # ["foo", "bar", "baz", "quux"]d = "foo bar baz".split # ["foo", "bar", "baz"]
a = a.reverse # [“world”, “hello”, 3, 2, 1]
a.push(“hello”) # [1, 2, 3, “hello”]a << “world” # [1, 2, 3, “hello”, “world”]
a[0] # 3
a.delete(“hello”) # [“world”, 3, 2, 1]
Ruby - Hash
• Hash is a dictionary surrounded by curly braces• Dictionaries match words with their definitions
• New (better) hash notation in Ruby 1.9+
• Important for passing optional params (can omit braces if hash is last argument)
my_var = {:sup => "dog", :foo => "bar"}my_var[:foo]>> "bar“my_var[:nk] = “new” # {foo : "bar“, nk: “new” , sup : "dog" }
{sup : "dog", foo : "bar"}.class #Ruby 1.9+ >> Hash
Ruby – Methods (Functions)
• Function that performs some operations when called and returns something when done
• Implicitly returns last expression in method• Use Ruby poetry style:
o a.should(be() >= 7) #bado a.should be >= 7
Ruby – Blocks
• Block of Code surrounded by curly braces
• Can use Do and end to indicate block as well
• Can take argumentso variables surrounded by pipe (|)
2.times { puts "hello"}>> "hello">> "hello"
2.times do puts "hello“end
2.times do |i| puts "hello {#i}”end>> "hello 0">> "hello 1"
Ruby – Blocks (Advanced Only)
• Blocks are closures: they carry their environment around with them
• Block are anonymous λ functions• Examples compared to scheme:
o (map '(lambda (x) (+ x 2)) mylist )
o mylist.map { |x| x+2 }
• Try this: ('a'..'z').to_a.shuffle[0..7].join
(map'(lambda (x) (+ x 2))
(filter '(lambda (x) (even? x)) mylist))
mylist.select {|x| x.even?}.map {|x| x+2 }
Ruby – Blocks, Methods, Hashes
def list_hash(options = {:default => "foo"})options.each do |key, value|
puts "key '#{key}' points to '#{value}'"end
end
list_hash override : "bar")>> "key 'override' points to 'bar'"
list_hash multiple : "values", can : "be_passed")
>> "key 'multiple' points to 'values'"
>> "key 'can' points to 'be_passed'"
Ruby – Hashes in Rails
• Used heavily as parameters
Ruby – Classes and Objects
• Ruby, like many object-oriented languages, uses classes to organize methods; these classes are then instantiated to create objects
Ruby – Classes and Objects
• Most common uses will be in Models and Controllers
• attribute accessors (attr_accessor) corresponding to a user’s name and email address.
• This creates “getter” and “setter” methods that allow us to retrieve (get) and assign (set) @name and @email instance variables
Ruby Class and Object Example
• Save the above code into a file called example_user.rb• Run the following in irb
Ruby – Classes and Objects (Advanced Only)class SavingsAccount < Account # inheritance
# constructor used when SavingsAccount.new(...) calleddef initialize(starting_balance=0) # optional argument@balance = starting_balance
enddef balance # instance method
@balance # instance var: visible only to this objectenddef balance=(new_amount) # note method name: like setter
@balance = new_amountend
def deposit(amount)@balance += amount
end@@bank_name = "MyBank.com" # class (static) variable# A class methoddef self.bank_name # note difference in method def@@bank_name
end# or: def SavingsAccount.bank_name ; @@bank_name ; end
end
Ruby – Objects and Method Calls (Advanced Only)
1.send(:+, 2)my_array.send(:[], 4)my_array.send(:[]=, 3,"foo")if (x.send(:==, 3)) ...self.send(:my_func, z)
• Even lowly integers and nil are true objects:57.methods
57.heinz_varietiesnil.respond_to?(:to_s) "
• Rewrite each of these as calls to send:"– Example: my_str.length => my_str.send(:length)1 + 2
my_array[4]my_array[3] = "foo"if (x == 3) ....my_func(z)
• When you are calling a method, you are actually sendinga method call to the receiver object, which responds
Ruby – Method Calls (Advanced Only)
yy
= [1,2] = y + ["foo",:bar] # => [1,2,"foo",:bar]
yy
<< 5<< [6,7]
# => [1,2,"foo",:bar,5] # => [1,2,"foo",:bar,5,[6,7]]
• Remember! These are nearly all instance methods of Array—not language operators!"
• So 5+3, "a"+"b", and [a,b]+[b,c] are all differentmethods named '+'"– Numeric#+, String#+, and Array#+, to be specific"
• Every operation is a method call
• a.b means: call method b on object a– a is the receiver to which you send the method call,
assuming a will respond to that method"
Ruby – Practice
• Tryruby.org (code in ruby on your browser and work through free exercises)
• Read Section 4.1 to 4.5 of Ruby on Rails Tutorial by Michael Hartl
Rails - Models
We will focus on Models in this section
But First, we must understand the underlying datastore that actually stores the data
Databases, Tables, SQL
Rails – Database backed Models
• Store and access massive amounts of data• Table
o Columns (name, type, modifier)o Rows
Table:Users
SQL
• Structured Query Languageo A way to talk to databases
• Operations (CRUD)o Createo Read (Query)o Updateo Deleteo Schema creation and modification
SELECT *FROM BookWHERE price > 100.00ORDER BY title;
Rails – Object Relational Mapping
• Maps database backend to ruby objects• ActiveRecord (Rail’s Default ORM)
>> userVariable = User.where(name: "Bob")
>> userVariable.name=> Bob
Generates:SELECTWHERE
"users".* FROM "users"(name = 'bob')
Rails – Object Relational Mapping
>> userVariable = User.where(name: "Bob")
• Plural of Model name is table name (User -> users)
• Subclassing from ActiveRecord::Base “Connects” a model to the databaseo Provides CRUD operations on the modelo Database table column names are getters & setters for model
attributeso Model attributes automagically defined from the database table
columns
models/user.rbclass User < ActiveRecord::Base attr_accesor :name, :emailend
Rails – Creating Users
• We could start from scratch and create all aspects of the Users models from scratch, but that wouldn’t be in the philosophy of an MVP
• What additional functions might a user model need?o Registrationo Log in/Log out (sessions)o Reset/Lost Passwordo Email confirmationso Invitations to friends
Rails – Creating Users - Devise
• We will use the awesome Gem: Devise• Gems are packages/libraries for your rails project• Before coding, always see if a gem exists at The Rails Toolbox
Rails - Devise
• Create a new rails appo rails new MiniTwitter
• Open Gemfile (from last class)
• Add the line:Gem ‘devise’, ‘2.1.0’
• Run Bundle install from the console
• Install Devise by typing in the console: rails generate devise:install
• Generate the user by typing in the console:rails generate devise User
• Run the migration by typing in the console:Bundle exec rake db:migrate
Rails – Devise
• You may seem some hints/warnings:
Rails – Devise
• Go to http://localhost:3000/users/sign_up to see Devise in action!
• Sign up a fake user account and now try to log in at http://localhost:3000/users/sign_in
• Rails never shows or stores passwords in plaintext
Rails – Devise
• What did we just do?o rails generate devise User
o Focus on Migration and User Model
Rails – Migrations
• Create data structure in your database• Set a database’s schema incrementally
o Consistent across multiple machines (no conflicts)o Easy to upgrade, rollback, track changes, etc
• Migration is automatically created every time you create a model
• Open db/migrate/[timestamp]_devise_create_users.rb
Rails – Migrations• Creates a table named
Users
• Adds Columns:o Emailo Encrypted_passwordo Etc, etco T.timestamps creates the
columns created_at and updated_at autmagically
o Can pass parameters to columns, default values, nullable, etc
• Adds Indices:o Ensures uniquenesso Faster searchingo Index on email
Rails – Migrations
• Active Record Maps ruby objects to database• User.email
Rails – Migrations
• Database looks like:
• Same as what was specified in the migration
Rails – Migrations
• Run a migration
• Rollback (undo) a migration
>> bundle exec rake db:migrate
>> bundle exec rake db:rollback
Rails – Migrationsrails generate migration AddNameToUsers name:string
• Creates a migration automagically on the users table because we followed naming conventionso AddXXXToYYY followed by column name and type
• More info: http://guides.rubyonrails.org/migrations.html
Rails – Models
• Open app/models/user.rb
• Attr_accessible is important for preventing mass assignment vulnerability
• Notice we don’t have any attributes explicitly defined from the database (ORM maps attributes implicitly in Rails’ Activerecord)o Can remind ourselves of the database columns using ‘annotate’ gem
Rails – Attributes
• Open app/models/user.rb
• Can add methods to the user modeldef unchanged_duration
updated_at – Time.now
end- Duration in which the user was not modified
Rails – Models- Validations
• Check your parameters before save• Provided by ActiveModel
class Person < ActiveRecord::Basevalidates :title, :presence => true
end
bob = Person.create(title: nil)>> bob.valid?=> false>> bob.save=> false
Rails – Models- Validations
• Rails’ built in validation
• Can also write your own validations
:acceptance => Boolean.:confirmation => Boolean.:exclusion => { :in => Enumerable }.:inclusion => { :in => Enumerable }.:format => { :with => Regexp, :on => :create }.:length => { :maximum => Fixnum }.:numericality => Boolean.:presence => Boolean.:uniqueness => Boolean.
class User < ActiveRecord::Basevalidate :my_custom_validationprivate
def my_custom_validationself.errors.add(:coolness, "bad") unless self.cool ==
“supercool”end
end
Rails – Models and Migrations Exercises
• Create a migration to add first_name and last_name to the User table
• Add validation for user’s email, first_name and last_name such that they must be present
• Make a method full_name on user object to retrieve user’s full name by concatenating first and last name
Rails - Models
• Further ReadingRuby on Rails Tutorial – Michael HartlSection 6.1 – 6.2 (6.3 optional)
Git Commit
git initgit add .git commit –m “Initial Commit of MiniTwitter”(optional) git remote add origin
[email protected]:<username>/first_app.git(optional)git push –u origin master
Heroku – New MiniTwitter App
• Sign up for Heroku (it’s Free!) http://api.heroku.com/signup
• Install the Heroku Toolbelt https://toolbelt.heroku.com/
• Heroku login• Heroku create
o This will create a heroku app and tell you the url of your app
• Git push heroku mastero This’ll deploy your code to Heroku. Let it do its magic!
• Heroku run rake db:migrate• Heroku open
Next Time…
• Exploring Rails deeper• More about Controllers and Views• Building toward our Twitter app posts, friends,
followers, feeds, etc• Stay Tuned….
• Thanks!
Rails - Appendix
• If we have time…
Rails – Models - Create
• Must call save or save! on an AR modelinstance to actually save changes to DB"– '!' version is “dangerous”: throws exception ifoperation fails"
– create just combines new and save
• Once created, object acquires a primary key(id column in every AR model table)"– if x.id is nil or x.new_record? is true, xhas never been saved"– These behaviors inherited from ActiveRecord::Base—not true of Ruby objects in general"
Rails – Models - Read
• Class method where selects objects based onattributes
Movie.where("rating='PG’)Movie.where('release_date < :cutoff and
rating = :rating',:rating => 'PG', :cutoff => 1.year.ago)
Movie.where("rating=#{rating}") # BAD IDEA!• Can be chained together efficiently
kiddie = Movie.where("rating='G')
old_kids_films =kiddie.where "release_date < ?",
30.years.ago
Rails – Models - Read
Movie.find(3) #exception if not foundMovie.find_by_id(3) # nil if not found
• Dynamic attribute-based finders using
Movie.find_all_by_rating('PG')Movie.find_by_rating('PG')Movie.find_by_rating!('PG')
• Find Models by id
Rails – Models - Delete
• Note! destroy is an instance methodm = Movie.find_by_name('The Help')m.destroy
• Thereʼs also delete, which doesnʼt triggerlifecycle callbacks weʼll discuss later (so,avoid it)"
• Once an AR object is destroyed, you canaccess but not modify in-memory object
m.title = 'Help'