First, let say that my understanding of the Ruby object model is woefully incomplete, having come from C, PHP, and Java.
Now, my application contains functionality that is shared by several models, so I’m trying to move that functionality into a module to keep things DRY. Each model has a ‘data’ attribute that needs to be serialized. However, when I move the serialize call into a module, I can’t make it work. Here is the basic model:
class MyModel < ActiveRecord::Base
include MyApp::Data
end
And here is the the module:
module MyApp
module Data
serialize :data, ActiveRecord::Coders::Hstore
def get_data(key)
data && data[key]
end
def set_data(key, value)
self.data = (data || {}).merge(key => value)
end
end
end
When I try to instantiate a model that includes MyApp:Data, I get the following error:
NoMethodError: undefined method `serialize' for MyApp::Data:Module
How do I make it so that when I instantiate MyModel, the data attribute is serialized by simply including MyApp::Data?
EDIT:
I’ve also tried this:
module MyApp
module Data
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
serialize :data, ActiveRecord::Coders::Hstore
end
def get_data(key)
data && data[key]
end
def set_data(key, value)
self.data = (data || {}).merge(key => value)
end
end
end
Unfortunately, I’m still getting the same error.
As you have written it
serializeis called when the module is loaded. The method call to serialize has no receiver so it is made on the Data module. You on the other hand want to call it on the class that is including the module.You can do this with the
self.includedhook – it is called whenever the module is included and the class that is doing the including is passed as an argument, for exampleWith activesupport you can also do
It sets up the self.included for you and evaluates the block such that self will be the including class, not the module (which can be handy if the methods you want to call happen to be protected/private – dsl stuff isn’t as readable when it’s full of
base.send :blah)