On 24/07/07, Ben Munat <[EMAIL PROTECTED]> wrote:
> So, Piers, you're the one that started this whole thing... if it really is
> safe to do:
>
> class MyModel < ActiveRecord::Base
> def initialize(attrs = {}, &block)
> super
> # all my initialization stuff here
> end
> end
>
> and this is documented in the AR::Base docs, then this does kind of seem like
> enough. I mean, it's
> what I did all the time in my java life.
>
> I'd just swear that someone told me the holy beasts of doom would swarm down
> upon me if I ever tried
> to override AR::Base#initialize.
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.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby
on Rails: Core" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/rubyonrails-core?hl=en
-~----------~----~----~----~------~----~------~--~---