Interesting Things
ActiveRecord's #method_missing takes precedence over private methods, which means you cannot simply mark "private" database-derived attributes.
code:# File: app/models/rock_star.rb # # == Schema Information # Schema version: 1 # # Table name: rock_stars # # id :integer not null, primary key # real_name :string(255) # band_name :string(255) # personal_life :string(255) # class RockStar < ActiveRecord::Base def method_missing(method, *arguments, &block) puts "I see you've sent my #{method} back and my ActiveRecords and they're all scratched" super end private def personal_life=(arg) puts "Vanish in the air you'll never find me" attributes[:personal_life] = arg end endscript/console:
Loading development environment (Rails 2.0.2) >> sting = RockStar.new(:real_name => 'Gordon Sumner', :band_name => 'The Police') I see you've sent my real_name= back and my ActiveRecords and they're all scratched => #<RockStar id: nil, real_name: "Gordon Sumner", band_name: "The Police", personal_life: nil> >> sting.personal_life = "I'll be watching you" I see you've sent my personal_life= back and my ActiveRecords and they're all scratched => "I'll be watching you"Potential solutions:
- Convention... name "private" database attributes with leading underscores
Exception:
class RockStar < ActiveRecord::Base def personal_life=(arg) raise "Protest is futile, nothing seems to get through" end endHave another? Post a comment.
||= ("or equal") blows up you have a public "writer", but a private "reader"; makes sense, but still worth a mention.
code:class Model < ActiveRecord::Base def field_name=(arg) @field_name = arg end private def field_name @field_name end endscript/console:
Loading development environment (Rails 2.0.2) >> instance = Model.new => #<Model id: nil, field_name: nil> >> instance.field_name = 'lala' => "lala" >> instance.field_name ||= 'dodo' NoMethodError: private method `field_name' called for #<Model:0x17df6d0> from /Library/Ruby/Gems/1.8/gems/activerecord-2.0.2/lib/active_record/attribute_methods.rb:205:in `method_missing' from (irb):4ActiveRecord writers always return the passed in argument, even if you define some other return value. This also makes sense -- necessary for chaining, etc., but what the heck...
code:class Model < ActiveRecord::Base def field_name=(arg) @field_name = arg return "custom return value" end endscript/console:
Loading development environment (Rails 2.0.2) >> instance = Model.new => #<Model id: nil, field_name: nil> >> instance.field_name = 'lala' => "lala"

The last example is due to Ruby, not AR.
class X; def x=(a); return :huh?; end; end
for the private method issue you can do something like this:
Re: ||= ("or equal") blows up you have a public "writer", but a private "reader"
The example you show doesn't work because the caller doesn't have access to the callee's private methods. However, the truly annoying thing about ||= for private accessors is that it doesn't work even when called from within the class. For example:
class Thing def public_method value ||= 1 # sets a local, does not call value= self.value ||= 1 # error: explicit sender end
private
attr_accessor :value end
Nice of my browser to strip out my carriage returns for me.
It's not only ActiveRecord Writers that return the passed argument, it's all Ruby methods that end in in equals sign and are called "the default way".
If you use #send to call a method, you will get the correct return value. Weirdness :)