Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

PIVOTAL LABS
Standup 05/30/2008

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
    end
    

    script/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
      end
      
    • Have 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
    end
    

    script/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):4
    
  • ActiveRecord 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
    end
    

    script/console:

    Loading development environment (Rails 2.0.2)
    >> instance = Model.new
    => #<Model id: nil, field_name: nil>
    >> instance.field_name = 'lala'
    => "lala"
    

Comments
  1. coderrr says:

    The last example is due to Ruby, not AR.

    class X; def x=(a); return :huh?; end; end

    >> X.new.x = 5
    => 5

  2. coderrr says:

    for the private method issue you can do something like this:

    class ActiveRecord::Base
    alias_method :old_mm, :method_missing
    def method_missing m, *a, &b
    raise NoMethodError, “its private!” if self.class.private_method_defined? m

    old_mm m, *a, &b
    end
    end

  3. Adam says:

    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

  4. Adam says:

    Nice of my browser to strip out my carriage returns for me.

  5. Arthur says:

    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 :)

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *