Constant inheritance

  • Thread starter David Heinemeier Hansson
  • Start date
D

David Heinemeier Hansson

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
 
J

Jim Driscoll

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.


Jim
 
D

David Heinemeier Hansson

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.
 
D

Dan Doel

class A
X = 0
def X
X
end
end

p A.new.X

class B < A
A::X = 10
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
 
J

Jim Driscoll

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.

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
 
D

Dan Doel

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
 
D

David Heinemeier Hansson

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
 
R

Robert Klemme

Jim Driscoll said:
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
 
J

Joel VanderWerf

David said:
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?
 
J

Joe Mason

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
 

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,770
Messages
2,569,586
Members
45,084
Latest member
HansGeorgi

Latest Threads

Top