Forum: Ruby Dynamic code generation

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B1d59a804bd67487c964bc505a8eb892?d=identicon&s=25 Thiago Arrais (Guest)
on 2006-04-01 19:04
(Received via mailing list)
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
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-04-01 23:19
(Received via mailing list)
On Apr 1, 2006, at 12:03 PM, Thiago Arrais wrote:

>     end
> class SecondChild < Parent; end
> In this example you can see two points: (a) I am a total newbie, (b)
> 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
Be223e60c56535a0e465b84243aeb0d1?d=identicon&s=25 Timothy Goddard (Guest)
on 2006-04-02 01:06
(Received via mailing list)
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
B1d59a804bd67487c964bc505a8eb892?d=identicon&s=25 Thiago Arrais (Guest)
on 2006-04-02 06:05
(Received via mailing list)
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
B1d59a804bd67487c964bc505a8eb892?d=identicon&s=25 Thiago Arrais (Guest)
on 2006-04-02 06:18
(Received via mailing list)
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
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-04-02 22:45
(Received via mailing list)
On Apr 1, 2006, at 11:15 PM, Thiago Arrais 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 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
B1d59a804bd67487c964bc505a8eb892?d=identicon&s=25 Thiago Arrais (Guest)
on 2006-04-03 16:57
(Received via mailing list)
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
455ac2a64d06dc8461f4d258d7f7e980?d=identicon&s=25 Michael Trier (Guest)
on 2006-04-03 18:57
(Received via mailing list)
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
B1d59a804bd67487c964bc505a8eb892?d=identicon&s=25 Thiago Arrais (Guest)
on 2006-04-03 19:15
(Received via mailing list)
On 4/3/06, Michael Trier <mtrier@gmail.com> 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 Arrais
455ac2a64d06dc8461f4d258d7f7e980?d=identicon&s=25 Michael Trier (Guest)
on 2006-04-03 20:01
(Received via mailing list)
Got it.  I misunderstood what you were trying to achieve.

Michael
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-04-03 21:36
(Received via mailing list)
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
B1d59a804bd67487c964bc505a8eb892?d=identicon&s=25 Thiago Arrais (Guest)
on 2006-04-03 21:58
(Received via mailing list)
On 4/3/06, Logan Capaldo <logancapaldo@gmail.com> wrote:
> Is this simpler enough?

Yup. I think I am going with this one.

Thanks,

Thiago Arrais
This topic is locked and can not be replied to.