class Docile::FallbackContextProxy
@api private
A proxy object with a primary receiver as well as a secondary fallback receiver.
Will attempt to forward all method calls first to the primary receiver, and then to the fallback receiver if the primary does not handle that method.
This is useful for implementing DSL evaluation in the context of an object.
@see Docile.dsl_eval
rubocop:disable Style/MissingRespondToMissing
Constants
- NON_FALLBACK_METHODS
The set of methods which will not fallback from the block’s context to the dsl object.
- NON_PROXIED_INSTANCE_VARIABLES
The set of instance variables which are local to this object and hidden. All other instance variables will be copied in and out of this object from the scope in which this proxy was created.
- NON_PROXIED_METHODS
The set of methods which will not be proxied, but instead answered by this object directly.
Public Class Methods
@param [Object] receiver the primary proxy target to which all methods
initially will be forwarded
@param [Object] fallback the fallback proxy target to which any methods
not handled by `receiver` will be forwarded
# File lib/docile/fallback_context_proxy.rb, line 46 def initialize(receiver, fallback) @__receiver__ = receiver @__fallback__ = fallback # Enables calling DSL methods from helper methods in the block's context unless fallback.respond_to?(:method_missing) # NOTE: We could switch to {#define_singleton_method} on current Rubies singleton_class = (class << fallback; self; end) # instrument {#method_missing} on the block's context to fallback to # the DSL object. This allows helper methods in the block's context to # contain calls to methods on the DSL object. singleton_class. send(:define_method, :method_missing) do |method, *args, &block| m = method.to_sym if !NON_FALLBACK_METHODS.member?(m) && !fallback.respond_to?(m) && receiver.respond_to?(m) receiver.__send__(method.to_sym, *args, &block) else super(method, *args, &block) end end if singleton_class.respond_to?(:ruby2_keywords, true) singleton_class.send(:ruby2_keywords, :method_missing) end # instrument a helper method to remove the above instrumentation singleton_class. send(:define_method, :__docile_undo_fallback__) do singleton_class.send(:remove_method, :method_missing) singleton_class.send(:remove_method, :__docile_undo_fallback__) end end end
Public Instance Methods
@return [Array<Symbol>] Instance variable names, excluding
{NON_PROXIED_INSTANCE_VARIABLES}
# File lib/docile/fallback_context_proxy.rb, line 85 def instance_variables super.reject { |v| NON_PROXIED_INSTANCE_VARIABLES.include?(v) } end
Proxy all methods, excluding {NON_PROXIED_METHODS}, first to ‘receiver` and then to `fallback` if not found.
# File lib/docile/fallback_context_proxy.rb, line 91 def method_missing(method, *args, &block) if @__receiver__.respond_to?(method.to_sym) @__receiver__.__send__(method.to_sym, *args, &block) else begin @__fallback__.__send__(method.to_sym, *args, &block) rescue NoMethodError => e e.extend(BacktraceFilter) raise e end end end