First of all your module structure is a bit unidiomatic. To define instance methods, we can simply put a def inside the module. To define class methods, one would typically define a nested module ClassMethods and call base.extend in the include hook. It saves a level of indentation - this doesn't seem like much but it can add up to quite awkward code. The following modules are equivalent:
module A
def self.included(base)
base.class_eval do
def instance_method
puts 'Hello from instance A!'
end
def self.class_method
puts 'Hello from class A!'
end
foo() # code evaluated in class body
end
end
end
module B
def self.included(base)
base.class_eval do
foo()
end
base.extend ClassMethods
end
def instance_method
puts 'Hello from instance B!'
end
module ClassMethods
def class_method
puts 'Hello from class B!'
end
end
end
If you're using Rails or require 'active_support/concern' you can even extend ActiveSupport::Concern to reduce the amount of boilerplate code:
module C
extend ActiveSupport::Concern
included do
foo()
end
def instance_method
puts 'Hello from instance B!'
end
module ClassMethods
def class_method
puts 'Hello from class B!'
end
end
end
The next thing you need to understand is the difference between instance variables and class instance variables. For example this is how to set an instance variable and then retrieve it in context of the instance
class C
def set_value
@x = 123
end
def get_value
@x
end
end
And this is how to set a class instance variable and then retrieve it in context of the instance. If you think of class D as instance of the class Class, then the term "class instance variable" makes kind of sense. I think the simplest way to access a class instance variable from an instance of the class is to define an attr_accessor in the class' Eigenclass.
class D
class << self
attr_accessor :x
end
def self.set_value
self.x = 123 # self refers to the class, we need to use self.x
end
def get_value
self.class.x # self refers to the instance, we need to use self.class.x
end
end
All of these hints result in the following code:
module ObjectInquiry
def self.included(base)
base.class_eval do
class << self
attr_accessor :evaluator
end
end
base.extend ClassMethods
end
def method_missing(method, *args, &block)
self.class.evaluator.call
end
module ClassMethods
def inquiry(method_name = nil, &block)
self.evaluator = block if block_given?
end
end
end
Testing:
class Post
include ObjectInquiry
inquiry do
true
end
end
Post.new.flabbergasted?
#=> true
@evaluatoryou set is on class level, the@evaluatoryou try to read is on instance level. The class and the instance are two different objects and therefore the two@evaluatorare different variables.inquiryyou set the class instance variable@evaluator. When you callmethod_missingyou sendcallto the uniititalized instance variable with the same name. The two variables are as different as@dogand@cat.