Consider:
class BigGuy
def initialize
@favorites = [“a”,“b”,“c”]
end
class << self
def display_favs
class_eval “p @favorites”
end
end
end
class LittleGuy < BigGuy
display_favs
end
Why does this display “nil”? I expected the initialize of BigGuy to
occur prior to invoking display_favs. Any other construction I should
be using to get my desire behavior of the constructor of the base class
getting called prior to singleton methods called from the child class?
[email protected] [email protected] wrote:
end
getting called prior to singleton methods called from the child class?
“Initialize” is called when the class is instantiated (i.e. with new).
You are never instantiating this class, so “initialize” is never called.
m.
On 10/26/06, matt neuburg [email protected] wrote:
def display_favs
occur prior to invoking display_favs. Any other construction I should
be using to get my desire behavior of the constructor of the base class
getting called prior to singleton methods called from the child class?
“Initialize” is called when the class is instantiated (i.e. with new).
You are never instantiating this class, so “initialize” is never called.
True, but kind of beside the point. The initialize method if and when
it is called will set the instance variable @favorites in a new
INSTANCE of BigGuy.
class BigGuy
…
class << self
def display_favs
class_eval “p @favorites”
end
end
end
This makes display_favs a class method of BigGuy.
In the method display_favs self is the class object BigGuy.
so
class_eval “p @favorites”
is the same as
BigGuy.class_eval “p @favorites”
which is printing a class instance variable of BigGuy which doesn’t
exist.
And…
class LittleGuy < BigGuy
display_favs
end
inside this class definition, self is LittleGuy so
display_favs
is equivalent to:
LittleGuy.display_favs
which executes
BigGuy::display_favs
by inheritance
which prints the uninitialized class instance variable @favorites.
Note also that the class instance variable @favorite in BigGuy is
different from the clas instance variable @favorite in LittleGuy,
although neither one is initialized.
–
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/
I’ll say thanks to both of you. I think I was confused on all
accounts, but the biggest issue is probably that I thought the
@favorites was the same for both classes. I guess I will need to
restructure things then. Basically, I want the base class to provide
some default settings that a child class could override using a class
method. Apparently, I am unclear on the way to do so.
Rick DeNatale [email protected] wrote:
class << self
Why does this display “nil”? I expected the initialize of BigGuy to
occur prior to invoking display_favs. Any other construction I should
be using to get my desire behavior of the constructor of the base class
getting called prior to singleton methods called from the child class?
“Initialize” is called when the class is instantiated (i.e. with new).
You are never instantiating this class, so “initialize” is never called.
True, but kind of beside the point.
Perhaps it’s a matter of pedagogy. What is “the point”? That depends
on where one thinks the OP has ultimately gone wrong. The OP might have
any of several misconceptions (it is hard to be certain); the OP might
not be aware:
(1) that initialize is called at instantiation time.
(2) that a class and an instance of that class are different things.
(3) that the singleton method is a class method.
(4) that the @favorites referred to in the class method is not the
@favorites referred to in “initialize”.
Each of us is trying to teach, so each of us, like a good teacher, made
a guess about where the heart of the OP’s misunderstanding might lie. I
guessed (1), with a little bit of (2) thrown in. You are leaning more
toward (3) and (4). Your guess is reasonable, but my guess is not
unreasonable, especially since the OP explicitly said: “I expected the
initialize … to occur”. I spoke to that issue, showing why that
expectation was wrong. So what I said is hardly “beside the point”.
m.
On Fri, 27 Oct 2006, [email protected] wrote:
I’ll say thanks to both of you. I think I was confused on all
accounts, but the biggest issue is probably that I thought the
@favorites was the same for both classes. I guess I will need to
restructure things then. Basically, I want the base class to provide
some default settings that a child class could override using a class
method. Apparently, I am unclear on the way to do so.
it’s astonishingly hard to do in a generic way. take my word for it,
the
easiest way is to do
harp:~ > cat a.rb
require 'rubygems'
require 'attributes'
class C
class << self
attribute 'a' => 42
end
end
class K < C
end
p C.a
p K.a
class K
a 'forty-two'
end
p C.a
p K.a
harp:~ > ruby a.rb
42
42
42
"forty-two"
regards.
-a
If you want to do it at the class level instead of the instance level,
it looks like what is done quite a lot in rails
for example in active record :
class Toto<<ActiveRecord::Base
end
would use totos as table name by default (define by AR) but you can
chage it to something else like this :
class Toto<<ActiveRecord::Base
set_table_name “mice”
end
now it will use mice instead of toto
if this is what you are trying to do, have a look at rails
implementation, which relies on rails define_attr_method :
http://railsmanual.org/class/ActiveRecord::Base/define_attr_method
hope this helps
matt neuburg [email protected] wrote:
def BigGuy.make_subclass(what)
end
Okay, scratch that. It’s fatally flawed. Oh, well. m.
[email protected] [email protected] wrote:
Basically, I want the base class to provide
some default settings that a child class could override using a class
method. Apparently, I am unclear on the way to do so.
I guess I’m wondering: does it absolutely have to be a class method?
Could it be an ordinary instance method? In this approach, as you
subclass you get to define an instance method, “setup”, that you know
will be called by the constructor:
class BigGuy
def setup
@favorites = “bluto”
end
def initialize
setup
p @favorites
end
end
class LittleGuy < BigGuy
def setup
@favorites = “popeye”
end
end
BigGuy.new #=> “bluto”
LittleGuy.new #=> “popeye”
BigGuy.new #=> “bluto”
LittleGuy.new #=> “popeye”
If you really do want it to be class-based, perhaps a class method might
generate a subclass, set up the way you want it, from which you derive
further subclasses. I’m not very adept at this sort of thing, but my
first go would be something like this:
class BigGuy
def setup
@favorites = “bluto”
end
def initialize
setup
p @favorites
end
class MiddleGuy < BigGuy
end
def BigGuy.make_subclass(what)
MiddleGuy.class_eval %{
def setup
@favorites = “#{what}”
end
}
return MiddleGuy
end
end
class LittleGuy < BigGuy.make_subclass(‘popeye’)
end
BigGuy.new #=> “bluto”
LittleGuy.new #=> “popeye”
BigGuy.new #=> “bluto”
LittleGuy.new #=> “popeye”
I like the ultimate syntax used to generate LIttleGuy, but I’m not
entirely happy because it assumes that the parameter is a string. But I
got all caught up the “eval” situation and couldn’t think straight any
more after a while… Maybe it was a silly idea. m.
matt neuburg [email protected] wrote:
Okay, scratch that. It’s fatally flawed. Oh, well. m.
Aha! This is better:
class BigGuy
def setup
@favorites = “bluto”
end
def initialize
setup
p @favorites
end
class MiddleGuy < BigGuy
end
def self.make_subclass(what)
p = Proc.new {@favorites = what}
MiddleGuy.send( :define_method, :setup, p )
return MiddleGuy.clone
end
end
class LittleGuy < BigGuy.make_subclass(‘popeye’)
end
class DevilishGuy < BigGuy.make_subclass(666)
end
BigGuy.new #=> “bluto”
LittleGuy.new #=> “popeye”
BigGuy.new #=> “bluto”
DevilishGuy.new #=> 666
LittleGuy.new #=> “popeye”
The Proc handles the problem with eval that I was having before, so any
kind of value can now be passed to make_subclass. The other problem I
was having is that we if called make_subclass twice with different
values, only one of those values was being used; hence the “clone” call.
Perhaps this will give the OP something to build on… Anyway it was fun
to think about.
m.
Jacob F. [email protected] wrote:
end
class BigGuy
Class.new(self) do
LittleGuy = BigGuy.with_favorites(‘popeye’)
DevilishGuy = BigGuy.with_favorites(666)
Then the following code produces the same output as yours above:
BigGuy.new #=> “bluto”
LittleGuy.new #=> “popeye”
BigGuy.new #=> “bluto”
DevilishGuy.new #=> 666
LittleGuy.new #=> “popeye”
Yeah! I could NOT for the life of me figure out how to make that
anonymous class, and the direct assignment just didn’t occur to me.
Thanks - m.
On 10/27/06, matt neuburg [email protected] wrote:
def self.make_subclass(what)
p = Proc.new {@favorites = what}
MiddleGuy.send( :define_method, :setup, p )
return MiddleGuy.clone
end
end
You can do away with the explicit “MiddleGuy” class and the clone
call, by just creating an anonymous class on each invocation:
class BigGuy
def setup
@favorites = “bluto”
end
def initialize
setup
p @favorites
end
def self.with_favorites(favorites)
Class.new(self) do
define_method(:setup) do
@favorites = favorites
end
end
end
end
And while we’re at it, if the only purpose of the on-the-fly class was
for the subclass to inherit from, why not make the subclass be the
on-the-fly class:
LittleGuy = BigGuy.with_favorites(‘popeye’)
DevilishGuy = BigGuy.with_favorites(666)
Then the following code produces the same output as yours above:
BigGuy.new #=> “bluto”
LittleGuy.new #=> “popeye”
BigGuy.new #=> “bluto”
DevilishGuy.new #=> 666
LittleGuy.new #=> “popeye”
Jacob F.