ActiveRecord 2.3
-
Upload
reuven-lerner -
Category
Technology
-
view
1.010 -
download
1
description
Transcript of ActiveRecord 2.3
![Page 1: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/1.jpg)
ActiveRecord 2.3Reuven M. Lerner
October 20th, 2010
1Monday, October 25, 2010
![Page 2: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/2.jpg)
What is a database?
Database
Store data confidently
Retrieve data flexibly
2Monday, October 25, 2010
![Page 3: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/3.jpg)
Relational databases
Define tables, store data in them
Database
Retrieve data from related tables
3Monday, October 25, 2010
![Page 4: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/4.jpg)
Database communication
SQL goes hereCREATE TABLE
INSERTUPDATEDELETE
Database
4Monday, October 25, 2010
![Page 5: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/5.jpg)
SQL is great! But...
• It adds a second language to existing Ruby
• It’s a totally different paradigm
• We want to work with Ruby objects!
• Incidentally, SQL-in-something-else was the paradigm for years...
5Monday, October 25, 2010
![Page 6: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/6.jpg)
Solution: ORM (object-relational mapper)
ORMRuby
Database
6Monday, October 25, 2010
![Page 7: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/7.jpg)
Solution: ORM (object-relational mapper)
ORMRuby
Database
Write in Ruby
6Monday, October 25, 2010
![Page 8: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/8.jpg)
Solution: ORM (object-relational mapper)
ORMRuby
Database
Write in Ruby
ORM translates to SQL, sends to database
6Monday, October 25, 2010
![Page 9: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/9.jpg)
Solution: ORM (object-relational mapper)
ORMRuby
Database
Write in Ruby
ORM translates to SQL, sends to database
Results goto ORM
6Monday, October 25, 2010
![Page 10: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/10.jpg)
Solution: ORM (object-relational mapper)
ORMRuby
Database
Write in Ruby
ORM translates to SQL, sends to database
Results goto ORM
ORM turns results into Ruby objects
6Monday, October 25, 2010
![Page 11: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/11.jpg)
ActiveRecord
• By far, the most popular ORM for Ruby
• Not the only one — e.g., DataMapper
• We work with objects whenever possible
• We define as little as possible
• Our objects act as intelligent representations of what’s in the database
7Monday, October 25, 2010
![Page 12: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/12.jpg)
ActiveRecord and Rails
• The idea of ActiveRecord preceded Rails
• Mark Fowler, from Thoughtworks (and the “Refactoring” book)
• You can use ActiveRecord without Rails
• ActiveRecord was written for Rails
• And let’s face it — nearly everyone uses ActiveRecord within Rails applications
8Monday, October 25, 2010
![Page 13: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/13.jpg)
Opinionated!
• ActiveRecord has some ideas about how your application should work
• If you work in the same way, the work is both easy and fun
• If you try to work in a different way, it’ll be very difficult and frustrating
• “Syntactic vinegar”
9Monday, October 25, 2010
![Page 14: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/14.jpg)
Using ActiveRecord
• Typically, we subclass ActiveRecord::Base in each of our model files
• That is, all of the classes defined in app/models/*.rb
• You don’t have to inherit from ActiveRecord::Base, of course! You can even mix and match with different models
10Monday, October 25, 2010
![Page 15: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/15.jpg)
Version warning!
• Everything that I’m about to show is for Rails 2.3.8
• That’s the version we’re using here
• But the latest official release is 3.0, and much online documentation will reflect that
• So when you look online, check the version number!
11Monday, October 25, 2010
![Page 16: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/16.jpg)
Person model
class Person < ActiveRecord::Base
end
12Monday, October 25, 2010
![Page 17: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/17.jpg)
Person model
class Person < ActiveRecord::Base
end
Singular class name
12Monday, October 25, 2010
![Page 18: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/18.jpg)
Person model
class Person < ActiveRecord::Base
end
Singular class name Standard parent class
12Monday, October 25, 2010
![Page 19: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/19.jpg)
We can already start!
~/Downloads/foo$ ./script/console
Loading development environment (Rails 2.3.8)
>> Person
=> Person(Table doesn't exist)
13Monday, October 25, 2010
![Page 20: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/20.jpg)
Object ≠ Table
• The object exists, but the table doesn’t.
• So let’s create the table!
14Monday, October 25, 2010
![Page 21: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/21.jpg)
Non-Rails approach
• Create the table
• Keep the definition in a file
• Tell everyone that you’ve created the table
• When you make changes to the table, update the file and tell everyone again
• Hope that your changes don’t clash!
15Monday, October 25, 2010
![Page 22: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/22.jpg)
Migrations
• Ruby program that describes how to change the database schema
• Add tables
• Rename columns
• Remove columns
• Set defaults
16Monday, October 25, 2010
![Page 23: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/23.jpg)
Platform independent
• Because migrations are written in Ruby, they’re platform independent
• Well, mostly...
• ... they tend to use MySQL ideas and semantics
• No foreign keys, for example
• Works well enough with most databases
17Monday, October 25, 2010
![Page 24: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/24.jpg)
Create a migration
./script/generate migration create_person
./script/generate model person
./script/generate model person first_name:string last_name:string email:string
18Monday, October 25, 2010
![Page 25: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/25.jpg)
The migration file
• Migrations are in db/migrate
• Each has a unique filename, and a timestamp
• (The odds of two developers creating migrations at the same second, and with the same name, are slim)
19Monday, October 25, 2010
![Page 26: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/26.jpg)
class CreatePeople < ActiveRecord::Migration def self.up create_table :people do |t| t.string :first_name t.string :last_name t.string :email
t.timestamps end end
def self.down drop_table :people endend
20Monday, October 25, 2010
![Page 27: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/27.jpg)
class CreatePeople < ActiveRecord::Migration def self.up create_table :people do |t| t.string :first_name t.string :last_name t.string :email
t.timestamps end end
def self.down drop_table :people endend
Migrate forward
20Monday, October 25, 2010
![Page 28: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/28.jpg)
class CreatePeople < ActiveRecord::Migration def self.up create_table :people do |t| t.string :first_name t.string :last_name t.string :email
t.timestamps end end
def self.down drop_table :people endend
Migrate forward
Migrate backward
20Monday, October 25, 2010
![Page 29: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/29.jpg)
class CreatePeople < ActiveRecord::Migration def self.up create_table :people do |t| t.string :first_name t.string :last_name t.string :email
t.timestamps end end
def self.down drop_table :people endend
Migrate forward
Migrate backward
Data types
20Monday, October 25, 2010
![Page 30: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/30.jpg)
class CreatePeople < ActiveRecord::Migration def self.up create_table :people do |t| t.string :first_name t.string :last_name t.string :email
t.timestamps end end
def self.down drop_table :people endend
Migrate forward
Migrate backward
Data types
Block!
20Monday, October 25, 2010
![Page 31: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/31.jpg)
Run the migration
• To run all pending migrations:
rake db:migrate
• To run the “down” method until we get to a certain migration:
rake db:migrate VERSION=20101013095549
21Monday, October 25, 2010
![Page 32: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/32.jpg)
Changing migrations
• Changing the migration file is OK!
• Add indexes, defaults, etc.
• But don’t change a table structure by editing a migration
• Rather, create a new migration that adds/renames/deletes the column
• Migrations are additive (and addictive)
22Monday, October 25, 2010
![Page 33: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/33.jpg)
rake db:migrate
• A table, schema_migrations, is automatically created in the database
• It has one column, “version”, which contains one row for each run migration
• When you run db:migrate, it runs all of the migrations that are not in the table
• This allows for merges between developers
23Monday, October 25, 2010
![Page 34: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/34.jpg)
Migrations and models
• Column names and types are defined in the database, not in the model
• This means that column names and types are set in the migrations
• The easiest way to find out what columns are in an ActiveRecord model class?
• The console, of course!
24Monday, October 25, 2010
![Page 35: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/35.jpg)
Migrating~/Downloads/foo$ rake db:migrate (in /Users/reuven/Downloads/foo)== CreatePeople: migrating ===================================================-- create_table(:people) -> 0.2094s== CreatePeople: migrated (0.2097s) ==========================================
~/Downloads/foo$ rake db:migrate (in /Users/reuven/Downloads/foo)
25Monday, October 25, 2010
![Page 36: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/36.jpg)
Migrating~/Downloads/foo$ rake db:migrate (in /Users/reuven/Downloads/foo)== CreatePeople: migrating ===================================================-- create_table(:people) -> 0.2094s== CreatePeople: migrated (0.2097s) ==========================================
~/Downloads/foo$ rake db:migrate (in /Users/reuven/Downloads/foo)
We’re up to date, so nothing happens
25Monday, October 25, 2010
![Page 37: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/37.jpg)
Let’s check again
>> Person
=> Person(id: integer, first_name: string, last_name: string, email: string, created_at: datetime, updated_at: datetime)
26Monday, October 25, 2010
![Page 38: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/38.jpg)
Let’s check again
>> Person
=> Person(id: integer, first_name: string, last_name: string, email: string, created_at: datetime, updated_at: datetime)
Hey, where did “id”come from?
26Monday, October 25, 2010
![Page 39: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/39.jpg)
Let’s check again
>> Person
=> Person(id: integer, first_name: string, last_name: string, email: string, created_at: datetime, updated_at: datetime)
Hey, where did “id”come from?
And whatabout these?
26Monday, October 25, 2010
![Page 40: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/40.jpg)
Assumptions
• Convention over configuration!
• Tables are plural, classes are singular
• class “Person”, but table “People”
• Primary key is always called “id”
• created_at, updated_at are set automatically by ActiveRecord upon creation or update to the record
27Monday, October 25, 2010
![Page 41: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/41.jpg)
Wait! Where’s the DB?
• When did we tell Rails how to connect to the database?
• Look in config/database.yml
• The only configuration you need
• It tells ActiveRecord what database you have, and how to connect...
• ... for each environment
28Monday, October 25, 2010
![Page 42: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/42.jpg)
development: adapter: postgresql encoding: unicode database: foo_development pool: 5 username: reuven password: reuven
29Monday, October 25, 2010
![Page 43: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/43.jpg)
Wait, that’s it?
• Well, mostly.
• There are additional (optimal) parameters
• And some configuration is done in the environment config files
• We’ll ignore these for now
30Monday, October 25, 2010
![Page 44: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/44.jpg)
Console reloading
• If you use the console (and you should!) then modifying ActiveRecord models may cause issues
• Use “reload!” to reload the environment
• You’ll then need to re-create all objects
• Better than having invalid objects...
31Monday, October 25, 2010
![Page 45: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/45.jpg)
How many records?
?> Person.count
=> 0
32Monday, October 25, 2010
![Page 46: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/46.jpg)
OK, we’ll add one>> p = Person.new
=> #<Person id: nil, first_name: nil, last_name: nil, email: nil, created_at: nil, updated_at: nil>
>> p.save!
=> true
>> Person.count
=> 1
33Monday, October 25, 2010
![Page 47: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/47.jpg)
Wait a second!
• That person record we just created is pretty useless.
• We really don’t want nameless people in our database.
• We could (and should!) update the database definition with a new migration
• But we’ll ignore that for now. Don’t tell!
34Monday, October 25, 2010
![Page 48: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/48.jpg)
Better creation
• The “new” method creates a new object, but doesn’t save it to the database
• This is why it has nil for an ID
• After you save, it has an ID
• To create an object and save it right away, use the “create” method instead
• Both “new” and “create” return the object
35Monday, October 25, 2010
![Page 49: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/49.jpg)
save! and create!
• save returns true or false
• create returns the object or false
• save! and create! are the same as their “quiet” counterparts upon success
• But raise an exception if there is a problem
36Monday, October 25, 2010
![Page 50: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/50.jpg)
Missing attributes?
• If you fail to set an attribute, then Ruby will pass it nil
• However, if you have a default value set in the database, then it’ll get that
• Don’t set created_at and updated_at; those are set automatically
37Monday, October 25, 2010
![Page 51: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/51.jpg)
The Java way>> p = Person.new => #<Person id: nil, first_name: nil, last_name: nil, email: nil, created_at: nil, updated_at: nil>>> p.first_name = 'Reuven' => "Reuven">> p.last_name = 'Lerner' => "Lerner">> p.email = '[email protected]' => "[email protected]">> p.save! => true
38Monday, October 25, 2010
![Page 52: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/52.jpg)
The Ruby way
>> p = Person.new(:first_name => 'Reuven', :last_name => 'Lerner', :email => '[email protected]')
=> #<Person id: nil, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: nil, updated_at: nil>
39Monday, October 25, 2010
![Page 53: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/53.jpg)
The Ruby way
>> p = Person.new(:first_name => 'Reuven', :last_name => 'Lerner', :email => '[email protected]')
=> #<Person id: nil, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: nil, updated_at: nil>
Hash of name-value pairs
39Monday, October 25, 2010
![Page 54: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/54.jpg)
It’s not a free-for-all>> p10 = Person.new(:eye_color => 'brown')
ActiveRecord::UnknownAttributeError: unknown attribute: eye_color
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2906:in `assign_attributes'
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2902:in `each'
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2902:in `assign_attributes'
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2775:in `attributes='
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2473:in `initialize'
from (irb):44:in `new'
from (irb):44
>>
40Monday, October 25, 2010
![Page 55: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/55.jpg)
It’s not a free-for-all>> p10 = Person.new(:eye_color => 'brown')
ActiveRecord::UnknownAttributeError: unknown attribute: eye_color
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2906:in `assign_attributes'
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2902:in `each'
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2902:in `assign_attributes'
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2775:in `attributes='
from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2473:in `initialize'
from (irb):44:in `new'
from (irb):44
>>
40Monday, October 25, 2010
![Page 56: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/56.jpg)
Updating fields
>> p.first_name = 'Bibi'
=> "Bibi"
>> p.save
=> true
41Monday, October 25, 2010
![Page 57: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/57.jpg)
Updating fields
>> p.first_name = 'Bibi'
=> "Bibi"
>> p.save
=> true
If you don’t save the object,then you haven’t changed it
in the database!
41Monday, October 25, 2010
![Page 58: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/58.jpg)
update_attributes
• It’s easier and safer to update both the object and the database simultaneously
>> p.update_attributes(
:first_name => 'Bibi')
=> true
42Monday, October 25, 2010
![Page 59: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/59.jpg)
Multiple attributes
• p.update_attributes(
:first_name => 'Bibi',
:last_name => 'Netanyahu')
43Monday, October 25, 2010
![Page 60: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/60.jpg)
Avoid this!
• update_attribute
• singular (not plural)
• takes two params (attribute, value), rather than a hash
• doesn’t go through any Rails validators!
• From my perspective, this method is dangerous, and should be avoided
44Monday, October 25, 2010
![Page 61: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/61.jpg)
By the way...
• Remember our model file?
• It’s still empty.
• And yet, it allows us to create, save, and update models naturally and easily.
• Pretty cool, eh?
45Monday, October 25, 2010
![Page 62: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/62.jpg)
Semi-protection
• attr_protected :first_name
• first_name cannot be changed with update_attributes, but it can be updated with a setter or update_attribute
• attr_accessible: Lists those attributes that are not protected
attr_accessible :email, :zip_code
46Monday, October 25, 2010
![Page 63: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/63.jpg)
find
• This is the workhorse of ActiveRecord
• The “find” method is really a lot of different methods with a single interface
47Monday, October 25, 2010
![Page 64: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/64.jpg)
find by ID
Person.find(3)
• If there is a Person object with ID = 3, that one object is returned
• If no object exists, an exception is raised
• Yes, this is annoying
Person.find(2, 6) # returns array
48Monday, October 25, 2010
![Page 65: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/65.jpg)
Get them all!
Person.find(:all)
Person.all # same thing
49Monday, October 25, 2010
![Page 66: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/66.jpg)
One object or many?
• Simple find with an ID — one object (or raises an exception)
• find with multiple IDs — returns an array of objects, or an exception if even one ID doesn’t exist
• all — always returns an array, and perhaps even an empty array
50Monday, October 25, 2010
![Page 67: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/67.jpg)
Conditions
• We can add conditions
• turned into WHERE clause in SQL
• You’ll almost always want conditions
51Monday, October 25, 2010
![Page 68: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/68.jpg)
Conditions, Ruby style>> Person.all(:conditions => {:first_name => 'foo'})=> []>> Person.all(:conditions => {:first_name => 'Reuven'})=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
52Monday, October 25, 2010
![Page 69: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/69.jpg)
Conditions, Ruby style>> Person.all(:conditions => {:first_name => 'foo'})=> []>> Person.all(:conditions => {:first_name => 'Reuven'})=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
Hash
52Monday, October 25, 2010
![Page 70: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/70.jpg)
Conditions, SQL style
>> Person.all(:conditions => "first_name = 'Reuven'")
=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
53Monday, October 25, 2010
![Page 71: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/71.jpg)
Conditions, SQL style
>> Person.all(:conditions => "first_name = 'Reuven'")
=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
String
53Monday, October 25, 2010
![Page 72: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/72.jpg)
Conditions, SQL style
>> Person.all(:conditions => "first_name = '#{@person.first_name}'")
=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
54Monday, October 25, 2010
![Page 73: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/73.jpg)
Conditions, SQL style
>> Person.all(:conditions => "first_name = '#{@person.first_name}'")
=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
Variable
54Monday, October 25, 2010
![Page 74: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/74.jpg)
Don’t do this!
• SQL injection attacks can happen
• There’s no reason for it
• If someone hands you a string containing a quote mark and then some SQL, it could be executed if you’re not careful
• Injection attacks should no longer occur!
• (This was true as far back as 1996...)
55Monday, October 25, 2010
![Page 75: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/75.jpg)
XKCD
56Monday, October 25, 2010
![Page 76: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/76.jpg)
Sweden, last month
57Monday, October 25, 2010
![Page 77: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/77.jpg)
Interpolating parameters
• Instead of:>> Person.all(:conditions => "first_name = '#{@person.first_name}'")
• Use:>> Person.all(:conditions => ["first_name = ?", @person.first_name])
58Monday, October 25, 2010
![Page 78: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/78.jpg)
Interpolating parameters
• Instead of:>> Person.all(:conditions => "first_name = '#{@person.first_name}'")
• Use:>> Person.all(:conditions => ["first_name = ?", @person.first_name])
Array of strings
58Monday, October 25, 2010
![Page 79: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/79.jpg)
Interpolating parameters
• Instead of:>> Person.all(:conditions => "first_name = '#{@person.first_name}'")
• Use:>> Person.all(:conditions => ["first_name = ?", @person.first_name]) Question mark
Array of strings
58Monday, October 25, 2010
![Page 80: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/80.jpg)
Interpolating parameters
• Instead of:>> Person.all(:conditions => "first_name = '#{@person.first_name}'")
• Use:>> Person.all(:conditions => ["first_name = ?", @person.first_name]) Question mark
No quotes!
Array of strings
58Monday, October 25, 2010
![Page 81: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/81.jpg)
Ordering results
• Remember: A relational database doesn’t store its rows in any order
• If you don’t specify an order, you will almost certainly be surprised
59Monday, October 25, 2010
![Page 82: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/82.jpg)
Ascending order>> Person.all(:conditions => "first_name = 'Reuven'", :order => 'created_at')
=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
60Monday, October 25, 2010
![Page 83: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/83.jpg)
Descending order>> Person.all(:conditions => "first_name = 'Reuven'", :order => 'created_at DESC')
=> [#<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">, #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">]
61Monday, October 25, 2010
![Page 84: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/84.jpg)
Combining
>> Person.all(:order => 'last_name ASC, created_at DESC')
=> [#<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">, #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, ...
62Monday, October 25, 2010
![Page 85: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/85.jpg)
Where to order?
• The database is almost certainly faster at ordering
• So invoking #all and then #sort is probably not a good idea
• In fact, the database is generally faster at filtering, too — so conditions are better than #all and #select
63Monday, October 25, 2010
![Page 86: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/86.jpg)
first
• Returns the first row (object) from the database — or nil, if none was found
Person.first
• Of course, without an order, you don’t know which row you’ll get!
Person.first(:order => 'created_at')
64Monday, October 25, 2010
![Page 87: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/87.jpg)
Transforming results
• Person.all returns an array — so you can invoke whatever you want on that array!
• Get an array of last names:
Person.all.map {|p| p.last_name}
65Monday, October 25, 2010
![Page 88: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/88.jpg)
Iterate over results
Person.all.each {|p| puts p.inspect}
Person.all.each {|p| p.update_attributes(:admin => false)}
66Monday, October 25, 2010
![Page 89: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/89.jpg)
Dynamic finders
• Remember method_missing? ActiveRecord uses this to provide “dynamic finders” — versions of find that can make our code more readable
• If you have a row named xxx, you can say find_by_xxx or find_all_by_xxx
67Monday, October 25, 2010
![Page 90: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/90.jpg)
find_by_first_name>> Person.find_by_first_name('Reuven')=> #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">
>> Person.find_all_by_first_name('Reuven')=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
68Monday, October 25, 2010
![Page 91: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/91.jpg)
find_by_first_name>> Person.find_by_first_name('Reuven')=> #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">
>> Person.find_all_by_first_name('Reuven')=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
Many find thiseasier to read
68Monday, October 25, 2010
![Page 92: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/92.jpg)
find_by_first_name>> Person.find_by_first_name('Reuven')=> #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">
>> Person.find_all_by_first_name('Reuven')=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">, #<Person id: 7, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:04:09", updated_at: "2010-10-14 06:32:19">]
Many find thiseasier to read
Add “all” toget an array
68Monday, October 25, 2010
![Page 93: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/93.jpg)
Negative results
>> Person.find_by_first_name('blah')
=> nil
>> Person.find_all_by_first_name('blah')
=> []
69Monday, October 25, 2010
![Page 94: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/94.jpg)
Negative results
>> Person.find_by_first_name('blah')
=> nil
>> Person.find_all_by_first_name('blah')
=> []
No exception!
69Monday, October 25, 2010
![Page 95: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/95.jpg)
Multiple attributes
>> Person.find_by_first_name_and_last_name('Reuven', 'Lerner')
=> #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">
70Monday, October 25, 2010
![Page 96: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/96.jpg)
find_or_create_by_...
• Use a dynamic finder... and if you don’t find a result, then create a new object
• If the object fails validations, return a new (unsaved) object
Person.find_or_create_by_first_name('Reuven');
Person.find_or_create_by_first_name('Reuven', last_name => 'Lerner');
71Monday, October 25, 2010
![Page 97: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/97.jpg)
Caching
• ActiveRecord caches results on a per-session basis
• So if you have already retrieved an object with the current request, it’ll be cached for further retrievals
• This doesn’t happen across requests, though
72Monday, October 25, 2010
![Page 98: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/98.jpg)
Look in the log!
• In the development environment, you’ll see your queries rewritten using SQL.
• This is a great way to see what is happening in the underlying database
73Monday, October 25, 2010
![Page 99: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/99.jpg)
Associations
• ActiveRecord really shines when it comes to “associations”
• The object equivalent of primary/foreign keys connecting database tables
74Monday, October 25, 2010
![Page 100: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/100.jpg)
Pets!
• Let’s make it possible for people to have pets
./script/generate model pet animal_type:string name:string person_id:integer
rake db:migrate
75Monday, October 25, 2010
![Page 101: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/101.jpg)
Pets!
• Let’s make it possible for people to have pets
./script/generate model pet animal_type:string name:string person_id:integer
rake db:migrateEach pet belongs to one person
75Monday, October 25, 2010
![Page 102: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/102.jpg)
belongs_to
• Declaration (aka a class method) in the model file
• Meaning: There is a foreign key pointing from self to another object, via its ID
• The name of the foreign key is (by default) the other object’s name (singular) with _id
76Monday, October 25, 2010
![Page 103: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/103.jpg)
Change Pet.rb
class Pet < ActiveRecord::Base
belongs_to :person
end
77Monday, October 25, 2010
![Page 104: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/104.jpg)
What does this do?
• Doesn’t create the foreign key in the DB
• Doesn’t set the foreign key
• Doesn’t enforce anything
• It does, however, define a bunch of methods that we can now use on a pet
78Monday, October 25, 2010
![Page 105: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/105.jpg)
Creating a pet>> spot = Pet.new(:animal_type => 'dog', :name => 'Spot', :person_id => Person.first.id)=> #<Pet id: nil, animal_type: "dog", name: "Spot", person_id: 6, created_at: nil, updated_at: nil>>> spot = Pet.new(:animal_type => 'dog', :name => 'Spot', :person => Person.first)=> #<Pet id: nil, animal_type: "dog", name: "Spot", person_id: 6, created_at: nil, updated_at: nil>
79Monday, October 25, 2010
![Page 106: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/106.jpg)
Creating a pet>> spot = Pet.new(:animal_type => 'dog', :name => 'Spot', :person_id => Person.first.id)=> #<Pet id: nil, animal_type: "dog", name: "Spot", person_id: 6, created_at: nil, updated_at: nil>>> spot = Pet.new(:animal_type => 'dog', :name => 'Spot', :person => Person.first)=> #<Pet id: nil, animal_type: "dog", name: "Spot", person_id: 6, created_at: nil, updated_at: nil>
Here we use the ID
79Monday, October 25, 2010
![Page 107: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/107.jpg)
Creating a pet>> spot = Pet.new(:animal_type => 'dog', :name => 'Spot', :person_id => Person.first.id)=> #<Pet id: nil, animal_type: "dog", name: "Spot", person_id: 6, created_at: nil, updated_at: nil>>> spot = Pet.new(:animal_type => 'dog', :name => 'Spot', :person => Person.first)=> #<Pet id: nil, animal_type: "dog", name: "Spot", person_id: 6, created_at: nil, updated_at: nil>
Here we use the ID
Here we use the object
79Monday, October 25, 2010
![Page 108: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/108.jpg)
New “person” method!
>> spot.person
=> #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">
80Monday, October 25, 2010
![Page 109: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/109.jpg)
Pet e-mail (p-mail?)
• Pets use their owner’s e-mail address
• One way is to define a new method on Pet.rb
• Every instance of a pet will now respond to the “email” method, and return the owner’s e-mail address
81Monday, October 25, 2010
![Page 110: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/110.jpg)
With our email method
class Pet < ActiveRecord::Base
belongs_to :person
def email
person.email
end
end
82Monday, October 25, 2010
![Page 111: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/111.jpg)
Easier: Delegation!
class Pet < ActiveRecord::Base
belongs_to :person
delegate :email, :to => :person
end
83Monday, October 25, 2010
![Page 112: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/112.jpg)
By the way...>> rover = Pet.new
=> #<Pet id: nil, animal_type: nil, name: nil, person_id: nil, created_at: nil, updated_at: nil>
>> rover.email
RuntimeError: email delegated to person.email, but person is nil: #<Pet id: nil, animal_type: nil, name: nil, person_id: nil, created_at: nil, updated_at: nil>
84Monday, October 25, 2010
![Page 113: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/113.jpg)
By the way...>> rover = Pet.new
=> #<Pet id: nil, animal_type: nil, name: nil, person_id: nil, created_at: nil, updated_at: nil>
>> rover.email
RuntimeError: email delegated to person.email, but person is nil: #<Pet id: nil, animal_type: nil, name: nil, person_id: nil, created_at: nil, updated_at: nil>We can’t delegate to nil!
84Monday, October 25, 2010
![Page 114: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/114.jpg)
Avoid nil problems
class Pet < ActiveRecord::Base
belongs_to :person
delegate :email, :to => :person,
:allow_nil => true
end
85Monday, October 25, 2010
![Page 115: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/115.jpg)
Problem solved
>> rover = Pet.new
=> #<Pet id: nil, animal_type: nil, name: nil, person_id: nil, created_at: nil, updated_at: nil>
>> rover.email
=> nil
86Monday, October 25, 2010
![Page 116: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/116.jpg)
The other side
• So far, pets know about their owners...
• ... but owners don’t know about their pets>> spot.person.pets
NoMethodError: undefined method `pets' for #<ActiveRecord::Associations::BelongsToAssociation:0x1089bba50>
87Monday, October 25, 2010
![Page 117: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/117.jpg)
one-to-one: has_one
If each person can have one pet, then we could change person.rb to read:
class Person < ActiveRecord::Base
has_one :pet
end
88Monday, October 25, 2010
![Page 118: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/118.jpg)
Using has_one>> p = Person.first
=> #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">
>> p.pet
=> #<Pet id: 1, animal_type: "dog", name: "Spot", person_id: 6, created_at: "2010-10-14 07:43:59", updated_at: "2010-10-14 07:43:59">
89Monday, October 25, 2010
![Page 119: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/119.jpg)
Using has_one>> p = Person.first
=> #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">
>> p.pet
=> #<Pet id: 1, animal_type: "dog", name: "Spot", person_id: 6, created_at: "2010-10-14 07:43:59", updated_at: "2010-10-14 07:43:59">
Each person has a pet
89Monday, October 25, 2010
![Page 120: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/120.jpg)
How does Rails do it?
• It does what we would do manually — looks for all pets with our primary key value
Pet Load (1.2ms) SELECT * FROM "pets" WHERE ("pets".person_id = 6) LIMIT 1
90Monday, October 25, 2010
![Page 121: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/121.jpg)
has_many
• More interesting, and trickier, is has_many
• Perhaps we have many pets!
class Person < ActiveRecord::Base
has_many :pets
end
91Monday, October 25, 2010
![Page 122: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/122.jpg)
has_many
• More interesting, and trickier, is has_many
• Perhaps we have many pets!
class Person < ActiveRecord::Base
has_many :pets
end Notice plural!
91Monday, October 25, 2010
![Page 123: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/123.jpg)
has_many
• With a has_many relationship in place, we get a method (plural!) for pets
• It always returns an array (perhaps empty)
>> p.pets
=> [#<Pet id: 1, animal_type: "dog", name: "Spot", person_id: 6, created_at: "2010-10-14 07:43:59", updated_at: "2010-10-14 07:43:59">]
92Monday, October 25, 2010
![Page 124: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/124.jpg)
Adding
>> p.pets => []>> p.pets << Pet.new(:animal_type => 'fish', :name => "Charlie") => [#<Pet id: 2, animal_type: "fish", name: "Charlie", person_id: 7, created_at: "2010-10-14 09:21:41", updated_at: "2010-10-14 09:21:41">]>> Pet.count => 2
93Monday, October 25, 2010
![Page 125: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/125.jpg)
Adding
>> p.pets => []>> p.pets << Pet.new(:animal_type => 'fish', :name => "Charlie") => [#<Pet id: 2, animal_type: "fish", name: "Charlie", person_id: 7, created_at: "2010-10-14 09:21:41", updated_at: "2010-10-14 09:21:41">]>> Pet.count => 2
Even though we used “new”, the object was saved
93Monday, October 25, 2010
![Page 126: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/126.jpg)
Adding
>> p.pets => []>> p.pets << Pet.new(:animal_type => 'fish', :name => "Charlie") => [#<Pet id: 2, animal_type: "fish", name: "Charlie", person_id: 7, created_at: "2010-10-14 09:21:41", updated_at: "2010-10-14 09:21:41">]>> Pet.count => 2
Even though we used “new”, the object was saved
Automatically used our person
93Monday, October 25, 2010
![Page 127: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/127.jpg)
Array fun
>> Person.first.pets.select {|p| p.animal_type == 'fish'}
=> []
>> Person.first.pets.select {|p| p.animal_type == 'dog'}
=> [#<Pet id: 1, animal_type: "dog", name: "Spot", person_id: 6, created_at: "2010-10-14 07:43:59", updated_at: "2010-10-14 07:43:59">]
94Monday, October 25, 2010
![Page 128: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/128.jpg)
many-to-many
• What if each person can have multiple pets, and each pet can have multiple owners?
• For that, we need a “join” table
95Monday, October 25, 2010
![Page 129: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/129.jpg)
Join table
People Person-Pets
Pets
foreign keys:person_id
pet_id
96Monday, October 25, 2010
![Page 130: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/130.jpg)
Migration
./script/generate model person_pet person_id:integer pet_id:integer
97Monday, October 25, 2010
![Page 131: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/131.jpg)
person_pet.rb
class PersonPet < ActiveRecord::Base
belongs_to :person
belongs_to :pet
end
98Monday, October 25, 2010
![Page 132: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/132.jpg)
Update person.rb
class Person < ActiveRecord::Base
has_many :person_pets
has_many :pets, :through => :person_pets
end
99Monday, October 25, 2010
![Page 133: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/133.jpg)
Update person.rb
class Person < ActiveRecord::Base
has_many :person_pets
has_many :pets, :through => :person_pets
end has_many :throughconnects our models
via the join table
99Monday, October 25, 2010
![Page 134: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/134.jpg)
Update pet.rb
class Pet < ActiveRecord::Base
has_many :person_pets
has_many :people, :through => :person_pets
end
100Monday, October 25, 2010
![Page 135: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/135.jpg)
Now it all works!
>> spot.people=> []>> spot.people << Person.first=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">]>> spot.people=> [#<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">]
101Monday, October 25, 2010
![Page 136: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/136.jpg)
From the other side...
>> Person.first.pets=> [#<Pet id: 1, animal_type: "dog", name: "Spot", person_id: 6, created_at: "2010-10-14 07:43:59", updated_at: "2010-10-14 07:43:59">]
102Monday, October 25, 2010
![Page 137: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/137.jpg)
Join model
• It’s a full ActiveRecord model
• You can hang other attributes on it, if you want
• However, it’s often there just for use as a connection, with no day-to-day direct use
103Monday, October 25, 2010
![Page 138: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/138.jpg)
Association options
• has_many, belongs_to, and has_one all take a bunch of options
• Some of them are to handle ActiveRecord naming conventions
• Others can really help to shrink your code, making your models more powerful and expressive
104Monday, October 25, 2010
![Page 139: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/139.jpg)
Example: Order
• Perhaps you always want to list pets in the order that they were created:
has_many :pets, :order => 'created_at'
• Person.first.pets will get the pets in order
• This is what we mean by pushing logic from the controller into the model
105Monday, October 25, 2010
![Page 140: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/140.jpg)
Example: Auto-destroy
class Person < ActiveRecord::Base
has_many :person_pets, :dependent => :destroy
has_many :pets, :through => :person_pets
end
106Monday, October 25, 2010
![Page 141: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/141.jpg)
Example: Auto-destroy
class Person < ActiveRecord::Base
has_many :person_pets, :dependent => :destroy
has_many :pets, :through => :person_pets
end
When we delete a person, we’ll also destroy the join
model person_pet
106Monday, October 25, 2010
![Page 142: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/142.jpg)
delete vs. destroy
• There are two ways to destroy an object
p.destroy
p.delete
• Both delete the row in the database
• Both freeze the object, so that we cannot change it
107Monday, October 25, 2010
![Page 143: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/143.jpg)
delete vs. destroy
• But:
• destroy runs before_destroy and after_destroy callbacks
• destroy handles dependent association options (i.e., you can set it such that dependent objects are deleted)
• So... use destroy, and not delete, OK?
108Monday, October 25, 2010
![Page 144: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/144.jpg)
Validations
• “Validations” are the ActiveRecord way to ensure that your data is valid
• You can get around them!
• So these shouldn’t come in place of constraints and checks in the database
• When you save or update a model, the validations are checked and must pass
109Monday, October 25, 2010
![Page 145: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/145.jpg)
Built-in validations
• Rails comes with a large number of validations
• declarations (i.e., class methods) put into the ActiveRecord class
• Use as many of these as you want
110Monday, October 25, 2010
![Page 146: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/146.jpg)
validates_presence_of
• Let’s ensure that every person has first and last names:
class Person < ActiveRecord::Base
validates_presence_of :first_name
validates_presence_of :last_name
end
111Monday, October 25, 2010
![Page 147: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/147.jpg)
Or, on a single line
class Person < ActiveRecord::Base
validates_presence_of :first_name, :last_name
end
• I prefer the multi-line version, for easier adding and removing of validations
112Monday, October 25, 2010
![Page 148: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/148.jpg)
So, what now?
>> p = Person.new
=> #<Person id: nil, first_name: nil, last_name: nil, email: nil, created_at: nil, updated_at: nil>
>> p.save!
ActiveRecord::RecordInvalid: Validation failed: First name can't be blank, Last name can't be blank
113Monday, October 25, 2010
![Page 149: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/149.jpg)
So, what now?
>> p = Person.new
=> #<Person id: nil, first_name: nil, last_name: nil, email: nil, created_at: nil, updated_at: nil>
>> p.save!
ActiveRecord::RecordInvalid: Validation failed: First name can't be blank, Last name can't be blank
Each violationis listed
113Monday, October 25, 2010
![Page 150: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/150.jpg)
What errors occurred?
>> p.errors
=> #<ActiveRecord::Errors:0x1085d1020 @base=#<Person id: nil, first_name: nil, last_name: nil, email: nil, created_at: nil, updated_at: nil>, @errors=#<OrderedHash {"last_name"=>[#<ActiveRecord::Error:0x1085a2c70 @options={:default=>nil}, @base=#<Person id: nil, first_name: nil, last_name: nil, email: nil, created_at: nil, updated_at: nil>, @type=:blank, @message=:blank, @attribute=:last_name>], "first_name"=>[#<ActiveRecord::Error:0x1085a3170 @options={:default=>nil}, @base=#<Person id: nil, first_name: nil, last_name: nil, email: nil, created_at: nil, updated_at: nil>, @type=:blank, @message=:blank, @attribute=:first_name>]}>>
114Monday, October 25, 2010
![Page 151: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/151.jpg)
Let’s try that again...
>> p.errors.class
=> ActiveRecord::Errors
>> p.errors.each_error {|attr, error| puts "[#{attr}] #{error}"}
[first_name] can't be blank
[last_name] can't be blank
=> ["first_name", "last_name"]
115Monday, October 25, 2010
![Page 152: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/152.jpg)
ActiveRecord::Errors
• When a validation fails, it adds an element to #errors — an enumerable instance of ActiveRecord::Errors
• You could also call it the “which validations failed, and why” array
• If #errors.empty? is true, then the save/update takes place
116Monday, October 25, 2010
![Page 153: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/153.jpg)
Built-in validations
• validates_acceptance_of
• The attribute must exist (e.g., a checkbox indicating user acceptance of site rules)
• validates_associated
• The object to which we’re connect via an association must also be valid
117Monday, October 25, 2010
![Page 154: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/154.jpg)
Built-in validations
• validates_confirmation_of
• Did PARAM equal PARAM_confirmation? (Think of password confirmation...)
• validates_each
• Takes a block, and validates each named attribute against the block
118Monday, October 25, 2010
![Page 155: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/155.jpg)
Built-in validations
• validates inclusion of
• validates_exclusion_of
• The attribute must (or may not) be a member of a particular array
• validates_format_of
• The attribute must match a regular expression to be valid
119Monday, October 25, 2010
![Page 156: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/156.jpg)
Built-in validations
• validates_length_of / validates_size_of
• The attribute may be no more (and/or no less) than a specified length
• validates_numericality_of
• The attribute must be a valid number
• validates_uniqueness_of
120Monday, October 25, 2010
![Page 157: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/157.jpg)
Validator options
• Many validators can take options
• For example:
validates_numericality_of :age, :only_integer => true, :greater_than => 0, :less_than_or_equal_to => 120
121Monday, October 25, 2010
![Page 158: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/158.jpg)
Messages
• Each validation has a default message
• We saw those messages when looking at the errors object
• Every validation lets you customize the message with the :message parameter
validates_presence_of :last_name, :message => "What, you think you're Madonna?"
122Monday, October 25, 2010
![Page 159: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/159.jpg)
Checking validity
• The #valid? method returns true or false
• It also sets the errors object
>> q.valid?
=> false
>> q.errors
=> #<ActiveRecord::Errors:0x1089712e8 @base=#<Person id: nil, first_ ...
123Monday, October 25, 2010
![Page 160: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/160.jpg)
Custom validators
• Sometimes, you need to validate in a particular way
• The easiest way is to define a new method in the model class
• If the error exists, invoke errors.add_to_base, with a string containing the message
124Monday, October 25, 2010
![Page 161: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/161.jpg)
Custom validator
validate :last_name_must_be_lerner
def last_name_must_be_lerner
errors.add_to_base("Sorry, but your last name must be 'Lerner'") unless last_name.downcase == 'lerner'
end
125Monday, October 25, 2010
![Page 162: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/162.jpg)
Testing our validator>> p = Person.new(:first_name => 'Reuven', :last_name => 'Lerner') => #<Person id: nil, first_name: "Reuven", last_name: "Lerner", email: nil, created_at: nil, updated_at: nil>
>> p.valid? => true>> p.last_name = 'Smith' => "Smith">> p.valid? => false
126Monday, October 25, 2010
![Page 163: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/163.jpg)
Use validations!
• They’re not database-level constraints, but they can be extremely flexible and powerful
• The built-in validators have a lot of options
• Use them!
• Only write a custom validator if you really need to do so
127Monday, October 25, 2010
![Page 164: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/164.jpg)
Callbacks
• Validations fire automatically when we save or update our model. How?
• Answer: They’re a form of “callback,” a method that is invoked automatically when something happens
• ActiveRecord offers many “hooks” that let you define callbacks
128Monday, October 25, 2010
![Page 165: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/165.jpg)
Uses for callbacks
• Update a counter, or total column (and avoid doing so in the controller)
• Encrypt user passwords
• Write to an audit trail about changes to a particular model
129Monday, October 25, 2010
![Page 166: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/166.jpg)
When callbacks can run
before_validation
before_validation_on_create / ...on_update
after_validation
after_validation_on_create / ...on_update
before_save
before_create / before_update
after_create / after_update
after_save
130Monday, October 25, 2010
![Page 167: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/167.jpg)
Downcase e-mail
>> p = Person.new(:first_name => 'Reuven', :last_name => 'Lerner', :email => '[email protected]') => #<Person id: nil, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: nil, updated_at: nil> >> p.save => true jruby-1.5.3 > p => #<Person id: 12, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-25 17:24:19", updated_at: "2010-10-25 17:24:19">
131Monday, October 25, 2010
![Page 168: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/168.jpg)
How did we do that?
before_save :downcase_email
def downcase_email
email.downcase!
end
132Monday, October 25, 2010
![Page 169: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/169.jpg)
How did we do that?
before_save :downcase_email
def downcase_email
email.downcase!
end
Declare the callback
132Monday, October 25, 2010
![Page 170: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/170.jpg)
How did we do that?
before_save :downcase_email
def downcase_email
email.downcase!
end
Declare the callback
Define the callback method
132Monday, October 25, 2010
![Page 171: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/171.jpg)
Declaring callbacks
• Don’t invoke them!
• They’re invoked automatically
• Don’t define them!
• Redefining them will have weird effects
• Class methods, not instance methods
• Executed in order of definition
133Monday, October 25, 2010
![Page 172: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/172.jpg)
Multiple callbacks
• You can have as many callbacks as you want
• You can run more than one callback on a given hook
• You can run more than one callback on a given attribute
134Monday, October 25, 2010
![Page 173: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/173.jpg)
Good uses of callbacks
• Automatic transformations
• Automatic calculations (e.g., price totals)
• Logging
• Creation of behind-the-scenes objects
• Actions that should occur when an object is saved or updated
135Monday, October 25, 2010
![Page 174: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/174.jpg)
Bad uses of callbacks
• Additional validations
• Use a validator instead! (Which is a form of callback, after all)
• Handle session-related items
• Remember the M-V-C separation
136Monday, October 25, 2010
![Page 175: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/175.jpg)
Return values from callbacks
• Normally, callbacks don’t return values
• But if you return false:
• In a before_* callback, all later callbacks and the action are cancelled!
• In an after_* callback, all later callbacks are cancelled
137Monday, October 25, 2010
![Page 176: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/176.jpg)
Oh, yeah
• Don’t call “save” or “update_attribute” inside of a callback.
• It’ll really hurt. A lot.
138Monday, October 25, 2010
![Page 177: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/177.jpg)
Looking at callbacks
• FYI, the callback on a model are stored in a “callback chain” object
• You can get at it with
Person.before_save_callback_chain
• Better yet:
Person.before_save_callback_chain.each {|c| puts c.method}; nil
139Monday, October 25, 2010
![Page 178: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/178.jpg)
Observers
• We won’t go into this today
• Each AR object can have an observer
• Observer method names are the same as callbacks (after_save, etc.)
• So what’s the difference?
• Semantic — in/out of the model
140Monday, October 25, 2010
![Page 179: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/179.jpg)
Dirty objects>> p = Person.first
=> #<Person id: 6, first_name: "Reuven", last_name: "Lerner", email: "[email protected]", created_at: "2010-10-13 18:01:03", updated_at: "2010-10-13 18:01:03">
>> p.first_name = 'Bibi'
=> "Bibi"
>> p.changed?
=> true
>> p.changed
=> ["first_name"]
141Monday, October 25, 2010
![Page 180: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/180.jpg)
More with dirty objects
>> p.changes=> {"first_name"=>["Reuven", "Bibi"]}
>> p.first_name_changed? => true
>> p.first_name_was => "Reuven"
142Monday, October 25, 2010
![Page 181: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/181.jpg)
More with dirty objects
>> p.changes=> {"first_name"=>["Reuven", "Bibi"]}
>> p.first_name_changed? => true
>> p.first_name_was => "Reuven"
Each changed attribute, with old and new values
142Monday, October 25, 2010
![Page 182: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/182.jpg)
Defining methods
• It’s common (and expected) to write methods for your model
• No model methods: Your controller is probably doing too much!
• It’s OK for your model methods to talk to other models via associations...
• ... but your controller probably shouldn’t!
143Monday, October 25, 2010
![Page 183: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/183.jpg)
Common methods
• Return a particular piece of information about the model
• String, calculation, result of a database query, statistics about the object
• Return an array, based on associations or other properties
• Associations are available for free!
144Monday, October 25, 2010
![Page 184: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/184.jpg)
Named scopes
• An easy way to create methods
• Basically, a wrapper around “find”
• Example, from a forum-posting model:
named_scope :questions, :conditions => { :is_question => true }, :order => "created_at DESC"
145Monday, October 25, 2010
![Page 185: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/185.jpg)
Parameterized scopes
named_scope :created_since,
lambda { |since| { :conditions => ['created_at >= ? ', since] }}
named_scope :search,
lambda { |term| { :conditions => ["lower(name) ilike ? ", term] } }
146Monday, October 25, 2010
![Page 186: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/186.jpg)
Parameterized scopes
named_scope :created_since,
lambda { |since| { :conditions => ['created_at >= ? ', since] }}
named_scope :search,
lambda { |term| { :conditions => ["lower(name) ilike ? ", term] } }
Named scope is a procedure object taking one parameter
146Monday, October 25, 2010
![Page 187: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/187.jpg)
When?
• When should you create a named scope?
• Simple answer: Whenever you invoke “find” in a controller, replace it with a named scope.
• It cleans up the controller code a lot.
• Note: Named scopes are class methods, not instance methods
147Monday, October 25, 2010
![Page 188: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/188.jpg)
Chaining scopes# in class Shirt
named_scope :red, :conditions => {:color => 'red'}
named_scope :dry_clean_only, :conditions => ['dry_clean_only = ?', true]
Shirt.red
Shirt.dry_clean_only
Shirt.red.dry_clean_only
148Monday, October 25, 2010
![Page 189: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/189.jpg)
Chaining scopes# in class Shirt
named_scope :red, :conditions => {:color => 'red'}
named_scope :dry_clean_only, :conditions => ['dry_clean_only = ?', true]
Shirt.red
Shirt.dry_clean_only
Shirt.red.dry_clean_only
Composition of scopes!
148Monday, October 25, 2010
![Page 190: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/190.jpg)
Transactions
Group.transaction do
group = Group.create!(:name => group_name)
Membership.create!(:person => @person,
:group => group,
:is_administrator => true,
:status => 'approved')
"Successfully created the group '#{group_name}'."
end
149Monday, October 25, 2010
![Page 191: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/191.jpg)
Transactions
Group.transaction do
group = Group.create!(:name => group_name)
Membership.create!(:person => @person,
:group => group,
:is_administrator => true,
:status => 'approved')
"Successfully created the group '#{group_name}'."
end
Class method “transaction”
149Monday, October 25, 2010
![Page 192: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/192.jpg)
Transaction tips
• Transactions are per connection, not model
• So use whatever class you want
• Failure raises ActiveRecord::Rollback
• These only work in databases that support transactions (i.e., not MySQL’s ISAM)
• Nested transactions work, but are often translated into “savepoints”
150Monday, October 25, 2010
![Page 193: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/193.jpg)
Declarations
• has_one, has_many, and belongs_to are class methods
• (I think of them as declarations)
• All they do is define methods!
• So has_many might seem magical, but all it’s doing is defining a bunch of methods on your object
151Monday, October 25, 2010
![Page 194: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/194.jpg)
Adding declarations
• Add a module to the lib directory
• (Automatically included)
• Use Module#included? to create one or more class methods in the including class
• Voila! Now you can do it, too
• e.g., adds_priority_tags_to_errors
152Monday, October 25, 2010
![Page 195: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/195.jpg)
:include
• When you perform a “find”, consider :include
• It retrieves another object with the current one
• Since the result is cached for this request, no more database retrievals are needed
• A major speedup in many cases
153Monday, October 25, 2010
![Page 196: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/196.jpg)
:include example
Person.all.each {|p| puts p.pets.inspect}
Person.all(:include => :pets).each {|p| puts p.pets.inspect}
154Monday, October 25, 2010
![Page 197: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/197.jpg)
Optimistic locking
• Add a lock_version field to your model, with a default value of 0
• Voila! Now you can stop people from saving older versions on top of newer ones
• Each save/update increments lock_version
• If an older version is saved/updated, a StaleObjectError exception is raised
155Monday, October 25, 2010
![Page 198: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/198.jpg)
Pessimistic locking
• If you pass :lock => true to find, you’ll get an exclusive lock on the row
• Uses SELECT .. FOR UPDATE
• If you need a different string, then pass a string, rather than “true”
• I’ve never used this
• But hey, I use PostgreSQL...
156Monday, October 25, 2010
![Page 199: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/199.jpg)
Seed data
• Don’t put data in a migration file!
• Instead, use the special db:seed Rake task
• File is db/seeds.rb
• Add lots of calls to “create” in here
• It only adds data — no doubles, erasing, or otherwise touching of existing data
157Monday, October 25, 2010
![Page 200: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/200.jpg)
Changing behavior
• Don’t write an “initialize” method for your ActiveRecord object. This will probably fail.
• Instead, use the after_initialize hook
• Or write a plugin that monkey-patches ActiveRecord!
158Monday, October 25, 2010
![Page 201: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/201.jpg)
YAML
• You can turn any ActiveRecord object into YAML with the .to_yaml
159Monday, October 25, 2010
![Page 202: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/202.jpg)
YAML
>> puts Person.first.to_yaml--- !ruby/object:Person attributes: created_at: 2010-10-13 18:01:03.330099 updated_at: 2010-10-13 18:01:03.330099 id: "6" last_name: Lerner email: [email protected] first_name: Reuvenattributes_cache: {}
160Monday, October 25, 2010
![Page 203: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/203.jpg)
JSON
>> puts Person.first.to_json
{"person":{"created_at":"2010-10-13T18:01:03Z","updated_at":"2010-10-13T18:01:03Z","id":6,"last_name":"Lerner","first_name":"Reuven","email":"[email protected]"}}
=> nil
161Monday, October 25, 2010
![Page 204: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/204.jpg)
XML>> puts Person.first.to_xml<?xml version="1.0" encoding="UTF-8"?><person> <created-at type="datetime">2010-10-13T18:01:03Z</created-at> <email>[email protected]</email> <first-name>Reuven</first-name> <id type="integer">6</id> <last-name>Lerner</last-name> <updated-at type="datetime">2010-10-13T18:01:03Z</updated-at></person>=> nil
162Monday, October 25, 2010
![Page 205: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/205.jpg)
:include
• If you want to include one or more associated objects in the JSON or XML output, just use :include
163Monday, October 25, 2010
![Page 206: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/206.jpg)
JSON with :include
>> puts Person.first.to_json(:include => :pets)
{"person":{"created_at":"2010-10-13T18:01:03Z","updated_at":"2010-10-13T18:01:03Z","pets":[{"name":"Spot","created_at":"2010-10-14T07:43:59Z","updated_at":"2010-10-14T07:43:59Z","id":1,"person_id":6,"animal_type":"dog"}],"id":6,"last_name":"Lerner","first_name":"Reuven","email":"[email protected]"}}
=> nil
164Monday, October 25, 2010
![Page 207: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/207.jpg)
>> puts Person.first.to_xml(:include => :pets)
<?xml version="1.0" encoding="UTF-8"?>
<person>
<created-at type="datetime">2010-10-13T18:01:03Z</created-at>
<email>[email protected]</email>
<first-name>Reuven</first-name>
<id type="integer">6</id>
<last-name>Lerner</last-name>
<updated-at type="datetime">2010-10-13T18:01:03Z</updated-at>
<pets type="array">
<pet>
<animal-type>dog</animal-type>
<created-at type="datetime">2010-10-14T07:43:59Z</created-at>
<id type="integer">1</id>
<name>Spot</name>
<person-id type="integer">6</person-id>
<updated-at type="datetime">2010-10-14T07:43:59Z</updated-at>
</pet>
</pets>
</person>
=> nil
165Monday, October 25, 2010
![Page 208: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/208.jpg)
Other options
• :except — ignore certain attributes/tags
• :only — we only want some attributes
• :methods — invoke methods and include their output in the XML
• Or hand a block to to_xml, and then you can use builder (Ruby’s XML-generating facility) to create whatever you want!
166Monday, October 25, 2010
![Page 209: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/209.jpg)
Better XML
• If you want to customize the XML, then use an XML view (instead of an HTML view)
• “Builder” allows you to create XML files very easily, with any tags and attributes
• We’ll talk about this further when we discuss views
167Monday, October 25, 2010
![Page 210: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/210.jpg)
Plugins
• Plugins modify default Rails behavior
• They go in /vendor/plugins
• Many modify ActiveRecord’s behavior
• Be careful before installing a plugin... they’re quite useful, but you don’t want clashes
168Monday, October 25, 2010
![Page 211: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/211.jpg)
Example: acts_as_tree
• Create a table with a “parent” attribute
• If you say “acts_as_tree”, then you get methods for “parent,” “children,” and so forth
• In very widespread use (written by DHH)
169Monday, October 25, 2010
![Page 212: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/212.jpg)
Some others
• acts_as_list
• acts_as_nested_set
• acts_as_taggable
• acts_as_taggable_on_steroids
• acts_as_state_machine
170Monday, October 25, 2010
![Page 213: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/213.jpg)
171Monday, October 25, 2010
![Page 214: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/214.jpg)
190 acts_as gems!
171Monday, October 25, 2010
![Page 215: ActiveRecord 2.3](https://reader033.fdocuments.net/reader033/viewer/2022052504/54c5516f4a7959ea038b45b7/html5/thumbnails/215.jpg)
Contacting me
• Call me in Israel: 054-496-8405
• Call me in the US: 847-230-9795
• E-mail me: [email protected]
• Interrupt me: reuvenlerner (Skype/AIM)
172Monday, October 25, 2010