Reputation: 8875
How does one override a class method defined in a model concern?
This is a bit tricky since you’re not really overriding a class method right? Because it’s using the concern api of definining class methods in the class_methods
block.
so say I have a concern that looks like this:
module MyConcern
extend ActiveSupport::Concern
class_methods do
def do_something
#some code
end
end
end
In model.. how would I override that method so that I could call it like we do with super when using inheritance? So in my model I’d like to go:
def self.do_something
#call module do_something
end
?
Upvotes: 4
Views: 4001
Reputation: 16793
If you've include
d MyConcern
in the model that defines self.do_something
, you should just be able to use super
:
module MyConcern
extend ActiveSupport::Concern
class_methods do
def do_something
puts "I'm do_something in the concern"
end
end
end
class UsesMyConcern < ActiveRecord::Base
include MyConcern
def self.do_something
super
end
end
UsesMyConcern.do_something
# => "I'm do_something in the concern"
If you haven't or don't want to include MyConcern
in the model and you want to invoke do_something
on the module without creating any intermediary objects, you can change your model to:
class UsesMyConcern < ActiveRecord::Base
def self.do_something
MyConcern::ClassMethods.instance_method(:do_something).bind(self).call
end
end
UsesMyConcern.do_something
# => "I'm do_something in the concern"
ActiveSupport::Concern.class_methods
defines a ClassMethods
module in the concern if there isn't one already, and that's where we can find the do_something
method.
Upvotes: 6
Reputation: 235
Why not simply call the module's method: MyConcern.do_something
?
I'm not sure if there's an easy of doing super
for modules (though I can see why that may be useful).
The next best solution could be doing something like calling #included_modules
and manually iterating with #responds_to?
:
def self.do_something
self.super_module(__method__)
end
def self.super_module(method)
self.included_modules.find { |m| m.responds_to? method }.public_send(method)
end
Upvotes: 1
Reputation: 4413
The old way using alias_method_chain
: https://ernie.io/2011/02/03/when-to-use-alias_method_chain/
The new way (requires > ruby 2.0.0) you really should use this, as there will be a DEPRECATION WARNING when using it in rails 5.0: http://paweljaniak.co.za/2014/09/30/understanding-ruby-module-prepend-and-include/
Upvotes: 0