Search code examples
rubypolymorphismmultiple-inheritancesuperclass

Inherit from **another** class other than actual parent class


I've been doing some 'monkey-patching' (ahem excuse me superman-patching), like so, adding the below code and more to file(s) in my "#{Rails.root}/initializers/" folder:

module RGeo
 module Geographic
 class ProjectedPointImpl
  def to_s
    coords = self.as_text.split("(").last.split(")").first.split(" ")
    "#{coords.last}, #{coords.first}"
  end# of to_s
  def google_link
    url = "https://maps.google.com/maps?hl=en&q=#{self.to_s}"
  end
 end# of ProjectedPointImpl class
end# of Geographic module
end

I ended up realizing that there were two different _Point_ instances which I wanted to utilize these methods on (both were strings with the same formatting, i.e. Well-Known Text (WKT)), and added an exact copy of the above two methods to a certain RGeo::Geos::CAPIPointImpl class space.

I then, in my youthful, unexperienced way, after thinking about DRY (don't repeat yourself) principles, proceeded to create an ad hoc class which I presumed I might be able to inherit from for both

class Arghhh
  def to_s
    coords = self.as_text.split("(").last.split(")").first.split(" ")
      "#{coords.last}, #{coords.first}"
  end# of to_s

  def google_link
    url = "https://maps.google.com/maps?hl=en&q=#{self.to_s}"
  end
end

and told my classes to inherit from it i.e.: ProjectedPointImpl < Arghhh

I was promptly responded to by ruby with this error when I stopped and then tried to reload my rails console:

`<module:Geos>': superclass mismatch for class CAPIPointImpl (TypeError)

...

I think my naivete in trying to get CAPIPointImpl (in this case) to inherit from another class than its parent also highlights my knowledge gap on this subject very explicitly

What methods might I be able to use to practically graft extra shared methods onto two classes coming from otherwise separate parents? Does ruby allow for these types of abstract exceptions?


Solution

  • What you need to do is to define the new methods in a module which you then "mix into" the existing classes. Here's a rough sketch:

    # Existing definition of X
    class X
      def test
        puts 'X.test'
      end
    end
    
    # Existing definition of Y
    class Y
      def test
        puts 'Y.test'
      end
    end
    
    module Mixin
      def foo
        puts "#{self.class.name}.foo"
      end
    
      def bar
        puts "#{self.class.name}.bar"
      end
    end
    
    # Reopen X and include Mixin module
    class X
      include Mixin
    end
    
    # Reopen Y and include Mixin module
    class Y
      include Mixin
    end
    
    x = X.new
    x.test # => 'X.test'
    x.foo  # => 'X.foo'
    x.bar  # => 'X.bar'
    
    y = Y.new
    y.test # => 'Y.test'
    y.foo  # => 'Y.foo'
    y.bar  # => 'Y.bar'
    

    In this example we have two existing classes X and Y. We define the methods we would like to add to both X and Y in a module which I've called Mixin. We can then reopen both X and Y and include the module Mixin into them. Once that's done, both X and Y have their original methods plus the methods from Mixin.