Class: Docile::FallbackContextProxy Private

Inherits:
Object
  • Object
show all
Defined in:
lib/docile/fallback_context_proxy.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

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.

rubocop:disable Style/MissingRespondToMissing

See Also:

Direct Known Subclasses

ChainingFallbackContextProxy

Constant Summary collapse

NON_PROXIED_METHODS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

The set of methods which will not be proxied, but instead answered by this object directly.

Set[:__send__, :object_id, :__id__, :==, :equal?,
:!, :!=, :instance_exec, :instance_variables,
:instance_variable_get, :instance_variable_set,
:remove_instance_variable]
NON_FALLBACK_METHODS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

The set of methods which will not fallback from the block's context to the dsl object.

Set[:class, :self, :respond_to?, :instance_of?]
NON_PROXIED_INSTANCE_VARIABLES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

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.

Set[:@__receiver__, :@__fallback__]

Instance Method Summary collapse

Constructor Details

#initialize(receiver, fallback) ⇒ FallbackContextProxy

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of FallbackContextProxy.

Parameters:

  • receiver (Object)

    the primary proxy target to which all methods initially will be forwarded

  • fallback (Object)

    the fallback proxy target to which any methods not handled by receiver will be forwarded



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 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

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Proxy all methods, excluding NON_PROXIED_METHODS, first to receiver and then to fallback if not found.



91
92
93
94
95
96
97
98
99
100
101
102
# 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

Instance Method Details

#instance_variablesArray<Symbol>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns Instance variable names, excluding NON_PROXIED_INSTANCE_VARIABLES.

Returns:



85
86
87
# File 'lib/docile/fallback_context_proxy.rb', line 85

def instance_variables
  super.reject { |v| NON_PROXIED_INSTANCE_VARIABLES.include?(v) }
end