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!
You must place class method invokation in
class_evalor just directly invoke it like:because when you place the method in the module, it will directy invoke it as the module’s method