Constant inheritance

Is there anyway to let a subclass overwrite a constant in the
superclass? As in getting B.new.X in the example below to return 0. If
not are there any similar constructions that would do something
comparable? (short of converting the constant to a method).

irb(main):001:0> class A
irb(main):002:1> X = 0
irb(main):003:1> def X
irb(main):004:2> X
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> A.new.X
=> 0
irb(main):008:0> class B < A
irb(main):009:1> X = 10
irb(main):010:1> end
=> 10
irb(main):011:0> B.new.X
=> 0

···


David Heinemeier Hansson.
http://www.loudthinking.com/ – Broadcasting Brain

You could just use a class variable (e.g. @@x) instead, the downside
being that it would be variable rather than constant.

Jim

···

On 25 Jan 2004, at 11:11, David Heinemeier Hansson wrote:

Is there anyway to let a subclass overwrite a constant in the
superclass? As in getting B.new.X in the example below to return 0. If
not are there any similar constructions that would do something
comparable? (short of converting the constant to a method).

class A
X = 0
def X
X
end
end

p A.new.X

class B < A
end

p B.new.X

This works, but you get a warning: “prac.rb:13: warning: already
initialized constant X”

Constants technically aren’t constant in Ruby.

  • Dan
···

A::X = 10

You could just use a class variable (e.g. @@x) instead, the downside
being that it would be variable rather than constant.

Right. The problem is that this really is a constant. The previous
example was a bit abstract. This should be more telling of my
intentions:

class Subscription # abstract
def price_with_discount
PRICE * 0.9
end
end

class BasicSubscription < Subscription
PRICE = 19
end

class PlusSubscription < Subscription
PRICE = 49
end

…but perhaps you’re right. A class variable would probably suffice.

···


David Heinemeier Hansson.
http://www.loudthinking.com/ – Broadcasting Brain

“Jim Driscoll” j@rjimlad.org schrieb im Newsbeitrag
news:E66A8EC6-4F28-11D8-AE61-000393D47484@rjimlad.org

Is there anyway to let a subclass overwrite a constant in the
superclass? As in getting B.new.X in the example below to return 0. If
not are there any similar constructions that would do something
comparable? (short of converting the constant to a method).

You could just use a class variable (e.g. @@x) instead, the downside
being that it would be variable rather than constant.

And the other downside that It would not have the desired effect:

irb(main):001:0> class Foo
irb(main):002:1> @@val = “yeah!”
irb(main):003:1> def val; @@val; end
irb(main):004:1> end
=> nil
irb(main):005:0> Foo.new.val
=> “yeah!”
irb(main):006:0> class Bar < Foo
irb(main):007:1> @@val = “huh”
irb(main):008:1> end
=> “huh”
irb(main):009:0> Foo.new.val
=> “huh”
irb(main):010:0>

IOW the subclass would change the value also for the parent class’s
instance. Something that you normally don’t want for constants.

Regards

robert
···

On 25 Jan 2004, at 11:11, David Heinemeier Hansson wrote:

Okay, try this then (referring explicitly to the current namespace):

irb(main):001:0> class A
irb(main):002:1> X=2
irb(main):003:1> def X
irb(main):004:2> self.class.const_get :X
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> class B < A
irb(main):008:1> end
=> nil
irb(main):009:0> class C < A
irb(main):010:1> X=3
irb(main):011:1> end
=> 3
irb(main):012:0> A.new.X
=> 2
irb(main):013:0> C.new.X
=> 3
irb(main):014:0> B.new.X
=> 2
irb(main):015:0>

Jim

···

On 25 Jan 2004, at 11:31, David Heinemeier Hansson wrote:

You could just use a class variable (e.g. @@x) instead, the downside
being that it would be variable rather than constant.

Right. The problem is that this really is a constant. The previous
example was a bit abstract. This should be more telling of my
intentions:

…but perhaps you’re right. A class variable would probably suffice.

Actually, this is better accomplished with class instance variables:

class Subscription
def price_with_discount
class << self.class
@price
end * 0.9
end
end

class BasicSubscription < Subscription
class << self
@price = 19
end
end

class PlusSubscription < Subscription
class << self
@price = 49
end
end

p BasicSubscription.new.price_with_discount
p PlusSubscription.new.price_with_discount

Using @@class variables will change the price of all subscriptions when
it’s set, so
all subscriptions would end up being $49.

You can also change the @price above to PRICE to use class instance
constants,
which is probably what you want.

  • Dan

David Heinemeier Hansson wrote:

class Subscription # abstract
def price_with_discount
PRICE * 0.9
self.class::PRICE * 0.9
end
end

class BasicSubscription < Subscription
PRICE = 19
end

class PlusSubscription < Subscription
PRICE = 49
end

p BasicSubscription.new.price_with_discount # ==> 17.1

I’m not sure how this solution fares in ruby 2.0, which has new constant
scoping rules. Anybody know?

You could just use a class variable (e.g. @@x) instead, the downside
being that it would be variable rather than constant.

Right. The problem is that this really is a constant. The previous
example was a bit abstract. This should be more telling of my
intentions:

class Subscription # abstract
def price_with_discount
PRICE * 0.9
end
end

class BasicSubscription < Subscription
PRICE = 19
end

class PlusSubscription < Subscription
PRICE = 49
end

Doesn’t answer the original question, but I’d disagree that PRICE should
be a constant anyway. It’s not a fundamental property of the
subscription, it’s a number you set on it based on economic conditions.
You’ll want to be able to raise and lower it as economics change.

(If that makes you feel any better about taking the easy way out.)

…but perhaps you’re right. A class variable would probably suffice.

Joe

···

In article 0AB1E374-4F2A-11D8-84B3-000A958E6254@loudthinking.com, David Heinemeier Hansson wrote:

Okay, try this then (referring explicitly to the current namespace):

Excellent! This way I can still refer to the class constant like
BasicSubscription::PRICE. Thanks…

/ David