ryeguy
ryeguy

Reputation: 66851

How can I get around the lack of module inheritance in Ruby?

I'm trying to create a component system in Ruby using the observer pattern. Components must be defined as modules because they exist only to be mixed in to a ComponentContainer. But there are certain methods that Components have, which I'd ideally like to define in some kind of base class, but I can't do that since they're modules.

Here's what I'd like to do:

module Component
  def self.on(event, &block)
    #definition..
  end

  def self.fire(event)
    #pass event to subscribers
  end
end

module FooComponent < Component
  on :foo_event do |param1, param2|
    #...
  end
end

The different types of Components use the on and fire methods, but they can't inherit them, because modules can't have parents. What should I do? Is this not ruby-like?

I could get this to work by making Component and FooComponent classes, but then I can't mix them into a ComponentContainer using extend or include.

Upvotes: 2

Views: 153

Answers (3)

Aaa
Aaa

Reputation: 1854

A clean way to do this is to abstract away the use of extend using the Module#included hook method. This method is called on a module with a reference to the base that is including it. What this code does is creates a Component module that automatically extends the base with the desired methods:

module Component
  def self.included(base)
    base.extend Methods
  end

  module Methods
    def on(event, &block)
      # ...
    end

    def fire(event)
      # ...
    end
  end
end

module FooComponent
  include Component

  on :foo_event do |param1, param2|
    # ...
  end
end

Upvotes: 3

J-_-L
J-_-L

Reputation: 9177

You can extend the Component's methods into your "child"-module:

module Component
  extend self # (only if you also want to allow calling via Component.on like in the original example)

  def on(event, &block)
    #definition..
  end

  def fire(event)
    #pass event to subscribers
  end
end

module FooComponent
  extend Component

  on :foo_event do |param1, param2|
    #...
  end
end

Upvotes: 2

kafuchau
kafuchau

Reputation: 5593

What if you created a separate module called BaseComponent that defined the basic methods all component modules should have, and then in your custom components, include that BaseComponent.

Then, you should still be able to mixin those custom components into your ComponentContainer class.

Upvotes: 2

Related Questions