Overriding new?

A

Andrea Dallera

Hi everybody,

given the following situation:

class Parent

def initialize
print "magic\n"
end

end

class Child < Parent

def initialize
print "more magic\n"
end

end

child = Child.new

is there a way in which i can get "magic" and "more magic" as the
output, without having to add super to the child class's constructor? I
was thinking about overriding the class method "new" but i really didn't
get anything to work up to now.

Any suggestions? Thanks in advance!
 
C

Chuck Remes

Hi everybody,

given the following situation:

class Parent

def initialize
print "magic\n"
end

end

class Child < Parent

def initialize
print "more magic\n"
end

end

child = Child.new

is there a way in which i can get "magic" and "more magic" as the
output, without having to add super to the child class's constructor? I
was thinking about overriding the class method "new" but i really didn't
get anything to work up to now.

Use the #super keyword to call the superclass.


class Parent
def initialize
print "magic\n"
end
end

class Child < Parent
def initialize
super
print "more magic\n"
end
end

child = Child.new

----------

cremes$ ruby a.rb
magic
more magic

cr
 
A

Andrea Dallera

Hei Chuck,

that's exactly what i was trying to avoid. While I'm at it, let me be
more specific about my problem, which is actually not just printing out
text to the console but changing the state of the newly created
instance:

class Parent

attr_reader :test_value

def initialize
@test_value = "from parent"
end

end

class Child < Parent

def initialize
p @test_value
end

end

child = Child.new

this should print out "from parent"
 
M

Marvin Gülker

Andrea said:
this should print out "from parent"

Maybe I can't follow you, but that's exactly what's super for:
-------------------------------------------
irb(main):001:0> class Parent
irb(main):002:1> attr_reader :test_value
irb(main):003:1> def initialize
irb(main):004:2> @test_value = "from parent"
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> class Child < Parent
irb(main):008:1> def initialize
irb(main):009:2> super
irb(main):010:2> p @test_value
irb(main):011:2> end
irb(main):012:1> end
=> nil
irb(main):013:0> child = Child.new
"from parent"
=> #<Child:0x0000000183c140 @test_value="from parent">
irb(main):014:0>
 
J

jbw

[Note: parts of this message were removed to make it a legal post.]

def initialize
p @test_value = super
end

?
 
C

Chuck Remes

Hei Chuck,

that's exactly what i was trying to avoid. While I'm at it, let me be
more specific about my problem, which is actually not just printing out
text to the console but changing the state of the newly created
instance:

Ah, I didn't read your original email very closely.

I don't know how to accomplish that without #super. There might be a way to modify the parent's meta class and then have the child pick up that value, but I'm not really sure how to do it.

cr
 
A

Aldric Giacomoni

Andrea:
I think something similar was brought up on this mailing
list/forum/newsgroup within the last two weeks or so. Take a look
through the older topics.
 
A

Andrea Dallera

Hi everybody,

the whole issues comes from here:
http://github.com/bolthar/freightrain/blob/master/lib/freightrain/freight_view_model.rb
FreightViewModel is part of the framework, and if you're using it that's
the way you should do it:

class MyViewModel < FreightViewModel

...

end

what i don't like is that if you want to make _your_ viewmodel do stuff
on initialize, which is usual, you have to write your initialize method
as such:

def initialize
super
#your stuff here
end

otherwise the automagical initialization (like building views, hooking
to services, etc) doesn't work, because the constructor of
FreightViewModel (that is the one taking care of all this) doesn't get
called.
I know this is not extremely important (you just have to remind to call
super) but i'd prefer not to have that hassle : i forget it myself more
than often.
 
A

Andrea Dallera

I'm sorry, I looked before posting and i couldn't find anything. I'm
looking now too and still I can't find it, so if you maybe have a
pointer to that it would be really nice. Sorry for spamming.
 
W

Walton Hoops

I'm sorry, I looked before posting and i couldn't find anything. I'm
looking now too and still I can't find it, so if you maybe have a
pointer to that it would be really nice. Sorry for spamming.
Found it: http://www.ruby-forum.com/topic/204105
While it does offer some methods you might use, I highly recommend not
trying to solve the problem this way. 'super' is an expected part of
sub-classing, and there may be instances in which the sub-classer does
*not* want your initializer to run. If this is something you need to
do, I might be inclined to solve it with the factory approach. So
instead those using your framework would do something like:
MyFactory.getInstance() do
# user defines their behavior here
end

