geezyx
geezyx

Reputation: 35

Ruby - Reference subclass in class method

Not sure if this is possible with Ruby, but I am trying to write a class with several methods that will be shared by a number of subclasses. This superclass will contain both class and instance methods that I want the subclasses to inherit. Assume that I will never use the superclass directly, I am only using it to abstract out methods that are repeated (thinking DRY). This involves having constants (API URIs) that will change from subclass to subclass.

Here's an example of what I am trying to do:

class Foo
  attr_accessor :param
  def initialize(param)
    @param = param
  end
  def self.get
    <SubClass>.new(self::MY_CONSTANT)
  end
end

class Bar < Foo
  MY_CONSTANT = 'Woot'
end

class Baz < Foo
  MY_CONSTANT = 'Pew Pew'
end

The behavior I want is something like this:

Bar.get
puts Bar.param # => Woot
Baz.get
puts baz.param # => Pew Pew

Is this possible? Am I approaching this completely the wrong way?

Update

Per @Nathan, self.new in the self.get method did the trick here.

[39] pry(main)> test = Bar.get
=> #<Bar:0x000001072ecc20 @param="Woot">
[40] pry(main)> test.param
=> "Woot"
[41] pry(main)> test
=> #<Bar:0x000001072ecc20 @param="Woot">
[42] pry(main)> test.class
=> Bar
[43] pry(main)> test2 = Baz.get
=> #<Baz:0x000001071b5528 @param="Pew Pew">
[44] pry(main)> test2.class
=> Baz
[45] pry(main)> test2.param
=> "Pew Pew"

Upvotes: 3

Views: 2338

Answers (1)

Nathan
Nathan

Reputation: 8026

Use self.

Inside Foo.get, self is the class you're calling get on, which would be Bar and Baz when you execute Bar.get and Baz.get, respectively.

Therefore, where you have <SubClass>, just put self, and everything will work as you expect.

def self.get
  self.new(self::MY_CONSTANT)
end

You can also omit self because that's what Ruby uses automatically when no explicit object is specified.

def self.get
  new(self::MY_CONSTANT)
end

Also, in your code where you're testing the values, I think what you intend is this:

bar = Bar.get
puts bar.param # => Woot
baz = Baz.get
puts baz.param # => Pew Pew

Finally as for whether this is the best way, that's opinion-based, but it seems odd to me that you're trying to pass the value into initialize when it's already available as a constant. In other words, if you have a method that needs to use the API URI, just reference the constant directly rather than initializing the instance itself with @param. You can reference the constant inside an instance method like this: self.class::MY_CONSTANT

Upvotes: 5

Related Questions