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
-~----------~----~----~----~------~----~------~--~---

Reply via email to