Is there a way to get a method to always run at the end of anydescendent's initialize method?

Discussion in 'Ruby' started by Xeno Campanoli, Feb 12, 2010.

  1. 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

    --
    "It's the preponderance, stupid!" - Professor Stephen Schneider, IPCC member
     
    Xeno Campanoli, Feb 12, 2010
    #1
    1. Advertising

  2. Xeno Campanoli

    Ryan Davis Guest

    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.
     
    Ryan Davis, Feb 12, 2010
    #2
    1. Advertising

  3. Xeno Campanoli

    Mat Brown Guest

    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

    On Fri, Feb 12, 2010 at 16: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.
    >
    > xc
    >
    > --
    > "It's the preponderance, stupid!" - Professor Stephen Schneider, IPCC member
    >
    >
     
    Mat Brown, Feb 12, 2010
    #3
  4. Ryan Davis wrote:
    > 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.
    >
    >
    >


    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.

    --
    "It's the preponderance, stupid!" - Professor Stephen Schneider, IPCC member
     
    Xeno Campanoli, Feb 12, 2010
    #4
  5. Xeno Campanoli

    Marc Heiler Guest

    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.
    --
    Posted via http://www.ruby-forum.com/.
     
    Marc Heiler, Feb 12, 2010
    #5
  6. 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.)
    --
    Posted via http://www.ruby-forum.com/.
     
    The Higgs bozo, Feb 12, 2010
    #6
  7. Xeno Campanoli

    Intransition Guest

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

    Or something like:

    require 'facets/kernel/as'

    class MyClass
    =A0 class << self
    alias _new new
    =A0 =A0 def new
    =A0 =A0 =A0 instance =3D _new
    ancestors.reverse_each do |anc|
    if anc.instance_methods(false).include?:)preinitialize)
    =A0 =A0 =A0 instance.as(anc).preinitialize
    end
    end
    =A0 =A0 end
    =A0 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).
     
    Intransition, Feb 13, 2010
    #7
  8. On 02/12/2010 11:41 PM, Xeno Campanoli wrote:
    > Ryan Davis wrote:
    >> 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.
    >>
    >>
    >>

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


    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


    --
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Feb 13, 2010
    #8
  9. Re: Is there a way to get a method to always run at the end of any descendent's initialize method?

    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.
    --
    Posted via http://www.ruby-forum.com/.
     
    The Higgs bozo, Feb 13, 2010
    #9
  10. On Sun, Feb 14, 2010 at 05:17:49AM +0900, The Higgs bozo wrote:
    > 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.


    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.

    --
    Aaron Patterson
    http://tenderlovemaking.com/
     
    Aaron Patterson, Feb 14, 2010
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. jerry
    Replies:
    13
    Views:
    859
    Malcolm McLean
    Oct 1, 2011
  2. Sean Ross
    Replies:
    3
    Views:
    126
    Aredridel
    Dec 25, 2003
  3. Harry Ohlsen
    Replies:
    10
    Views:
    249
    Zachary P. Landau
    Feb 8, 2004
  4. trans.  (T. Onoma)

    initialize always

    trans. (T. Onoma), Nov 27, 2004, in forum: Ruby
    Replies:
    15
    Views:
    203
    trans. (T. Onoma)
    Dec 16, 2004
  5. DaLoverhino
    Replies:
    1
    Views:
    102
    Uri Guttman
    Sep 9, 2009
Loading...

Share This Page