views:

1026

answers:

4

I need to define the constant in the module that use the method from the class that includes this module:

module B 
  def self.included(base)
    class << base
  CONST = self.find
    end
  end
end 

class A
  def self.find
    "AAA"
  end
  include B 
end

puts A::CONST

But the compiler gives the error on the 4th line.

Is there any other way to define the constant?

+2  A: 

In your specific case.

module B 
  def self.included(base)
    base.const_set("CONST", base.find)
  end
end 

class A
  def self.find
    "AAA"
  end
  include B 
end

puts A::CONST

Despite it works, it's a little bit messy. Are you sure you can't follow a different way to achieve your goal?

Simone Carletti
Yes, I reach this solution too. But that meta call is not very good idea. Is there more straight way?
Bogdan Gusiev
You are trying to dynamically assign a constant value at runtime. AFAIK, other ways will raise a "dynamic constant assignment" error.
Simone Carletti
+1  A: 

The more idiomatic way to achieve this is:

module B 
  def self.included(klass)
    klass.class_eval <<-ruby_eval
      CONST = find
    ruby_eval

    # note that the block form of class_eval won't work
    # because you can't assign a constant inside a method
  end
end

class A
  def self.find
    "AAA"
  end
  include B 
end

puts A::CONST

What you were doing (class << base) actually puts you into the context of A's metaclass, not A itself. The find method is on A itself, not its metaclass. The thing to keep in mind is that classes are themselves objects, and so have their own metaclasses.

To try to make it clearer:

class Human def parent # this method is on the Human class and available # to all instances of Human. end

 class << self
   def build
     # this method is on the Human metaclass, and
     # available to its instance, Human itself.
   end

   # the "self" here is Human's metaclass, so build
   # cannot be called.
 end

 def self.build
   # exactly the same as the above
 end

 build # the "self" here is Human itself, so build can
       # be called

end

Not sure if that helps, but if you don't understand it, you can still use the class_eval idiom above.

Yehuda Katz
A: 

How to understand the mentioned: The find method is on A itself, not its metaclass.

古井好月
A: 

module B def self.included(base) class << base CONST = self.find end end end

class A class << self def self.find "AAA" end end include B end

then the compiler error is fixed, pls try.

古井好月