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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With