Dynamic code generation

I have been trying to do some simple dynamic code generation in Ruby.
What I am specifically trying is to redefine a class level method on
the subclasses. I know I may achieve that by using a simple def, but I
would like come up with a simpler syntax, something like this:


class Parent
def self.redefinable_method
return “This is the default string returned when the method
isn’t redefined”
end

def redefinable_method
    return self.class.redefinable_method
end

end

class FirstChild < Parent
redefine “This is the custom string for the FirstChild class”
end

class SecondChild < Parent; end

fst = FirstChild.new
fst.redefinable_method => “This is the custom string for the FirstChild
class”

snd = SecondChild.new
snd.redefinable_method => “This is the default string returned when
the method isn’t redefined”

In this example you can see two points: (a) I am a total newbie, (b)
the method ‘redefine’ should be defined on the parent class, but I
don’t have a clue on how it should be written. I have taken a look at
ActiveRecord’s associations, but it seems like overkill to use the
exact same approach.

Two questions:

Is it possible to do that?
Can the client code (child classes) be made any simpler/shorter?

Cheers,

Thiago A.

On Apr 1, 2006, at 12:03 PM, Thiago A. wrote:

end

class SecondChild < Parent; end
In this example you can see two points: (a) I am a total newbie, (b)
Cheers,

Thiago A.

This seems to be a solution looking for a problem
class Parent
def redefinable_method
“This is the default string returned when the method isn’t
redefined”
end
end

class FirstChild < Parent
def redefinable_method
“This is the custom string for the FirstChild class”
end
end

class SecondChild < Parent; end

I don;t see what you gain by meta-programming here

Firstly you confuse things too much by using the same name as an
instance and class method. There’s probably an easier way but try this:

class ParentClass
def self.say
“This is a message”
end

def self.make_it_say(message)
class_methods = Module.new do
define_method :say do
message
end
end
self.extend(class_methods)
end
end

class ChildClass < ParentClass
make_it_say “I’ve been redefined!”
end

puts ParentClass.say
puts ChildClass.say

On 4/1/06, Timothy G. [email protected] wrote:

Firstly you confuse things too much by using the same name as an
instance and class method.

I thought too, code just seems too cluttered. The effect I was trying
to achieve was calling class methods from instances, but Ruby doesn’t
allow it. Maybe just calling class methods from instances is bad form.

Anyway, your idea helped me to solve my problem. I don’t think I will
even need the class method anymore. define_method is exactly what I
was looking for.

By the way, we don’t need to do the module thing. define_method can be
called inside make_it_say directly, like this:


class Parent
def self.make_it_say(message)
define_method :say do
message
end
end

def say
    'This is the default message'
end

end

Child and Parent code seem pretty clean to me now. Thanks.

Cheers,

Thiago A.

On 4/1/06, Logan C. [email protected] wrote:

I don;t see what you gain by meta-programming here

I need a method to identify the child classes, something like
Class#name, just more detailed. All instances of subclasses of the
parent class need to respond to that method, but the method returns
the same thing for all instances: a class identifier. Most classes
will respond in mostly the same way, but some will need customization.

Maybe a (almost) real example will help:


class Parent

def identifier
    return "This is class #{self.class.name}"
end

end

class ChildUsingDefaultIdentifier; end

class ChildUsingCustomIdentifier

def identifier
    return 'Instances of this class identify them on a totally

different way’
end

end

Sure we could just override the method in the subclasses that needed
to override it (like we did above), but it is just an identifier and a
whole def block seemed too much.

Regards,

Thiago A.

On 4/2/06, Logan C. [email protected] wrote:

This seems like a job for class constants:

This is what it seemed at first to me too. But I would like the
instances to respond to a method, not the classes.

The expected behavior is like this

$ tail identifiers.rb

calling the identifier method on the instances, not the classes

puts A.new.name
puts B.new.name
puts C.new.name
puts A.new.name

$ ruby identifiers.rb
Class A
Class A
Class C
Class A

I wouldn’t like to override a method just for returning a different
ID. I have solved the problem with the approach described on the
previous message. The question is: is there a simpler way? Maybe using
class constants?

Cheers,

Thiago A.

Take with a grain of salt, because I don’t really understand this
stuff. But, couldn’t you do:

irb(main):003:0> t = Time.at(0)
=> Wed Dec 31 19:00:00 Eastern Standard Time 1969
irb(main):004:0> t
=> Wed Dec 31 19:00:00 Eastern Standard Time 1969
irb(main):005:0> t.class
=> Time
irb(main):006:0> t.class.now
=> Mon Apr 03 12:53:14 Eastern Standard Time 2006
irb(main):007:0>

I’m calling the class level method of the instance variable. Is that
what you’re looking for?

Michael

On Apr 1, 2006, at 11:15 PM, Thiago A. wrote:

Sure we could just override the method in the subclasses that needed
to override it (like we did above), but it is just an identifier and a
whole def block seemed too much.

Regards,

Thiago A.

This seems like a job for class constants:

% cat identifiers.rb
class A
ID = “Class A”
end

class B < A # uses default id
end

class C < A # uses a custom id
ID = “Class C”
end

puts A::ID
puts B::ID
puts C::ID
puts A::ID

% ruby identifiers.rb
Class A
Class A
Class C
Class A

On 4/3/06, Michael T. [email protected] wrote:

=> Mon Apr 03 12:53:14 Eastern Standard Time 2006
irb(main):007:0>

I’m calling the class level method of the instance variable.

No, you are calling the class level method of the class variable.
The message now goes to the object returned by t.class (the Time
class), not the object ‘t’. You would be calling the class level
method of the instance variable if you did something like this

irb(main):005:0> t = Time.now
=> Mon Apr 03 14:07:59 BRT 2006
irb(main):006:0> t.now
NoMethodError: undefined method `now’ for Mon Apr 03 14:07:59 BRT
2006:Time
from (irb):6
from :0

Which, indeed, fails miserably. But if we think a little, this is
actually a feature, not a bug. In order to call a method on the class
object, we need to send a message directly to it. Messages to the
instances are handled by them.

Other languages actually allow this kind of auto-delegation. It would
be interesting to know what people think about it.

Is that
what you’re looking for?

It was one of the solutions I came up with, but it didn’t survive very
long. See previous posts.

Thanks,

Thiago A.

Got it. I misunderstood what you were trying to achieve.

Michael

On Apr 3, 2006, at 10:55 AM, Thiago A. wrote:

The question is: is there a simpler way? Maybe using
class constants?

Is this simpler enough?

% cat identifiers.rb
class A
ID = “Class A”
def name
self.class::ID
end
end

class B < A # uses default id
end

class C < A # uses a custom id
ID = “Class C”
end

puts A.new.name
puts B.new.name
puts C.new.name

% ruby identifiers.rb
Class A
Class A
Class C

On 4/3/06, Logan C. [email protected] wrote:

Is this simpler enough?

Yup. I think I am going with this one.

Thanks,

Thiago A.