which returns an already initialized object.
 
R

Robert Klemme

2010/3/15 Walton Hoops said:
Found it: http://www.ruby-forum.com/topic/204105
While it does offer some methods you might use, I highly recommend not
trying to solve the problem this way. =A0'super' is an expected part of
sub-classing, and there may be instances in which the sub-classer does
*not* want your initializer to run. =A0If this is something you need to
do, I might be inclined to solve it with the factory approach. So
instead those using your framework would do something like:
MyFactory.getInstance() do
# user defines their behavior here
end

which returns an already initialized object.

I agree: calling super is the default method for making sure super
classes are initialized properly. I would stick to that rather than
inventing some magic behind the scenes. For exampl, if you override
#new to do what OP wants (which is possible) measures should be taken
to not break if someone actually invokes super in their #initialize.

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

Hei Chuck,

that's exactly what i was trying to avoid.


One way to avoid it is to override new. You can use the "allocate" method
to create a new object instance without invoking its constructor. Name the
superclass's "initialize" method something different, like preinitialize,
and invoke that yourself. Be sure to invoke initialize too, if it's
defined.

I've used this approach in frameworks where I intend for users to subclass,
but my general impression nowadays is I should use a lot cleaner base class
that doesn't need these sorts of hax, or not expect users to ever define
their own constructor.
 
C

Chuck Remes

One way to avoid it is to override new. You can use the "allocate" method
to create a new object instance without invoking its constructor. Name the
superclass's "initialize" method something different, like preinitialize,
and invoke that yourself. Be sure to invoke initialize too, if it's
defined.

I've used this approach in frameworks where I intend for users to subclass,
but my general impression nowadays is I should use a lot cleaner base class
that doesn't need these sorts of hax, or not expect users to ever define
their own constructor.

I like Tony's final suggestion. In your base class initialize method, have it make a call to post_initialize. All subclasses should then create a post_initialize method if they need to do any construction (or leave it empty).

This technique is used in Eventmachine since its internals like to control when and how classes are instantiated.

cr
 
A

Andrea Dallera

Hei,

I am uncertain about what to do: on one side I'm very aware that it
would be a huge hack and, before all, using this kind of "template
method" in the base class, and having every element to inherit from it,
is very unflexible. On the other side i've been using freightrain for
quite a while now and i really really like the syntactic sugar, so
things like
MyFactory.getInstance() do
# user defines their behavior here
end
as Walton (which i thank for the pointer) suggested are a big no no.

I like this way of doing it:
def new(*a,&b)
obj = allocate
#do your stuff
obj.send:)initialize,*a,&b)
return obj
end

What do you think about it? Aside from the performance hit (in my case i really don't case), are there any motivation i'm not seeing for not using it?
Also forbidding to specify the constructor would be a way to go but i'd like to allow as much freedom as possible.
 
K

Ken Bloom

Hi everybody,

the whole issues comes from here:
http://github.com/bolthar/freightrain/blob/master/lib/freightrain/ freight_view_model.rb
FreightViewModel is part of the framework, and if you're using it that's
the way you should do it:

class MyViewModel < FreightViewModel

...

end

what i don't like is that if you want to make _your_ viewmodel do stuff
on initialize, which is usual, you have to write your initialize method
as such:

def initialize
super
#your stuff here
end

otherwise the automagical initialization (like building views, hooking
to services, etc) doesn't work, because the constructor of
FreightViewModel (that is the one taking care of all this) doesn't get
called.
I know this is not extremely important (you just have to remind to call
super) but i'd prefer not to have that hassle : i forget it myself more
than often.

You ought to just get in the habit of remembering it.

Maybe you can write some kind of automatic checker that will look for
definitions of initialize that don't call super.

I think it's always appropriate to call super in an initialize call,
because everything descends from Object, and Object defines an initialize
method.

--Ken
 
W

Walton Hoops

Hei,

I am uncertain about what to do: on one side I'm very aware that it
would be a huge hack and, before all, using this kind of "template
method" in the base class, and having every element to inherit from it,
is very unflexible. On the other side i've been using freightrain for
quite a while now and i really really like the syntactic sugar, so
things like
MyFactory.getInstance() do
# user defines their behavior here
end
as Walton (which i thank for the pointer) suggested are a big no no.

I like this way of doing it:
def new(*a,&b)
obj = allocate
#do your stuff
obj.send:)initialize,*a,&b)
return obj
end

