Forum: Ruby Is there a way to get a method to always run at the end of any descendent's initialize method?

8e44b211865cd5915f2f883fd960b56d?d=identicon&s=25 Xeno Campanoli (Guest)
on 2010-02-12 22:51
(Received via mailing list)
I have an initialize method I want to run at the end of any daughter or
granddaughter 'initialize' to make sure the state has been created
properly, and
I would rather specify the execution from the base class itself than
count on
those descendents to do it.

xc
5a837592409354297424994e8d62f722?d=identicon&s=25 Ryan Davis (Guest)
on 2010-02-12 23:05
(Received via mailing list)
On Feb 12, 2010, at 13:50 , Xeno Campanoli wrote:

> I have an initialize method I want to run at the end of any daughter or granddaughter 
'initialize' to make sure the state has been created properly, and I would rather specify 
the execution from the base class itself than count on those descendents to do it.

The most correct way to do it is to have all subclasses properly use
super at the end of their initialize bodies:

class Subclass < Superclass
  def initialize
    # ... stuff
    super
  end
end

There are "fancier" ways (read: overly clever), but doing it this way is
cleaner, faster, and easier to debug / maintain.
968e5e19e3ce6ccfbb26c311ed872f5d?d=identicon&s=25 Mat Brown (Guest)
on 2010-02-12 23:08
(Received via mailing list)
There's no simple way to do this. One possibility is to create a
separate class method that is responsible for building the object, and
then making the :new method private:

class MyClass
  class <<self
    def build
      instance = new
      instance.some_attr = "some_value"
    end
    private :new
  end
end

Of course, subclasses could still override the build method, so you're
not 100% safe. As far as I know, there's no way you could be in Ruby.

Mat
8e44b211865cd5915f2f883fd960b56d?d=identicon&s=25 Xeno Campanoli (Guest)
on 2010-02-12 23:42
(Received via mailing list)
Ryan Davis wrote:
>   end
> end
>
> There are "fancier" ways (read: overly clever), but doing it this way is cleaner, 
faster, and easier to debug / maintain.
>
>
>

The problem with that is you may want some things from super available
before
you do other things in initialize.  I figured out another way, which is
to
define initialize subroutines as pure virtual in the base class, called
from
initialize, then I put the thing at the end of initialize, and all the
daughters
only modify the called initializers, and all use the base class
initialize
method.  There is nothing to force daughters not to make their own
initialize,
but at least this is nice and formal.
4828d528e2e46f7c8160c336eb332836?d=identicon&s=25 Robert Heiler (shevegen)
on 2010-02-12 23:59
This actually reminds me of conditionally running hook-up methods.

It would be interesting to see how much this meta-aspect could be
included into ruby - instead of writing ruby code per se, write hooks
upon hooks that get triggered when this or that changes. Hmm... sounds
like another language though.
Beb77c4602c3cac7a12149431366ed11?d=identicon&s=25 The Higgs bozo (higgsbozo)
on 2010-02-13 00:34
Xeno Campanoli wrote:
> I have an initialize method I want to run at the end of any daughter or
> granddaughter 'initialize' to make sure the state has been created
> properly, and
> I would rather specify the execution from the base class itself than
> count on
> those descendents to do it.

This is a designed-in feature of Common Lisp, where you can define
:after methods which do what you describe (there's also :before and
:around methods too).

I thought I saw Ruby 2.0 prototype code for something similar (:pre and
:post methods?), but I can't seem find the reference now.

A possible long-term solution (which begins as an experiment) is to go
the whole nine yards: design the spec for :pre and :post methods (maybe
:around too), implement it, and publish the gem. Refine until it starts
to crystallize. Then use the gem.

(It's not overly clever if there's a clean API together with a boatload
of tests, in my opinion.)
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Thomas Sawyer (7rans)
on 2010-02-13 05:01
(Received via mailing list)
Or something like:

  require 'facets/kernel/as'

  class MyClass
    class << self
      alias _new new
      def new
        instance = _new
        ancestors.reverse_each do |anc|
          if anc.instance_methods(false).include?(:preinitialize)
            instance.as(anc).preinitialize
          end
        end
      end
    end
  end

Then define #preinitialize at any level you wish.

(Note this is off the top of my head so it may need tweaking to run).
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2010-02-13 09:12
(Received via mailing list)
On 02/12/2010 11:41 PM, Xeno Campanoli wrote:
>>   end
> initialize, then I put the thing at the end of initialize, and all the daughters
> only modify the called initializers, and all use the base class initialize
> method.  There is nothing to force daughters not to make their own initialize,
> but at least this is nice and formal.

