Defining class variables inside class_eval inside Module#included
mongodb, ruby
Solution
One method would be to create accessor methods for the class variable.
module Persist
def self.included(base)
# Adds class methods from 'ClassMethods' to including class.
base.extend(ClassMethods)
base.class_eval do
self.collection = Connection.new.db('nameofdb').collection(self.to_s.downcase)
# ...
end
end
module ClassMethods
def collection=(value)
@@collection = value
end
def collection
@@collection
end
end
# Instance methods follow
def find
self.class.collection.find().first
#...
end
end
class User
include Persist
end
class Post
include Persist
end
Another method would be to access the class variables of the including class in the module via the accessors class_variable_set, etc.
def self.included(base)
base.class_eval do
class_variable_set('@@collection', Connection.new.db('nameofdb').collection(self.to_s.downcase))
# …
end
end
I'll take a stab at answering your question "How is this possible? Isn't the context inside the class_eval block that of the class."
The class_eval method does make self refer to the class it's being invoked on inside the given block. This allows you to invoke class methods, etc. However, class variables will be evaluated in the context that the block is bound to - here, the module.
For instance, try doing this:
class Foo
@@bar = 1
end
Foo.class_eval { puts @@bar }
This would result in the exception "NameError: uninitialized class variable @@bar in Object". Here the given block is bound to the context of "Object", the top-level namespace.
Problem
How does one define class variables inside a class_eval block? I have the following: ``` module Persist def self.included(base) # base is the class including this module base.class_eval do # class context begin @@collection = Connection.new.db('nameofdb').collection(self.to_s.downcase) def self.get id # Class method #... end end end # Instance methods follow def find @@collection.find().first #... end end class User include Persist end class Post include Persist end ``` The classes User and Post both show `:get` when introspecting using `User.methods` or `Post.methods`. Which makes sense, as they are defined in the context of class_eval and is exactly what I need. Similarly the method `:find` is shown as an instance_method of the individual classes. However, what I thought was a class variable i.e. `@@collection`, turns out to be a Module level class_variable. When I introspect `User.class_variables` or `Post.class_variables`, they turn up empty. However `Persist.class_variables` shows `:@@collection`. How is this possible? Isn't the context inside the `class_eval` block that of the class. So shouldn't the variable `@@collection` get defined on the class and not the module? Also, the value of `@@collection` is always the last Class's name that included it. So in this case it is always 'posts' and never 'users'. I think this is because it's a module level variable, it will change on every include. Is this correct? Finally how would I define a class variable in this context so that each class has it's own `@@collection` definition.