Question

I have Ruby class into which I want to include both class and instance methods. Following the pattern described here, I'm currently using the following:

class SomeObject

  include SomeObject::Ability

  def self.some_builder_method(params)
    # use some_class_method ...
  end

end

module SomeObject::Ability

  module ClassMethods

    def some_class_method(param)
      # ...
    end

  end

  def self.included(klass)
    klass.extend(ClassMethods)
  end

  def some_instance_method
    # ...
  end

end

I'd rather not make two separate modules (one being included and the other being extended), because all the methods in my module logically fit together. On the other hand, this pattern a) requires me to define an additional ClassMethods module and b) requires me to write a boilerplate self.included method for every module.

Is there a better way to do this?

Edit 1: I've found another way, but I'm unsure if this is better than the first.

module Concern

  def included(base)

    # Define instance methods.
    instance_methods.each do |m|
      defn = instance_method(m)
      base.class_eval { define_method(m, defn) }
    end

    # Define class methods.
    (self.methods - Module.methods).each do |m|
      unless m == __method__
        base.define_singleton_method(m, &method(m))
      end
    end

  end

end

module SomeModule

  extend Concern

  def self.class_m
    puts "Class"
  end

  def instance_m
    puts "Instance"
  end

end

class Allo

  include SomeModule

end


Allo.class_m          # => "Class"
Allo.new.instance_m   # => "Instance"
Was it helpful?

Solution

If I understand you correctly, you really just want to use ActiveSupport::Concern:

module PetWorthy
  extend ActiveSupport::Concern

  included do
    validates :was_pet, inclusion: [true, 'yes']
  end

  def pet #instance method
  end

  module ClassMethods
    def find_petworthy_animal
      # ...
    end
  end
end

class Kitty
  include PetWorthy
end

Kitty.find_petworthy_animal.pet

You (hopefully obviously) don't need to use the included method if you don't have any behavior to trigger on include, but I put it in just to demonstrate.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top