Reputation: 1397
I am writing an application where many (but not all) ActiveRecord models have a hash
column. This is populated on creation with a random MD5 hash, and used for referencing a single object instead of its ID. To achieve this, I have included the following module in the respective models, and use find_by_id_or_hash!()
in all controllers instead of find
:
module IdOrHashFindable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
before_create :create_hash ## <-- THIS FAILS
# legacy. in use only until find by ID is phased out altogether
def find_by_id_or_hash!(id_or_hash)
id_or_hash.to_s.size >= 32 ? find_by_hash!(id_or_hash) : find(id_or_hash)
end
end
def to_param; self.hash end
def create_hash; self.hash = Support.create_hash end
end
To keep things DRY, I would like to have the before_create
call also inside the module. However, I keep getting either
undefined method `before_create' for IdOrHashFindable:Module
or
undefined method `before_create' for IdOrHashFindable::ClassMethods:Module
depending where I put it. This makes sense (after all, I am calling a function, not defining it), but I'd still like to know how this can be done. (I cannot overwrite before_create
since there are other before_create
invocations).
Also, for all models which have this module included, quite similar tests apply. How do I consistently test this feature? Do I write a custom describe ... end
block and require
it into each model_spec.rb
where it applies? How do I pass the correct model to use without resorting to global variables?
Any ideas would be appreciated!
Upvotes: 2
Views: 2022
Reputation: 1008
You must place class method invokation in class_eval
or just directly invoke it like:
module IdOrHashFindable
def self.included(base)
base.extend(ClassMethods)
base.before_create :create_hash
# or
base.class_eval do
before_create :create_hash
end
end
end
because when you place the method in the module, it will directy invoke it as the module's method
Upvotes: 6