lib/meta_presenter/base/delegate_all_to.rb
require 'active_support/concern'
require 'active_support/core_ext/class/attribute'
module MetaPresenter
class Base
# Give the presenter the ability to delegate methods to an object
module DelegateAllTo
extend ActiveSupport::Concern
included do
# Name of an object that when specified will delegate to
# all incoming methods send to the presenter that
# we not already handled by the presenter otherwise
# (such as explicitly defining the method)
class_attribute :delegate_all_to, instance_reader: false, instance_writer: false
include InstanceMethods
end
module InstanceMethods # :nodoc:
# Check to see whether a method has been either
# defined by or is delegated by this presenter
#
# @param *args method name and the other arguments
def respond_to_missing?(*args)
method_name = args.first
delegate_all_responds_to?(method_name) || super
end
private
def delegate_all_responds_to?(method_name)
delegate_all_to? && delegate_all_to.respond_to?(method_name)
end
# Use metaprogramming to delegate all methods
def method_missing(method_name, *args, &block)
# If `delegate_all_to` has been set up for the method name
# then delegate to it, otherwise pass it up the food chain
if !delegating_all_to? && delegate_all_responds_to?(method_name)
delegate_all_to.send(method_name, *args, &block)
else
super
end
end
def delegate_all_to
# Temporarily set a flag that we are delegating
# to an underlying method. this allows us
# to chain additional methods calls onto the end
@delegating = true
send(self.class.delegate_all_to)
ensure
# Cleanup the flag afterwards to close the door behind us
@delegating = false
end
def delegating_all_to?
@delegating == true
end
def delegate_all_to?
self.class.delegate_all_to.present?
end
end
end
end
end