Set attribute of superclass dinamically

Discussion in 'Ruby' started by Jeroen v., Apr 24, 2011.

  1. Jeroen v.

    Jeroen v. Guest

    I have the following code

    class foo
    price_per_liter = 1
    price
    module bar
    def set_price val
    self.price_per_liter = val
    end
    end
    end

    In this way I can succesfully set the price per liter of the class. How
    can I make it dunamically, so I can also set the attribute 'price'?

    I tried instance_variable_set(@variable, val), but that doesn't work.
    Probably because the variables of the class 'foo' and the module 'bar'
    lives independent of each other.

    --
    Posted via http://www.ruby-forum.com/.
     
    Jeroen v., Apr 24, 2011
    #1
    1. Advertising

  2. Re: Set attribute of superclass dynamically

    Jeroen v. wrote in post #994745:
    > I have the following code
    >
    > class foo


    That's wrong - class names must start with a capital letter

    > price_per_liter = 1


    That sets a local variable. It's only visible between the 'class' and
    'end' excluding any nested 'def', 'class' or 'module' (since each of
    those start a new scope for local variables). This is unlikely to be
    useful.

    > price


    That will raise an error - it's not a local variable so it must be a
    method call, but your class Foo object doesn't have a method 'price'
    either (i.e. def self.price ... )

    > module bar


    Error again: should be 'Bar' not 'bar'. However I'm not sure why you're
    making a module Foo::Bar - it's not a class, and it's not a subclass of
    Foo.

    > def set_price val
    > self.price_per_liter = val


    That will call method 'price_per_liter=', but you have not defined one.
    Error again.

    > end
    > end
    > end
    >
    > In this way I can succesfully set the price per liter of the class. How
    > can I make it dynamically, so I can also set the attribute 'price'?


    I'm not sure what it is you're trying to achieve. Here's a guess:

    class Foo
    def self.price_per_liter=(val)
    @price_per_liter = val
    end
    def self.price_per_liter
    @price_per_liter
    end
    end

    class Bar < Foo
    end

    Foo.price_per_liter = 133.9
    Bar.price_per_liter = 144.9

    puts Foo.price_per_liter
    puts Bar.price_per_liter

    > I tried instance_variable_set(@variable, val), but that doesn't work.
    > Probably because the variables of the class 'foo' and the module 'bar'
    > lives independent of each other.


    It's true that the two classes have separate instance variables (since
    each class is a separate object, of class Class). But you should be able
    to do

    Foo.instance_variable_set:)@price_per_liter, 133.9)

    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Apr 24, 2011
    #2
    1. Advertising

  3. Jeroen v.

    Jeroen v. Guest

    Re: Set attribute of superclass dynamically

    You're right. I wrote the example code too quick.

    In the meantime, I also found another solution that is good for me
    (however your solution would also worked):

    In the module you can do:
    self.send("#{method_name}=", value)
    This creates a setter on the fly for the superclass ;)

    --
    Posted via http://www.ruby-forum.com/.
     
    Jeroen v., Apr 24, 2011
    #3
  4. Jeroen v.

    Josh Cheek Guest

    [Note: parts of this message were removed to make it a legal post.]

    On Sun, Apr 24, 2011 at 9:29 AM, Jeroen v. <> wrote:

    > I have the following code
    >
    > class foo
    > price_per_liter = 1
    > price
    > module bar
    > def set_price val
    > self.price_per_liter = val
    > end
    > end
    > end
    >
    > In this way I can succesfully set the price per liter of the class. How
    > can I make it dunamically, so I can also set the attribute 'price'?
    >
    > I tried instance_variable_set(@variable, val), but that doesn't work.
    > Probably because the variables of the class 'foo' and the module 'bar'
    > lives independent of each other.
    >
    >

    What was the error it gave you? From here, I have a hard time believing you
    tried setting the ivar, because your code has two syntax errors (classes and
    modules must be constants) a name error (price is neither a local variable
    nor a method), and you can not set the price per liter of the class as you
    say you did, since price_per_liter is a local variable and there is no
    price_per_liter= method, anyway.


    Anyway, everything in Ruby is an object, including classes. Every object in
    Ruby has a singleton class, which is a class just for that one object (they
    are created lazily). Why is this relevant? Because methods are defined in
    classes, so defining methods on the object's singleton class will define
    methods for that one object. This is what class methods are.

    So what about modules? When you extend an object (and classes are objects)
    with a module, it gets put in the ancestry chain behind the singleton class.
    So when you ask the object for its methods, it looks in the singleton class,
    and when it doesn't find it, it goes up the chain into the module, where it
    discovers the method.

    So, we just define the #price= method in the module, then we extend the
    class with the module, and now the class has access to that method.

    # define the price method on the
    # singleton class of Foo
    class Foo
    def self.price
    @price
    end
    end

    # define the price= method as an
    # instance method of Bar
    module Bar
    def price=(new_price)
    @price = new_price
    end
    end

    # put Bar as an ancestor of
    # Foo's singleton class
    Foo.extend Bar

    # tada, everything works
    Foo.price # => nil
    Foo.price = 5
    Foo.price # => 5


    You also look like you don't understand the difference between local
    variables, instance variables, and methods. In Ruby, all instance variables
    are private, and they always begin with the @ sigil. So @price is an
    instance variable, and price is either a local variable or a method. When
    you say `price_per_liter = 1` in your code above, you are setting a local
    variable. If you want to invoke a method on self, you need
    `self.price_per_liter = 1` to disambiguate. If you want to set the instance
    variable, you need `@price_per_liter = 1`

    Note that Ruby has syntactic sugar to turn `self.price = 1` into
    `self.price=(1)`, that assignment is just a method call.
     
    Josh Cheek, Apr 24, 2011
    #4
  5. Re: Set attribute of superclass dynamically

    Jeroen v. wrote in post #994769:
    > In the module you can do:
    > self.send("#{method_name}=", value)
    > This creates a setter on the fly for the superclass ;)


    That *calls* the setter method, but only if it already exists. If it
    doesn't, it will raise an exception.

    You need to provide some code to demonstrate what you're saying.

    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Apr 24, 2011
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. JMMB

    Creating JavaScript Dinamically

    JMMB, Aug 26, 2003, in forum: ASP .Net
    Replies:
    1
    Views:
    435
    Kevin Spencer
    Aug 26, 2003
  2. VisionSet

    recast a superclass attribute

    VisionSet, Dec 3, 2003, in forum: Java
    Replies:
    4
    Views:
    539
    Daniel Bonniot
    Dec 19, 2003
  3. Replies:
    3
    Views:
    2,748
    Erick Crouse
    Aug 18, 2006
  4. Evan Klitzke
    Replies:
    0
    Views:
    398
    Evan Klitzke
    Aug 2, 2007
  5. bart van deenen
    Replies:
    6
    Views:
    822
    bart van deenen
    Mar 3, 2009
Loading...

Share This Page