Yeah, I think I've heard the Holy Beasts of Doom argument as well, I
just can't for the life of me remember where. I have certainly been
bitten by trying to set the defaults before calling super though
(because, dammit, that's the logical place to set defaults). Thinking
about it, the way to do that would be something like:
def initialize(attrs = {}, &block)
super attrs.reverse_merge(:default => 'this'), &block
end
but then you make yourself a hostage to fortune that everyone's going
to use symbols as keys. The post super approach to setting defaults
has some edge case problems too in cases where nil is a legal value
for some attribute (yeah, it's a weird edge case, but an edge case all
the same). You end up with ugly code like:
def initialize(attrs = {}, &block)
super
unless attrs.has_key?(:content) || attrs.has_key?('content')
self.content = "Write something here"
end
end
The issue is, I think, that ActiveRecord::Base#initialize is doing two
different 'sorts' of things: class invariant metadata initialization,
and instance specific initialization. You might compose the method as:
def initialize(attributes = nil, &block)
initialize_metadata
initialize_instance(attributes, &block)
end
def initialize_metadata
@attributes = attributes_from_column_definition
@new_record = true
ensure_proper_type
end
def initialize_instance(attributes
self.attributes = attributes unless attributes.nil?
yield self if block_given?
end
Maybe the right thing to do is to implement ActiveRecord::Base.new as:
class ActiveRecord::Base
def self.new(attributes = nil, &block)
returning(self.allocate) do |instance|
instance.initialize_metadata
instance.initialize(attributes, &block)
end
end
def initialize_metadata
@attributes = attributes_from_column_definition
@new_record = true
ensure_proper_type
end
def initialize(attributes)
self.attributes = attributes unless attributes.nil?
yield self if block_given?
end
end
Then anyone who wants to write code that initializes defaults before
the actual attributes are set can do:
def initialize(attributes = nil, &block)
self.content = "Write something here"
super
end
and everybody is happy.