diff options
Diffstat (limited to 'lib/delegate.rb')
| -rw-r--r-- | lib/delegate.rb | 130 |
1 files changed, 80 insertions, 50 deletions
diff --git a/lib/delegate.rb b/lib/delegate.rb index 03ebfddf4a..0cc3ddb1b0 100644 --- a/lib/delegate.rb +++ b/lib/delegate.rb @@ -21,6 +21,8 @@ # SimpleDelegator's implementation serves as a nice example of the use of # Delegator: # +# require 'delegate' +# # class SimpleDelegator < Delegator # def __getobj__ # @delegate_sd_obj # return object we are delegating to, required @@ -37,10 +39,13 @@ # Be advised, RDoc will not detect delegated methods. # class Delegator < BasicObject + # The version string + VERSION = "0.6.1" + kernel = ::Kernel.dup kernel.class_eval do alias __raise__ raise - [:to_s, :inspect, :=~, :!~, :===, :<=>, :hash].each do |m| + [:to_s, :inspect, :!~, :===, :<=>, :hash].each do |m| undef_method m end private_instance_methods.each do |m| @@ -73,16 +78,16 @@ class Delegator < BasicObject end # - # Handles the magic of delegation through \_\_getobj\_\_. + # Handles the magic of delegation through +__getobj__+. # - def method_missing(m, *args, &block) + ruby2_keywords def method_missing(m, *args, &block) r = true target = self.__getobj__ {r = false} - if r && target.respond_to?(m) + if r && target_respond_to?(target, m, false) target.__send__(m, *args, &block) elsif ::Kernel.method_defined?(m) || ::Kernel.private_method_defined?(m) - ::Kernel.instance_method(m).bind(self).(*args, &block) + ::Kernel.instance_method(m).bind_call(self, *args, &block) else super(m, *args, &block) end @@ -90,22 +95,39 @@ class Delegator < BasicObject # # Checks for a method provided by this the delegate object by forwarding the - # call through \_\_getobj\_\_. + # call through +__getobj__+. # def respond_to_missing?(m, include_private) r = true target = self.__getobj__ {r = false} - r &&= target.respond_to?(m, include_private) - if r && include_private && !target.respond_to?(m, false) + r &&= target_respond_to?(target, m, include_private) + if r && include_private && !target_respond_to?(target, m, false) warn "delegator does not forward private method \##{m}", uplevel: 3 return false end r end + KERNEL_RESPOND_TO = ::Kernel.instance_method(:respond_to?) # :nodoc: + private_constant :KERNEL_RESPOND_TO + + # Handle BasicObject instances + private def target_respond_to?(target, m, include_private) + case target + when Object + target.respond_to?(m, include_private) + else + if KERNEL_RESPOND_TO.bind_call(target, :respond_to?) + target.respond_to?(m, include_private) + else + KERNEL_RESPOND_TO.bind_call(target, m, include_private) + end + end + end + # # Returns the methods available to this delegate object as the union - # of this object's and \_\_getobj\_\_ methods. + # of this object's and +__getobj__+ methods. # def methods(all=true) __getobj__.methods(all) | super @@ -113,7 +135,7 @@ class Delegator < BasicObject # # Returns the methods available to this delegate object as the union - # of this object's and \_\_getobj\_\_ public methods. + # of this object's and +__getobj__+ public methods. # def public_methods(all=true) __getobj__.public_methods(all) | super @@ -121,7 +143,7 @@ class Delegator < BasicObject # # Returns the methods available to this delegate object as the union - # of this object's and \_\_getobj\_\_ protected methods. + # of this object's and +__getobj__+ protected methods. # def protected_methods(all=true) __getobj__.protected_methods(all) | super @@ -154,7 +176,7 @@ class Delegator < BasicObject end # - # Delegates ! to the \_\_getobj\_\_ + # Delegates ! to the +__getobj__+ # def ! !__getobj__ @@ -165,7 +187,7 @@ class Delegator < BasicObject # method calls are being delegated to. # def __getobj__ - __raise__ ::NotImplementedError, "need to define `__getobj__'" + __raise__ ::NotImplementedError, "need to define '__getobj__'" end # @@ -173,11 +195,11 @@ class Delegator < BasicObject # to _obj_. # def __setobj__(obj) - __raise__ ::NotImplementedError, "need to define `__setobj__'" + __raise__ ::NotImplementedError, "need to define '__setobj__'" end # - # Serialization support for the object returned by \_\_getobj\_\_. + # Serialization support for the object returned by +__getobj__+. # def marshal_dump ivars = instance_variables.reject {|var| /\A@delegate_/ =~ var} @@ -201,8 +223,8 @@ class Delegator < BasicObject end end - def initialize_clone(obj) # :nodoc: - self.__setobj__(obj.__getobj__.clone) + def initialize_clone(obj, freeze: nil) # :nodoc: + self.__setobj__(obj.__getobj__.clone(freeze: freeze)) end def initialize_dup(obj) # :nodoc: self.__setobj__(obj.__getobj__.dup) @@ -210,35 +232,12 @@ class Delegator < BasicObject private :initialize_clone, :initialize_dup ## - # :method: trust - # Trust both the object returned by \_\_getobj\_\_ and self. - # - - ## - # :method: untrust - # Untrust both the object returned by \_\_getobj\_\_ and self. - # - - ## - # :method: taint - # Taint both the object returned by \_\_getobj\_\_ and self. - # - - ## - # :method: untaint - # Untaint both the object returned by \_\_getobj\_\_ and self. - # - - ## # :method: freeze - # Freeze both the object returned by \_\_getobj\_\_ and self. + # Freeze both the object returned by +__getobj__+ and self. # - - [:trust, :untrust, :taint, :untaint, :freeze].each do |method| - define_method method do - __getobj__.send(method) - super() - end + def freeze + __getobj__.freeze + super() end @delegator_api = self.public_instance_methods @@ -259,6 +258,8 @@ end # end # end # +# require 'delegate' +# # class UserDecorator < SimpleDelegator # def birth_year # born_on.year @@ -347,7 +348,7 @@ def Delegator.delegating_block(mid) # :nodoc: lambda do |*args, &block| target = self.__getobj__ target.__send__(mid, *args, &block) - end + end.ruby2_keywords end # @@ -398,6 +399,17 @@ def DelegateClass(superclass, &block) protected_instance_methods -= ignores public_instance_methods = superclass.public_instance_methods public_instance_methods -= ignores + + normal, special = public_instance_methods.partition { |m| m.match?(/\A[a-zA-Z]\w*[!\?]?\z/) } + + source = normal.map do |method| + "def #{method}(...); __getobj__.#{method}(...); end" + end + + protected_instance_methods.each do |method| + source << "def #{method}(...); __getobj__.__send__(#{method.inspect}, ...); end" + end + klass.module_eval do def __getobj__ # :nodoc: unless defined?(@delegate_dc_obj) @@ -406,24 +418,42 @@ def DelegateClass(superclass, &block) end @delegate_dc_obj end + def __setobj__(obj) # :nodoc: __raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj) @delegate_dc_obj = obj end - protected_instance_methods.each do |method| - define_method(method, Delegator.delegating_block(method)) - protected method - end - public_instance_methods.each do |method| + + class_eval(source.join(";"), __FILE__, __LINE__) + + special.each do |method| define_method(method, Delegator.delegating_block(method)) end + + protected(*protected_instance_methods) end + klass.define_singleton_method :public_instance_methods do |all=true| super(all) | superclass.public_instance_methods end klass.define_singleton_method :protected_instance_methods do |all=true| super(all) | superclass.protected_instance_methods end + klass.define_singleton_method :instance_methods do |all=true| + super(all) | superclass.instance_methods + end + klass.define_singleton_method :public_instance_method do |name| + super(name) + rescue NameError + raise unless self.public_instance_methods.include?(name) + superclass.public_instance_method(name) + end + klass.define_singleton_method :instance_method do |name| + super(name) + rescue NameError + raise unless self.instance_methods.include?(name) + superclass.instance_method(name) + end klass.module_eval(&block) if block return klass end |
