Dynamic code generation

T

Thiago Arrais

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 =3D FirstChild.new
fst.redefinable_method =3D> "This is the custom string for the FirstChild c=
lass"

snd =3D SecondChild.new
snd.redefinable_method =3D> "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 Arrais
 
L

Logan Capaldo

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 Arrais

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
 
T

Timothy Goddard

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
 
T

Thiago Arrais

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 Arrais
 
T

Thiago Arrais

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 Arrais
 
L

Logan Capaldo

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 Arrais

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
 
T

Thiago Arrais

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 Arrais
 
M

Michael Trier

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

irb(main):003:0> t =3D Time.at(0)
=3D> Wed Dec 31 19:00:00 Eastern Standard Time 1969
irb(main):004:0> t
=3D> Wed Dec 31 19:00:00 Eastern Standard Time 1969
irb(main):005:0> t.class
=3D> Time
irb(main):006:0> t.class.now
=3D> 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
 
T

Thiago Arrais

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

irb(main):003:0> t =3D Time.at(0)
=3D> Wed Dec 31 19:00:00 Eastern Standard Time 1969
irb(main):004:0> t
=3D> Wed Dec 31 19:00:00 Eastern Standard Time 1969
irb(main):005:0> t.class
=3D> Time
irb(main):006:0> t.class.now
=3D> 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 =3D Time.now
=3D> 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 Arrais
 
L

Logan Capaldo

--Apple-Mail-21-429504684
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
format=flowed


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


--Apple-Mail-21-429504684--
 

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

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top