UpdateRick Olson pointed out the overhead in accessing instance_values, and suggested instead using instance_variable_get.

Done!

Ruby’s attr_accessor is incredibly handy for wiring up your class’s banal getters and setters. If you’re wiring up some simple booleans this way though, wouldn’t you like to get the beauty of Ruby’s method punctuation?

I mean come on, we’re asking a question here.

The old way:

class Doyen
  attr_accessor :recondite
 
  def recondite?
    self.recondite
  end
 
end

Blah… Let’s tidy that up.

class Doyen
  bool_attr_accessor :recondite
end

Now our Doyen no longer has doyen.recondite, but simply doyen.recondite? and no visual clutter.

The implementation is simple:

class Module
  def bool_attr_accessor *args
    args.each do |arg|
      self.instance_eval do
 
        define_method("#{arg}=") do |value|
          instance_variable_set("@#{arg}", !!value)
        end
 
        define_method("#{arg}?") do
          instance_variable_get("@#{arg}")
        end
 
      end
    end
 
  end
end

Originally I used an attr_writer *args to simplify the setters, but I decided that since this function would always return a boolean, it would be better to do the conversion on the setter and let the getter simply return the pre-converted value.

5 Comments

  1. rick

    I don’t know if you’ve looked at the implementation of #instance_values, but it’s creating a hash of every ivar on each access. That’s not very efficient, and it’s going to leave a lot of clutter for the GC. I think it’s best to keep it simple like instance_variable_get(”@#{arg}”).

  2. Piers Cawley

    Assuming the attribute is supposed to be purely boolean, wouldn’t you be better served by adding

    :recondite?
    ,
    :set_recondite
    ,
    unset_recondite
    and, possibly,
    toggle_recondite
    .

    I tend to think of boolean accessors as code smells though…

  3. Piers Cawley

    Hmm… note to self: Check the preview!

  4. duncanbeevers

    Boolean accessors certainly aren’t something you need every day, and using descriptive setters like that isn’t a bad idea.

    The reason I created these was for simple dirtiness checking on an attribute.

    class SwitchBox < ActiveRecord::Base
      bool_attr_accessor :button_pressed
      after_save :reset_buttons
     
      def press_button_one
        self.button_pressed = true
        # Do button one things
      end
     
      def press_button_two
        self.button_pressed = true
        # Do button two things
      end
     
      def reset_buttons
        if button_pressed?
          # Do cleanup things
          self.button_pressed = false
        end
      end
    end

    Strictly for convenience.

  5. Daniel Berger

    You can blame James Britt for personally shooting down the proposal to have “attr_accessor :recondite?” fail to DWIM, so you have to write it out long hand.

    Piers, you must be effing kidding me.

    Dan

Leave a Comment

Enclose code in <code lang="ruby"></code> if you care.
Preview your comment using the button below.