10

Possible Duplicate:
attr_accessor default values

I am using Ruby on Rails 3.0.9 and I would like to initialize some attr_accessor attribute values in my class\model that inherits from ActiveRecord::Base. That is,

... in my module I have:

class User < ActiveRecord::Base
  attr_accessor :attribute_name1,
                :attribute_name2,
                :attribute_name3,
                ...
end

and I would like to set to true all attr_accessor attribute values. How can I do that?

P.S.: Of course I would like to solve the above issue approaching "à la Ruby on Rails Way". I know about the after_initialize callback but by using that method I should repeat each attribute_name<N> statement for which I would like to set the value to true inside that after_initialize statement (... and this is not DRY - Don't Repeat Yourself). Maybe there is a better way to achieve this. Is there a way to set attr_accessor attribute values "on the fly" when you state those attributes? That is, I expect to declare and set attr_accessor attributes at once!

Community
  • 1
  • 1
Backo
  • 18,291
  • 27
  • 103
  • 170

4 Answers4

12

Did you try:

class User < ActiveRecord::Base
  attr_accessor :attribute_name1,
                :attribute_name2,
                :attribute_name3,
                ...

  after_initialize :set_attr

  def set_attr
    @attribute_name1 = true
    ...
  end
end
apneadiving
  • 114,565
  • 26
  • 219
  • 213
7

For Rails 3.2 or earlier, you could use attr_accessor_with_default:

class User < ActiveRecord::Base
  attr_accessor_with_default :attribute_name1, true
  attr_accessor_with_default :attribute_name2, true
  attr_accessor_with_default :attribute_name3, true
  ...
end

Since your default value is an immutable type (boolean), this form of the method is safe to use here. But don't use it if the default value is a mutable object, including an array or string, because all of your new model objects will share the exact same instance, which is probably not what you want.

Instead, attr_accessor_with_default will accept a block, where you can return a new instance each time:

attr_accessor_with_default(:attribute_name) { FizzBuzz.new }
mahemoff
  • 44,526
  • 36
  • 160
  • 222
Steve
  • 684
  • 7
  • 8
4

I would just define a getter that lazily loads the value you are interested in, and use attr_writer to define the setter. For instance,

class Cat
  attr_writer :amount_of_feet
  def amount_of_feet; @amount_of_feet ||= 4; end # usually true
end

Which, if you really mean it, can be rewritten with some meta-programming:

class Cat
  # The below method could be defined in Module directly
  def self.defaulted_attributes(attributes)
    attributes.each do |attr, default|
      attr_writer attr

      define_method(attr) do
        instance_variable_get("@#{attr}") ||
          instance_variable_set("@#{attr}", default)
      end
    end
  end

  defaulted_attributes :amount_of_feet => 4
end

calin = Cat.new
print "calin had #{calin.amount_of_feet} feet... "
calin.amount_of_feet -= 1
puts "but I cut one of them, he now has #{calin.amount_of_feet}"

This works because, usually, computing the default value won't have any side effect making the order matter and it won't be needed to compute the value until you first try to access it.

(Câlin is my cat; he's doing well, and still has the four of his feet)

Mon ouïe
  • 948
  • 5
  • 6
2

Brutal solution

class User < ActiveRecord::Base
  @@attr_accessible = [:attribute_name1, :attribute_name2, :attribute_name3]
  attr_accessor *@@attr_accessible
  after_initialize :set_them_all
  def set_them_all
    @@attr_accessible.each do |a|
      instance_variable_set "@#{a}", true
    end
  end
end

or little more conceptual: Ruby: attr_accessor generated methods - how to iterate them (in to_s - custom format)?

Community
  • 1
  • 1
fl00r
  • 82,987
  • 33
  • 217
  • 237