17. Address Book
■ Generate a new Rails Application
■ Generate some stuff
■ Prepare the database
■ Start the application
■ View application and be excited!
32. Terminal
$ cd address_book
$ ./script/server
=> Booting Mongrel
=> Rails 2.1.0 application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with development environment...
** Rails loaded.
** Loading any Rails specific GemPlugins
** Signals ready. TERM => stop. USR2 => restart.
** Rails signals registered.
** Mongrel 1.1.4 available at 0.0.0.0:3000
** Use CTRL-C to stop.
49. Objects, variables & methods
class Person
attr_accessor :name
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
50. class name
Objects, variables & methods
class Person
attr_accessor :name
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
51. class name
Objects, variables & methods
instance
variable
class Person
attr_accessor :name
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
52. class name
Objects, variables & methods
instance
variable
class Person method
attr_accessor :name
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
53. class Person
attr_accessor :name
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
Console: using a class
54. class Person
attr_accessor :name
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
Console: using a class
>> andre = Person.new
>> andre.name = ‘Andre’
55. class Person
attr_accessor :name
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
Console: using a class
>> andre = Person.new
>> andre.name = ‘Andre’
>> bart = Person.new
>> bart.name = ‘Bart’
56. class Person
attr_accessor :name
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
Console: using a class
>> andre = Person.new
>> andre.name = ‘Andre’
>> bart = Person.new
>> bart.name = ‘Bart’
>> bart.insults(andre, “dog”)
“Bart thinks Andre is a stupid dog!”
57. Inheritance
class Person
attr_accessor :name
end
class Student < Person
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
58. Inheritance
Student inherits
class Person
attr_accessor :name from person
end
class Student < Person
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
65. Modules
module Insulting
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
class Person
attr_accessor :name
include Insulting
end
66. Creates an
Modules ‘Insulting’ module
module Insulting
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
class Person
attr_accessor :name
include Insulting
end
67. Creates an
Modules ‘Insulting’ module
module Insulting
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
Person
end
classes can
class Person
‘Insult’
attr_accessor :name
include Insulting
end
68. Creates an
Modules ‘Insulting’ module
module Insulting
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
Person
end
classes can
class Person class Robot
‘Insult’
attr_accessor :name attr_accessor :name
include Insulting include Insulting
end end
Everyone can
insult now!
69. module Insulting
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
class Person
attr_accessor :name
end
Console: extending on the fly
70. module Insulting
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
class Person
attr_accessor :name
end
Console: extending on the fly
>> andre = Person.new
>> andre.name = “Andre”
>> andre.extend(Insulting)
nil
71. module Insulting
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
class Person
attr_accessor :name
end
Console: extending on the fly
>> andre = Person.new
>> andre.name = “Andre”
>> andre.extend(Insulting)
nil
>> andre.insults(bart)
“Andre thinks Bart is a stupid cow!”
72. module Insulting
def insults(other, object=quot;cowquot;)
quot;#{name} thinks #{other.name} is a stupid #{object}!quot;
end
end
class Person
attr_accessor :name
end
Console: extending on the flyWe could also extend
an entire class like this!
>> andre = Person.new
>> andre.name = “Andre”
>> andre.extend(Insulting)
nil
>> andre.insults(bart)
“Andre thinks Bart is a stupid cow!”
82. class Person
attr_accessor :name
end
Console: using blocks
83. class Person
attr_accessor :name
end
Console: using blocks
>> people
[<#Person:0x00 @name=”Andre”>,<#Person:0x00 @name=”Bart”>]
84. class Person
attr_accessor :name
end
Console: using blocks
>> people
[<#Person:0x00 @name=”Andre”>,<#Person:0x00 @name=”Bart”>]
>> people.map{ |item| “#{item.name} is kewl” }
[“Andre is kewl”, “Bart is kewl”]
85. class Person
attr_accessor :name
end
Console: using blocks also have: select, reject and inject to
We
work with collections!
>> people
[<#Person:0x00 @name=”Andre”>,<#Person:0x00 @name=”Bart”>]
>> people.map{ |item| “#{item.name} is kewl” }
[“Andre is kewl”, “Bart is kewl”]
93. Models
Talk to the database, contain business logic
Controllers
Provide the glue, prepare data when needed
Views
Show the content to the user
94. You are not a beautiful and unique
snowflake. You are the same decaying
organic matter as everyone else, and we
are all a part of the same compost pile.
— Tyler Durden, Fight Club
122. First 15 minutes
■ You should have an idea
■ You should have a rough sketch
■ You should have thought of what models you need
■ You should think of their relation to each other
■ Pick an pair of models with a 1..* relationship
1
Student * Beer
123. Next 10 minutes
■ You should have a new rails app
$ rails [your_app_name]
■ You should have generated the models
$ ./script/generate scaffold [model_name] [attr]:[type
type = string, text, integer, float, boolean,
date, time, datetime
reserved attrs => type, version
129. App structure
- app
- models the model files
- views
- controllers
- config
- db
- migrate
130. App structure
- app
- models the model files
- views templates for HTML
- controllers
- config
- db
- migrate
131. App structure
- app
- models the model files
- views templates for HTML
- controllers the controller files
- config
- db
- migrate
132. App structure
- app
- models the model files
- views templates for HTML
- controllers the controller files
- config basic configuration
- db
- migrate
133. App structure
- app
- models the model files
- views templates for HTML
- controllers the controller files
- config basic configuration
- db
- migrate database migrations
136. Create a ‘People’
db/migrate/00000000_create_people.rb
table on UP
class CreatePeople < ActiveRecord::Migration
def self.up
create_table :people do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :people
end
end
137. Create a ‘People’
db/migrate/00000000_create_people.rb
With a ‘Name’ table on UP
String class CreatePeople < ActiveRecord::Migration
def self.up
create_table :people do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :people
end
end
138. Create a ‘People’
db/migrate/00000000_create_people.rb
With a ‘Name’ table on UP
String class CreatePeople < ActiveRecord::Migration
def self.up
create_table :people do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :people
And some time-stamps:
end
end
created_at & updated_at
139. Create a ‘People’
db/migrate/00000000_create_people.rb
With a ‘Name’ table on UP
String class CreatePeople < ActiveRecord::Migration
def self.up
create_table :people do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :people
And some time-stamps:
end
end
created_at & updated_at
Drop the
table on
DOWN
141. 1
Student * Beer
app/models/student.rb
class Student < ActiveRecord::Base
has_many :beers
end
app/models/beer.rb
class Beer < ActiveRecord::Base
belongs_to :student
end
142. db/migrate/00000000_create_beers.rb
class CreateBeers < ActiveRecord::Migration
def self.up
create_table :beers do |t|
t.string :brand
t.references :student
t.timestamps
end
end
def self.down
drop_table :beers
end
end
143. db/migrate/00000000_create_beers.rb
Add a reference to Student
(:student_id)
class CreateBeers < ActiveRecord::Migration
def self.up
create_table :beers do |t|
t.string :brand
t.references :student
t.timestamps
end
end
def self.down
drop_table :beers
end
end
144. Next 10 minutes
■ You should update your model files when needed
belongs_to, has_many, has_one, has_many :through
■ You should add references to migrations
t.references :student
145. Next 5 minutes
■ You should have migrated the database
$ rake db:migrate
■ You should have a running server
$ ./script/server
■ You should see your app
$ open http://localhost:3000
147. config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.resources :students
map.resources :beers
# You can have the root of your site routed with map.root
# map.root :controller => quot;welcomequot;
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
148. config/routes.rb
Routes for each resource
ActionController::Routing::Routes.draw do |map|
map.resources :students
map.resources :beers
# You can have the root of your site routed with map.root
# map.root :controller => quot;welcomequot;
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
149. config/routes.rb
Routes for each resource
ActionController::Routing::Routes.draw do |map|
map.resources :students
map.resources :beers
# You can have the root of your site routed with map.root
# map.root :controller => quot;welcomequot;
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
Remove # and
change :controller to ‘students’
150. ADD POST /people
SEE GET /people
CHANGE PUT /people/1
REMOVE DELETE /people/1
SEE GET /people/1
CHANGE GET /people/1/edit
ADD GET /people/new
151. Next 5 minutes
■ Change the routes
Uncomment & choose default controller
■ Remove ‘public/index.html’
$ rm public/index.html
■ Refresh your browser
158. Next 10 minutes
■ You should play with your app, create some instances
We created a student named “Andre”
■ You should start a console
$ ./script/console
■ You should build a few relationships using the console
>> andre = Student.find_by_name(“Andre”)
>> beer = andre.beers.create( :brand => “Grolsch” )
>> beer.student.name
”Andre”
161. Title of the page
app/views/beer/new.html.erb
<h1>New beer</h1>
<% form_for(@beer) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :brand %><br />
<%= f.text_field :brand %>
</p>
<p>
<%= f.submit quot;Createquot; %>
</p>
<% end %>
<%= link_to 'Back', beers_path %>
162. Title of the page
app/views/beer/new.html.erb
A form
<h1>New beer</h1>
<% form_for(@beer) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :brand %><br />
<%= f.text_field :brand %>
</p>
<p>
<%= f.submit quot;Createquot; %>
</p>
<% end %>
<%= link_to 'Back', beers_path %>
163. Title of the page
app/views/beer/new.html.erb
A form
<h1>New beer</h1>
<% form_for(@beer) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :brand %><br />
<%= f.text_field :brand %> Show errors messages if
</p>
<p> something goes wrong
<%= f.submit quot;Createquot; %>
</p>
<% end %>
<%= link_to 'Back', beers_path %>
164. Title of the page
app/views/beer/new.html.erb
A form
<h1>New beer</h1>
<% form_for(@beer) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :brand %><br />
<%= f.text_field :brand %> Show errors messages if
</p>
<p> something goes wrong
<%= f.submit quot;Createquot; %>
Some fields </p>
<% end %>
and a button
<%= link_to 'Back', beers_path %>
165. Title of the page
app/views/beer/new.html.erb
A form
<h1>New beer</h1>
<% form_for(@beer) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :brand %><br />
<%= f.text_field :brand %> Show errors messages if
</p>
<p> something goes wrong
<%= f.submit quot;Createquot; %>
Some fields </p>
<% end %>
and a button
<%= link_to 'Back', beers_path %>
Back link
168. Next 15 minutes
■ Should be able to set a belongs_to relationship
collection_select
■ Relationship must be able to be set on new and
existing objects
Change both the edit and new view!
■ Test that it really works!
Try to edit the show view to represent the object relationship so
another human understands it!
179. spec/models/student_spec.rb
ActiveRecord objects
require File.dirname(__FILE__) + '/../spec_helper'
describe Student do
have a valid? method
it quot;should never have a blank namequot; do
no_name = Student.new( :name => “” )
no_name.should_not be_valid
end
end
180. require File.dirname(__FILE__) + '/../spec_helper'
describe Student do
it quot;should never have a blank namequot; do
no_name = Student.new( :name => “” )
no_name.should_not be_valid
end
end
Terminal: Running tests
181. require File.dirname(__FILE__) + '/../spec_helper'
describe Student do
it quot;should never have a blank namequot; do
no_name = Student.new( :name => “” )
no_name.should_not be_valid
end
end
Terminal: Running tests
$ ruby spec/models/student_spec.rb
F
Failed:
Student should never have a blank name (FAILED)
Finished in 0.1 seconds
1 examples, 1 failures, 0 pending
184. Next 5 minutes
■ Add Rspec to your project
./script/generate rspec
■ Generate specs for your models - don’t replace files!
./script/generate rspec_model [model-name]
■ Clean spec files. Make sure they look like this.
require File.dirname(__FILE__) + '/../spec_helper'
describe Student do
end
■ Build the test database
rake db:migrate RAILS_ENV=test
185. Next 15 minutes
■ Spec out all your validations
■ First, all your specs should fail
■ Add the validations to your models
validates_presence_of
validates_uniqueness_of
validates_format_of
validates_length_of
validates_numericality_of
■ Then, all your specs should pass