Template method pattern.

Here's another approach, only mildly more sophisticated:

irb(main):001:0> class Base
irb(main):002:1> def self.new(*a, &b)
irb(main):003:2> x = allocate
irb(main):004:2> x.send(:initialize, *a, &b)
irb(main):005:2> x.after_init
irb(main):006:2> x
irb(main):007:2> end
irb(main):008:1> def after_init; puts "hook run for #{self.class}"; end
irb(main):009:1> end
=> nil
irb(main):010:0> class Derived < Base
irb(main):011:1> def initialize; puts "work for #{self.class}"; end
irb(main):012:1> end
=> nil
irb(main):013:0> d = Derived.new
work for Derived
hook run for Derived
=> #<Derived:0x8c6b85c>
irb(main):014:0> class Derived2 < Derived
irb(main):015:1> def initialize;super;puts "in #{self.class}"; end
irb(main):016:1> end
=> nil
irb(main):017:0> e = Derived2.new
work for Derived2
in Derived2
hook run for Derived2
=> #<Derived2:0x8c25ac4>
irb(main):018:0>

The basic trick is to redefine the base class's #new method.

Kind regards

  robert
Beb77c4602c3cac7a12149431366ed11?d=identicon&s=25 The Higgs bozo (higgsbozo)
on 2010-02-13 21:17
Thomas Sawyer wrote:
> Or something like:
>
> require 'facets/kernel/as'
>
> class MyClass
>   class << self
>     alias _new new
>     def new
>       instance = _new
>       ancestors.reverse_each do |anc|
>         if anc.instance_methods(false).include?(:preinitialize)
>           instance.as(anc).preinitialize
>         end
>       end
>     end
>   end
> end
>
> Then define #preinitialize at any level you wish.
>
> (Note this is off the top of my head so it may need tweaking to
> run).

Yeah, that was Ryan's point about overly clever solutions. That may
look good now, but someone who is debugging some code which depends on
it will hate you. And when you are debugging six months from now,
you'll hate yourself.

If you really want to do this, put some real effort into designing a
clean API for it. Put it in a gem. Make it real, real obvious that
you're putting a twist on the conventional method calling
chain. Document the API so it's obvious in a coherent, readily
understandable way.

Don't slip a concrete dildo into someone's box of Fruit Loops. They
won't be happy with your Morning Breakfast Surprise. Put the concrete
dildo in a clearly labeled box, with instructions. Then when someone
encounters a problem, "Hey, something is screwing me here. Maybe it's
the concrete dildo?" at least they know to ask.
Be30361bb0b0c495e3077db43ad84b56?d=identicon&s=25 Aaron Patterson (Guest)
on 2010-02-14 04:52
(Received via mailing list)
On Sun, Feb 14, 2010 at 05:17:49AM +0900, The Higgs bozo wrote:
> >       ancestors.reverse_each do |anc|
> > (Note this is off the top of my head so it may need tweaking to
> chain. Document the API so it's obvious in a coherent, readily
> understandable way.
>
> Don't slip a concrete dildo into someone's box of Fruit Loops. They
> won't be happy with your Morning Breakfast Surprise. Put the concrete
> dildo in a clearly labeled box, with instructions. Then when someone
> encounters a problem, "Hey, something is screwing me here. Maybe it's
> the concrete dildo?" at least they know to ask.

I was wondering where my concrete dildo went.  Please clean the Fruit
Loops off and return ASAP.  My lawn ornaments just aren't the same
without it.
Cec345a59245af9d06e4438a413f4eb5?d=identicon&s=25 Shot (Piotr Szotkowski) (Guest)
on 2010-02-22 17:33
(Received via mailing list)
Attachment: signature.asc (198 Bytes)
Xeno Campanoli:

> I have an initialize method I want to run at the end of any daughter
> or granddaughter 'initialize' to make sure the state has been created
> properly, and I would rather specify the execution from the base class
> itself than count on those descendents to do it.

Other than what others already said, you could try either an AOP
solution like Aquarium or Gazer (both seem unmaintained) or RCapture
(my personal favourite), which seems to be alive.

http://cheind.wordpress.com/2010/01/07/introducing-rcapture/
http://code.google.com/p/rcapture/
http://www.elctech.com/projects/aspect-oriented-pr...
http://github.com/teejayvanslyke/gazer
http://aquarium.rubyforge.org/

— Shot
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.