Home > OS >  Ruby: why doesn't calling super.method work from subclass
Ruby: why doesn't calling super.method work from subclass

Time:10-03

I apologize up front. I'm going to struggle articulating this question.

TL;DR - I have an object I'm using to call a method in the subclass. That method needs access to a method in a parent class (forcing inheritance...read below). I'm thinking this won't work because I'm instantiating the subclass, so the inheritance won't work right, but I'm not sure. I'm still seeking out documentation. Basically, the method in the parent class is not "seen" the way I'm doing this - NoMethodError is the exception.

I prefer DRY code, as most people do. I usually use compositing in lieu of inheritance in my code, but I think I'm at a point where if I want to keep this DRY, I have to use inheritance (though I could be wrong, so I'm open to suggestions), and so I'm trying it out, which leads me to this question.

Given the following Ruby "pseudo" code or example to kind of demonstrate what I'm trying to accomplish:

module SomeModule
  class ParentClass
    def initialize
    end

    def method_i_want_to_use(arg1, *args)
      # does all the things
    end

    def self.method_i_want_to_use(arg1, *args)
      arg = args.first unless args.empty?
      self.class.method_i_want_to_use(arg1, arg)
    end
  end
end

And then in a different file, same module

module SomeModule
  class SubClass < ParentClass
    def initialize
    end

    # this isn't working
    def my_other_method
      # things get done and then
      method_i_want_to_use(arg1, args)  ## <<=== fails
    end
  end
end

Yet in another file

module SomeModule
  class Thing
    def initialize
      @my_obj = SubClass.new
    end

    def my_method
      @my_obj.my_other_method
    end
  end
end

So one important thing I missed. The method method_i_want_to_use is a method that is used all over the place in my code. It just so happens that in this one class, inheritance was NOT originally used because this class is basically atomic with the exception of this one method. So my problem is either I copy the method into this class and use it (but that kinda breaks the DRY principle sorta) or I find a way to share this method between classes.

This gets into OOP design pretty heavily and I am aware of that. One could ask: well, is the inheritance as it currently sits even relevant to the objects in question? Yes...and no. They can be. In short, principally, it works, but frankly, I don't like it. TBH, I almost prefer to just copy the method into the "subclass" and remove the inheritance and be done with it, but DRY -- unless I'm going a little too wild with DRY in this context and I kinda think I am.

Anyway, just curious what folks with more knowledge than I have for me on this. This really is the first time I've dabbled this deeply into inheritance. :) I'd love pointers on how I can keep from implementing

CodePudding user response:

There are two different methods here:

an instance method:

    def method_i_want_to_use(arg1, *args)
      # does all the things
    end

and a class method:

   def self.method_i_want_to_use(arg1, *args)
      arg = args.first unless args.empty?
      self.class.method_i_want_to_use(arg1, arg)
    end

but what you probably want in this case is

 def self.method_i_want_to_use(arg1, *args)
      arg = args.first unless args.empty?
      self.new.method_i_want_to_use(arg1, arg)
    end

There are a few choices and it depends on what method_i_want_to_use is doing. Is it a separate thing? Then you can call it as a class method ParentClass.method_i_want_to_use inside the SubClass without inheritance.

Another way is to define it in a module and include it

include ModuleX

# and then in your code
method_i_want_to_use(...)

I'd use inheritance if you want to have some kind of common abstraction layer and you expect multiple subclasses to behave the same way. If the classes/objects that need to use method_i_want_to_use have different behaviours then inheritance is not the correct choice. Let's say you have a class that send a request to a 3rd party API and you have a class that does saves records to your db. For some reason you need to use the same piece of code (a method) in both cases, maybe to calculate some value. Using inheritance to include the method would be a mistake, because both classes have different behaviours.

Hope that helps.

CodePudding user response:

After fixing some of the syntax errors and changing the call self.class.method_i_want_to_use to self.new.method_i_want_to_use as Adam also mentioned in his answer, this code seems to work fine.

I did not get any undefined methods until I tried to call SomeModule::ParentClass.method_i_want_to_use(3,4) and that was fixed by the change from class to new. Are you sure your undefined method error was not related to that?

module SomeModule
  class ParentClass
    def initialize
    end

    def method_i_want_to_use(arg1, *args)
      # does all the things
      puts "here #{arg1} , #{args}"
    end

    def self.method_i_want_to_use(arg1, *args)
      arg = args.first unless args.empty?
      self.new.method_i_want_to_use(arg1, arg)
    end
  end
end


module SomeModule
  class SubClass < ParentClass
    def initialize
    end

    # this isn't working
    def my_other_method(arg1, arg2)
      # things get done and then
      method_i_want_to_use(arg1, arg2)  ## <<=== fails
    end
  end
end

module SomeModule
  class Thing
    def initialize
      @my_obj = SubClass.new
    end

    def my_method(arg1,arg2)
      @my_obj.my_other_method(arg1, arg2)
    end
  end
end


SomeModule::Thing.new.my_method(1,2)
SomeModule::ParentClass.method_i_want_to_use(3,4)

prints:
here 1 , [2]
here 3 , [4]
  • Related