[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Dynamic code generation

Thiago Arrais

4/1/2006 5:04:00 PM

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


11 Answers

Logan Capaldo

4/1/2006 9:17:00 PM

0


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

> 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



Timothy Goddard

4/1/2006 11:01:00 PM

0

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

Thiago Arrais

4/2/2006 4:04:00 AM

0

On 4/1/06, Timothy Goddard <interfecus@gmail.com> 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 Arrais


Thiago Arrais

4/2/2006 4:16:00 AM

0

On 4/1/06, Logan Capaldo <logancapaldo@gmail.com> 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 Arrais


Logan Capaldo

4/2/2006 8:45:00 PM

0


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

> On 4/1/06, Logan Capaldo <logancapaldo@gmail.com> 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 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



Thiago Arrais

4/3/2006 2:56:00 PM

0

On 4/2/06, Logan Capaldo <logancapaldo@gmail.com> 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 Arrais


Michael Trier

4/3/2006 4:55:00 PM

0

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


Thiago Arrais

4/3/2006 5:14:00 PM

0

On 4/3/06, Michael Trier <mtrier@gmail.com> wrote:
> 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.

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 Arrais


Michael Trier

4/3/2006 6:01:00 PM

0

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

Michael


Logan Capaldo

4/3/2006 7:36:00 PM

0


On Apr 3, 2006, at 10:55 AM, Thiago Arrais 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