Rails: динамически определять метод класса, основанный на имени родительского класса в модуле/проблема

Я хочу динамически генерировать метод класса в Mixin на основе имени класса, которое включает этот Mixin.

Вот мой текущий код:

module MyModule  
  extend ActiveSupport::Concern  

  # def some_methods  
  #   ...  
  # end  

  module ClassMethods  

    # Here is where I'm stuck...
    define_method "#{self.name.downcase}_status" do  
      # do something...  
    end  

  end  
end  

class MyClass < ActiveRecord::Base  
  include MyModule  
end  

# What I'm trying to achieve:
MyClass.myclass_status

Но это дает мне следующее имя метода:

MyClass.mymodule::classmethods_status  

Получение имени базового класса внутри определения метода работает (self, self.name...), но я не могу заставить его работать для имени метода...

До сих пор я пробовал

define_method "#{self}"
define_method "#{self.name"
define_method "#{self.class}"
define_method "#{self.class.name}"
define_method "#{self.model_name}"
define_method "#{self.parent.name}"

Но ничто из этого не похоже на трюк:/

Есть ли способ получить имя базового класса (не уверен, что вызывать класс, который включает мой модуль). Я уже несколько часов борюсь с этой проблемой, и я не могу понять, как это сделать: (

Спасибо!

Ответ 1

Вы не можете сделать это так - на данный момент еще не известно, какие классы (или классы) включают модуль.

Если вы определяете метод self.included, он будет вызываться каждый раз, когда модуль включен, и предмет, выполняющий включение, будет передан в качестве аргумента. Альтернативно, поскольку вы используете AS:: Concern, вы можете сделать

included do 
  #code here is executed in the context of the including class
end

Ответ 2

Я нашел чистое решение: используя define_singleton_method (доступно в ruby ​​v1.9.3)

module MyModule  
  extend ActiveSupport::Concern  

  included do
    define_singleton_method "#{self.name}_status" do
      # do stuff
    end
  end

  # def some_methods  
  #   ...  
  # end  

  module ClassMethods  
    # Not needed anymore!
  end  
end  

Ответ 3

Вы можете сделать что-то вроде этого:

module MyModule
  def self.included(base)
    (class << base; self; end).send(:define_method, "#{base.name.downcase}_status") do
      puts "Hey!"
  end

  base.extend(ClassMethods)
end

  module ClassMethods
    def other_method
      puts "Hi!"
    end
  end
end

class MyClass
  include MyModule
end

MyClass.myclass_status
MyClass.other_method

Ответ 4

Работает для extend:

module MyModule  
  def self.extended who
    define_method "#{who.name.downcase}_status" do
      p "Inside"
    end
  end
end  

class MyClass  
  extend MyModule  
end  

MyClass.myclass_status