Friday
Oct242008
Rails 2.2 Change: Private Methods on Association Proxies are Private
Friday, October 24, 2008 at 11:26AM
There's a small change in Active Record behavior that's going to bite some existing applications under Rails 2.2: private methods on an association proxy are actually private. To illustrate, this code would work in Rails 2.1:
user.rb:
[sourcecode language='ruby']
class User < ActiveRecord::Base
has_one :account
end
[/sourcecode]
account.rb:
[sourcecode language='ruby']
class Account < ActiveRecord::Base
belongs_to :user
private
def secret_code
"ABCDE"
end
end
[/sourcecode]
Controller code:
[sourcecode language='ruby']
@u = User.first
@code = u.account.secret_code
[/sourcecode]
With Rails 2.1, you'll actually get the secret code in @code, even though it comes from a private method of the account object. Rails 2.2 is stricter about this: that same code will fail with a NoMethodError. If you want to access a private method across an association proxy now, you'll need to use @code = u.account.send("secret_code").
Directly accessing private methods on an Active Record instance itself was already blocked in Rails 2.1, but Rails 2.2 updates that failure to give back a NoMethodError as well.
user.rb:
[sourcecode language='ruby']
class User < ActiveRecord::Base
has_one :account
end
[/sourcecode]
account.rb:
[sourcecode language='ruby']
class Account < ActiveRecord::Base
belongs_to :user
private
def secret_code
"ABCDE"
end
end
[/sourcecode]
Controller code:
[sourcecode language='ruby']
@u = User.first
@code = u.account.secret_code
[/sourcecode]
With Rails 2.1, you'll actually get the secret code in @code, even though it comes from a private method of the account object. Rails 2.2 is stricter about this: that same code will fail with a NoMethodError. If you want to access a private method across an association proxy now, you'll need to use @code = u.account.send("secret_code").
Directly accessing private methods on an Active Record instance itself was already blocked in Rails 2.1, but Rails 2.2 updates that failure to give back a NoMethodError as well.

Reader Comments (2)
I'm glad this functionality is getting some attention, since I think it significantly improves the correctness of method calling via associations. However, I'm not sure I agree with your final paragraph.
Calling private methods defined on classes derived from ActiveRecord fail (I believe) with a NoMethodError in every version of Rails that I have ever worked with; this is standard Ruby functionality. However, if you try to restrict access to a method added to an ActiveRecord by Rails from a database column, you will be disappointed. Rails 2.1, and earlier, dispatches to those methods via method missing without checking for privacy. Rails 2.2 corrects this error.
For instance, if your users table has an email_address column, this code will succeed in 2.1, but fail with a NoMethodError in 2.2:
class User < ActiveRecord::Base
private
def email_address
read_attribute(:email_address)
end
end
...
puts User.new.email_address
Is this changable... [sourcecode language='ruby']
@u = User.first
@code = u.account.secret_code
[/sourcecode]
then, how it possible..
Email Database