Received: by 10.35.62.1 with SMTP id p1mr3410130pyk.1185273658702; Tue, 24 Jul 2007 03:40:58 -0700 (PDT) Return-Path: Received: from py-out-1112.google.com (py-out-1112.google.com [64.233.166.179]) by mx.google.com with ESMTP id v36si4015494wah.2007.07.24.03.40.57; Tue, 24 Jul 2007 03:40:58 -0700 (PDT) Received-SPF: pass (google.com: domain of pdcaw...@gmail.com designates 64.233.166.179 as permitted sender) DomainKey-Status: good (test mode) Received: by py-out-1112.google.com with SMTP id d32so3613472pye for ; Tue, 24 Jul 2007 03:40:57 -0700 (PDT) DKIM-Signature: a=rsa-sha1; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:sender:to:subject:in-reply-to:mime-version:content-type:content-transfer-encoding:content-disposition:references:x-google-sender-auth; b=LJ1SpAlxqjgrjpF/4cl4LhGNKxAPoG+6Qdih9GyICpCCPrVXKUezHHOX/D+JnWHGrdjqdzzMxERSvoFd/rThfIbJDUBklKTNkFFfszkZyNTbOmNinkU8GmTA4ZtvXqVEwF1+PjxiqrfE4jx2qNojTn9dg+e9GWQaDzKG5MmzboA= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:sender:to:subject:in-reply-to:mime-version:content-type:content-transfer-encoding:content-disposition:references:x-google-sender-auth; b=SUe44tSERYs9oKxRuL0VwrOzRyrMKW2CCJQULAKuIZFPDKWoFwO9xlNL3MDysOtDap9hletW07iGcl1XeBpry2q6GkeD+ABciuBniBRI3qU7+/Snl07tooSUyhHgR/ef3ZPlyQ+4FkiFYftL7WpXGffCmEZNxqnP/UCJlNPAE3U= Received: by 10.35.39.2 with SMTP id r2mr7637506pyj.1185273657790; Tue, 24 Jul 2007 03:40:57 -0700 (PDT) Received: by 10.35.16.16 with HTTP; Tue, 24 Jul 2007 03:40:57 -0700 (PDT) Message-ID: <897e96cc0707240340s1b83d0a0wf9de398b1d0bf0e4@mail.gmail.com> Date: Tue, 24 Jul 2007 11:40:57 +0100 From: "Piers Cawley" Sender: pdcaw...@gmail.com To: rubyonrails-core@googlegroups.com Subject: Re: [Rails-core] Re: after_initialize/after_find misfeature In-Reply-To: <46A5AA13.6030101@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-Disposition: inline References: <897e96cc0707220254l750943a2naeab8aaf02499...@mail.gmail.com> <897e96cc0707221556q61c4d314o31c8124b1ed1a...@mail.gmail.com> <4c8ba2280707230730j32db9a9fkacefc1531ce7d...@mail.gmail.com> <46A54EC6.10...@gmail.com> <9212531d0707231814rdb56698j40ac4f61c9bb9...@mail.gmail.com> <46A558FB.2010...@gmail.com> <897e96cc0707232243h22d99933v97e5954541de7...@mail.gmail.com> <46A5AA13.6030...@gmail.com> On 24/07/07, Ben Munat 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.