What do you think about it? Aside from the performance hit (in my case i really don't case), are there any motivation i'm not seeing for not using it?
Also forbidding to specify the constructor would be a way to go but i'd like to allow as much freedom as possible.
I assume that '#do your stuff' is where you do things like call the
parents initializer?
1) What happens is the programmer *does* call super in initialize.
While sometimes it's ok for code to get run twice, sometimes it's not.
Be sure that if you are ensuring the parent class's initialize method
runs, that it doesn't foobar something if it runs twice because the user
called 'super'.
2) Are you sure there isn't an instance where someone would want to
subclass your class, but not run the parents initializer? One example
where I can think of this occurring is doing mocks for unit testing. I
may still want to use some of the functionality from your class (thus
subclassing it), but cut out some of the backing guts and replace them
with something I have control over.

Provided you have thought about those two things, and still think this
is the right choice, then I see no problem with this code.
 
A

Andrea Dallera

I should have been clearer in the example. This is the actual situation:

class FreightViewModel

def initialize
do_some_init_stuff()
end

end

and this is what it will become if i apply that solution:

class FreightViewModel

def new(*args, &block)
obj = allocate
obj.do_some_init_stuff
obj.send:)initialize, *args, &block)
return obj
end

end
What happens is the programmer *does* call super in initialize.
While sometimes it's ok for code to get run twice, sometimes it's not.
Be sure that if you are ensuring the parent class's initialize method
runs, that it doesn't foobar something if it runs twice because the user
called 'super'.
Calling super shouldn't be a problem in this case: the constructor of the base class does nothing...right?
Are you sure there isn't an instance where someone would want to
subclass your class, but not run the parents initializer? One example
where I can think of this occurring is doing mocks for unit testing. I
may still want to use some of the functionality from your class (thus
subclassing it), but cut out some of the backing guts and replace them
with something I have control over.
Good point. I'm taking care of this, in a way that provides a full stub
(with all the dependencies stubbed and injected) for integration testing
with one line of code, which was one of the initial objectives: i come
from WPF and it can be a pain to write integration testing for a VM with
even just 3 services, i wanted to be able to test without having to
write huge and complex setups all the time. Still, one can think of
other cases where a VM is is still needed "uninitialized": i guess i'll
provide an hard switch if the case actually arises.

Thanks a lot for all the suggestions!
 
W

Walton Hoops

I should have been clearer in the example. This is the actual situation:

class FreightViewModel

def initialize
do_some_init_stuff()
end

end

and this is what it will become if i apply that solution:

class FreightViewModel

def new(*args, &block)
obj = allocate
obj.do_some_init_stuff
obj.send:)initialize, *args, &block)
return obj
end

end


Calling super shouldn't be a problem in this case: the constructor of the base class does nothing...right?
Right, so not an issue in this case.
Good point. I'm taking care of this, in a way that provides a full stub
(with all the dependencies stubbed and injected) for integration testing
with one line of code, which was one of the initial objectives: i come
from WPF and it can be a pain to write integration testing for a VM with
even just 3 services, i wanted to be able to test without having to
write huge and complex setups all the time. Still, one can think of
other cases where a VM is is still needed "uninitialized": i guess i'll
provide an hard switch if the case actually arises.

Thanks a lot for all the suggestions!
Sounds to me like you've given it plenty of thought and are good to go.
Best of luck with your project!
 
A

Andrea

Andrea Dallera said:
what i don't like is that if you want to make _your_ viewmodel do stuff
on initialize, which is usual, you have to write your initialize method
as such:

def initialize
super
#your stuff here
end

I suppose you could change a bit the design and use a hook method
instead of initialize in the subclass. I mean, in the superclass you do
all your automagical initalization, then you call a (blank) hook method,
and later you redefine it when you build the new class. Something like
this:

class Parent
def initalize
# do automagic stuff
configure
end

def configure
# just a blank hook method
end
end

class Child < Parent
def configure
# do some specific stuff
end
end
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,731
Latest member
MarcyGipso

Latest Threads

Top