Posted by ryan
at 7:38 PM
on Monday, March 31, 2008
The ability to track whether or not your active record objects have been modified or not becomes a lot easier with the new dirty object functionality of edge rails. It’s dead simple, and pretty slick, to use:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
article = Article.find(:first)
article.changed? #=> false
# Track changes to individual attributes with
# attr_name_changed? accessor
article.title #=> "Title"
article.title = "New Title"
article.title_changed? #=> true
# Access previous value with attr_name_was accessor
article.title_was #=> "Title"
# See both previous and current value with attr_name_change accessor
article.title_change #=> ["Title", "New Title"] |
Beyond the clever attribute based accessor methods, you can also query to object directly for its list of all changed attributes. (Continuing from the previous example):
1
2
3
4
5
|
# Get a list of changed attributes
article.changed #=> ['title']
# Get the hash of changed attributes and their previous and current values
article.changes #=> { 'title' => ["Title", "New Title"] } |
Note: Once you save a dirty object it clears out its changed state tracking and is once again considered unchanged.
1
2
3
|
article.changed? #=> true
article.save #=> true
article.changed? #=> false |
If you’re going to be modifying an attribute outside of the attr= writer, you can use attr_name_will_change! to tell the object to be aware of the change:
1
2
3
4
|
article = Article.find(:first)
article.title_will_change!
article.title.upcase!
article.title_change #=> ['Title', 'TITLE'] |
And coming down the pipe is a feature that will make the most of this functionality – partial SQL updates that will only update attributes that have changed…
tags: ruby,
rubyonrails
Posted by ryan
at 11:59 AM
on Monday, March 24, 2008
It would appear that has_one has finally grown up the stature of has_many and now has support for the :through option.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Magazine < ActiveRecord::Base
has_many :subscriptions
end
class Subscription < ActiveRecord::Base
belongs_to :magazine
belongs_to :user
end
class User < ActiveRecord::Base
has_many :subscriptions
has_one :magazine, :through => : subscriptions, :conditions => ['subscriptions.active = ?', true]
end |
The intermediate associations are properly handled when doing assignments as well:
1
2
3
|
@ryan.subscriptions #=> []
@ryan.magazine = Magazine.create(:name => 'Hustler')
@ryan.subscriptions #=> [<Subscription magazine_id: 1, user_id: 1 ...>] |
tags: ruby,
rubyonrails
Posted by ryan
at 11:37 AM
on Monday, March 24, 2008
It looks like Nick Kallen’s wildly popular has_finder plugin will be making its way into Rails 2.x in the form of named_scope. Observe:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class User < ActiveRecord::Base
named_scope :active, :conditions => {:active => true}
named_scope :inactive, :conditions => {:active => false}
named_scope :recent, lambda { { :conditions => ['created_at > ?', 1.week.ago] } }
end
# Standard usage
User.active # same as User.find(:all, :conditions => {:active => true})
User.inactive # same as User.find(:all, :conditions => {:active => false})
User.recent # same as User.find(:all, :conditions => ['created_at > ?', 1.week.ago])
# They're nest-able too!
User.active.recent
# same as:
# User.with_scope(:conditions => {:active => true}) do
# User.find(:all, :conditions => ['created_at > ?', 1.week.ago])
# end |
All the goodness you’ve come to love in has_finder is now available as named_scope – plus you get some extra goodies too. User.all is given to you for free as an alias for User.find(:all).
Advanced
For those with more discriminating needs, don’t forget some of these has_finder tidbits:
Passing Arguments
Pass in arguments to your named scopes to specify conditions (or other props) at run-time.
1
2
3
4
5
|
class User < ActiveRecord::Base
named_scope :registered, lambda { |time_ago| { :conditions => ['created_at > ?', time_ago] }
end
User.registered 7.days.ago # same as User.find(:all, :conditions => ['created_at > ?', 7.days.ago]) |
Named Scope Extensions
Extend named scopes (in a similar fashion to association extensions).
1
2
3
4
5
6
7
8
9
10
|
class User < ActiveRecord::Base
named_scope :inactive, :conditions => {:active => false} do
def activate
each { |i| i.update_attribute(:active, true) }
end
end
end
# Re-activate all inactive users
User.inactive.activate |
Anonymous Scopes
You can also pass around scopes as first class objects using scoped (a named scoped provided to you for free) as a way to build hairy queries on the fly.
1
2
3
4
5
6
7
8
9
|
# Store named scopes
active = User.scoped(:conditions => {:active => true})
recent = User.scoped(:conditions => ['created_at > ?', 7.days.ago])
# Which can be combined
recent_active = recent.active
# And operated upon
recent_active.each { |u| ... } |
named_scope is a truly great feature. If you haven’t started using it yet, do so. You won’t know how you lived without it. Major thanks goes out to Nick.
tags: ruby,
rubyonrails