summaryrefslogtreecommitdiff
path: root/lib/delegate.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/delegate.rb')
-rw-r--r--lib/delegate.rb130
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