I would love to see this added to Ruby too, so that I don't have to repeat myself by defining attributes in one place and then initializing them later in initialize
(as discussed in #5825 and #8563).
In the meantime, however, I've come across the fattr gem, which does exactly the same thing (plus a few extra things like defining a foo!
method to re-initialize; I don't think attr_reader
should do those extra things). (By the way, there was a great RubyTapas episode (subscription required) about fattr that got me excited about using it.)
Note that it's possible to support both initializing an attribute to a Proc and using a block to initialize a attribute to an expression that is evaluated during object initialization.
Here's how fattr solves that problem... If you pass a block to fattr
, it is evaluated during initialization:
class C
fattr foo: 42
fattr(:foo_2) { foo * 2 } # evaluated during initialization
fattr(:time) { Time.now } # evaluated during initialization
end
p C.new.foo #=> 42
p C.new.foo_2 #=> 84
p C.new.time #=> 2015-02-05 ...
... whereas if you pass a block as the value for a hash key, it leaves it as a Proc and doesn't evaluate it:
class C
fattr my_proc: ->{ 'my_proc' }
end
p C.new.my_proc #=> #<Proc:0x0...>
p C.new.my_proc.() #=> 'my_proc'
I can't think of any downside to supporting the block behavior for attr_accessor
. It doesn't appear that the block syntax of attr_accessor
is used for anything currently...
class C2
attr_accessor(:foo_2) { foo * 2 } # The block is never called
end
c = C2.new
p c.foo_2 #=> nil
Otherwise, you would be forced to add an initialize
method any time you needed to initialize something to an expression that needed to be evaluated at initialization time, which would require duplication and mixing of initialization methods, which I think should be avoided:
class C
fattr foo: 42
attr_accessor :foo_2
def initialize
@foo_2 = foo * 2
end
end
p C.new.foo #=> 42
p C.new.foo_2 #=> 84