I am having an ActiveRecord model with a polymorphic association like this:
class Reach < ActiveRecord::Base
belongs_to :reachable, :polymorphic => true
end
This model acts like a proxy. What I need to do is to forward all method calls on that object to the associated object :reachable
. I think delegate
won't help here because I have to explicitly name all the methods I need to delegate. I need something like delegate :all
to delegate all methods (not all
method).
There are two things you can do here:
The slower (performance-wise) but easier method is to use method_missing:
class Reach < ActiveRecord::Base def method_missing(method, *args) return reachable.send(method, *args) if reachable.respond_to?(method) super end end
The faster performing method would be to define each method dynamically that you want to delegate:
class Reach < ActiveRecord::Base [:all, :my, :methods, :here].each do |m| define_method(m) do |*args| reachable.send(m, *args) end end end
You could even use that method in a more dynamic manner, if you wanted, by taking the Reach class, finding the methods that are defined on it and it alone, and defining only those on Reachable. I would do it by hand though because there are some you probably won't want to include.
Since Rails 5.1+ you can delegate everything not implemented with delegate_missing_to :reachable
Basically, do what you expect. You could read more on the Api Doc
If you are stuck in a previous version then just recommend using the method_missing
from @Veraticus answer , is less performance-wise as mentioned but I think is the more flexible approach.
For Rails, I did the following:
class User < ApplicationRecord
has_one :member
delegate (Member.new.attributes.keys - User.new.attributes.keys), to: :member
end
the - User.new...
is to not override existing attributes on User
(eg, created_at
)
I'm not sure how this approach would work with polymorphism, however.
I have found a neat way to approach the problem using refinements. There already is a class in the standard library that allows the delegation of every method call to a target object. Delegator
and by extension SimpleDelegator
Now there is a way to insert SimpleDelegator
into your inheritance chain without inheriting from it directly using refinements:
def self.include_delegator
mod = Module.new do
include refine(SimpleDelegator) { yield if block_given? }
end
self.send :include, mod
end
include_delegator
Now in order to take advantage of SimpleDelegator
set the delegation target in an after initialize callback like so:
after_initialize do |instance|
__setobj__(instance.reachable)
end
This is equivalent to inheriting directly from SimpleDelegator and setting the delegation in the construction, there is no manual housekeeping of methods to delegate and you can avoid using method missing.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.