Class: Police::DataFlow::ProxyBase
- Inherits:
- BasicObject
- Defined in:
- lib/police/dataflow/proxy_base.rb
Overview
Base class for labeled objects replacements.
Direct Known Subclasses
Class Attribute Summary collapse
-
.__police_classes__ ⇒ Object
The classes of the labels supported by the proxy class.
Instance Attribute Summary collapse
-
#__police_class__ ⇒ Object
readonly
The actual class of a proxy object.
-
#__police_labels__ ⇒ Object
readonly
The Label instances attached to the proxied object.
-
#__police_proxied__ ⇒ Object
readonly
The object being proxied by this object.
-
#__police_stickies__ ⇒ Object
readonly
Attached Label instances whose classes are sticky.
Instance Method Summary collapse
-
#initialize(proxied, proxy_class, label_set) ⇒ ProxyBase
constructor
Creates a proxied object.
-
#method_missing(name, *args, &block) ⇒ Object
Handles method calls to the proxied object.
-
#respond_to_missing?(name, include_private) ⇒ Boolean
Called when Object#respond_to? returns false.
Constructor Details
#initialize(proxied, proxy_class, label_set) ⇒ ProxyBase
Creates a proxied object.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/police/dataflow/proxy_base.rb', line 49 def initialize(proxied, proxy_class, label_set) @__police_proxied__ = proxied @__police_labels__ = label_set # Cache the sticky labels to speed up certain computations. @__police_stickies__ = {} @__police_labels__.each do |class_id, instance_set| label_class = instance_set.first.first.class next unless label_class.sticky? # NOTE: the set of instances is intentionally shared with # @__police_labels__ so updates to the former are automatically # reflected in the stickies cache @__police_stickies__[class_id] = instance_set end # Holds the object's class, because Object#class is not available. @__police_class__ = proxy_class end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
Handles method calls to the proxied object.
Whenever possible, proxy methods are created on-the-fly, so that future calls to the same method will be faster.
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/police/dataflow/proxy_base.rb', line 72 def method_missing(name, *args, &block) # Build a fast path for future method calls, if possible. respond_to_missing? name, true # NOTE: going out of our way to use the fast path methods, because they # handle native method argument unwrapping correctly if @__police_class__.method_defined?(name) || @__police_class__.private_method_defined?(name) return __send__(name, *args, &block) end if block return_value = @__police_proxied__.__send__ name, *args do |*yield_args| # Yielded values filtering. @__police_labels__.each do |_, label_hash| label_class = label_hash.first.first.class if hook = label_class.yield_args_hook(name) label_hash.each do |label, _| yield_args = label.__send__ hook, self, yield_args, *args end elsif label_class.sticky? label_hash.each do |label, _| yield_args.map! { |arg| ::Police::DataFlow.label arg, label } end end end yield_return = yield(*yield_args) # TODO(pwnall): consider adding a yield value filter next yield_return end else return_value = @__police_proxied__.__send__ name, *args end # Return value filtering. @__police_labels__.each do |_, label_hash| label_class = label_hash.first.first.class if hook = label_hash.first.first.class.return_hook(name) label_hash.each do |label, _| return_value = label.__send__ hook, return_value, self, *args end elsif label_class.sticky? label_hash.each do |label, _| return_value = ::Police::DataFlow.label return_value, label end end end return return_value end |
Class Attribute Details
.__police_classes__ ⇒ Object
The classes of the labels supported by the proxy class.
This is a Police::DataFlow implementation detail. Do not read it directly.
166 167 168 |
# File 'lib/police/dataflow/proxy_base.rb', line 166 def __police_classes__ @__police_classes__ end |
Instance Attribute Details
#__police_class__ ⇒ Object (readonly)
The actual class of a proxy object.
This is necessary because the class method for a proxy object must return the proxied object’s class.
Use the Police::DataFlow API instead of reading this attribute directly.
37 38 39 |
# File 'lib/police/dataflow/proxy_base.rb', line 37 def __police_class__ @__police_class__ end |
#__police_labels__ ⇒ Object (readonly)
The Label instances attached to the proxied object.
This is a Hash whose keys are the __id__s of the Class objects for the labels’ classes. The values are sets of labels that are instances of the corresponding label classes. Sets are implemented as Hashes mapping the label instances to boolean true values.
Use the Police::DataFlow API instead of reading this attribute directly.
22 23 24 |
# File 'lib/police/dataflow/proxy_base.rb', line 22 def __police_labels__ @__police_labels__ end |
#__police_proxied__ ⇒ Object (readonly)
The object being proxied by this object.
Use the Police::DataFlow API instead of reading this attribute directly.
11 12 13 |
# File 'lib/police/dataflow/proxy_base.rb', line 11 def __police_proxied__ @__police_proxied__ end |
#__police_stickies__ ⇒ Object (readonly)
Attached Label instances whose classes are sticky.
This is a proxying implementation detail and should not be relied on.
28 29 30 |
# File 'lib/police/dataflow/proxy_base.rb', line 28 def __police_stickies__ @__police_stickies__ end |
Instance Method Details
#respond_to_missing?(name, include_private) ⇒ Boolean
Called when Object#respond_to? returns false.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/police/dataflow/proxy_base.rb', line 124 def respond_to_missing?(name, include_private) return false unless @__police_proxied__.respond_to? name, include_private # A method on the proxied object doesn't have a corresponding proxy. # Fix this by creating all possible proxies. # NOTE: this approach is cheaper than creating proxies one by one, because # it plays nice with method caches ::Police::DataFlow::Proxying.add_instance_methods @__police_class__, @__police_proxied__.class # NOTE: we don't want to create unnecessary singleton classes # target_methods = @__police_proxied__.singleton_methods true # unless target_methods.empty? # ::Police::DataFlow::Proxying.add_singleton_methods self, # @__police_proxied__, target_methods # end true end |