зеркало из https://github.com/github/ruby.git
185 строки
3.8 KiB
Ruby
185 строки
3.8 KiB
Ruby
# Delegation class that delegates even methods defined in super class,
|
|
# which can not be covered with normal method_missing hack.
|
|
#
|
|
# Delegator is the abstract delegation class. Need to redefine
|
|
# `__getobj__' method in the subclass. SimpleDelegator is the
|
|
# concrete subclass for simple delegation.
|
|
#
|
|
# Usage:
|
|
# foo = Object.new
|
|
# foo2 = SimpleDelegator.new(foo)
|
|
# foo.hash == foo2.hash # => false
|
|
#
|
|
# Foo = DelegateClass(Array)
|
|
#
|
|
# class ExtArray<DelegateClass(Array)
|
|
# ...
|
|
# end
|
|
|
|
class Delegator
|
|
|
|
def initialize(obj)
|
|
preserved = ::Kernel.public_instance_methods(false)
|
|
preserved -= ["to_s","to_a","inspect","==","=~","==="]
|
|
for t in self.class.ancestors
|
|
preserved |= t.public_instance_methods(false)
|
|
preserved |= t.private_instance_methods(false)
|
|
preserved |= t.protected_instance_methods(false)
|
|
break if t == Delegator
|
|
end
|
|
preserved << "singleton_method_added"
|
|
for method in obj.methods
|
|
next if preserved.include? method
|
|
begin
|
|
eval <<-EOS
|
|
def self.#{method}(*args, &block)
|
|
begin
|
|
__getobj__.__send__(:#{method}, *args, &block)
|
|
rescue Exception
|
|
$@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
|
|
$@.delete_if{|s| /^\\(eval\\):/ =~ s}
|
|
::Kernel::raise
|
|
end
|
|
end
|
|
EOS
|
|
rescue SyntaxError
|
|
raise NameError, "invalid identifier %s" % method, caller(4)
|
|
end
|
|
end
|
|
end
|
|
alias initialize_methods initialize
|
|
|
|
def method_missing(m, *args)
|
|
target = self.__getobj__
|
|
unless target.respond_to?(m)
|
|
super(m, *args)
|
|
end
|
|
target.__send__(m, *args)
|
|
end
|
|
|
|
def respond_to?(m)
|
|
return true if super
|
|
return self.__getobj__.respond_to?(m)
|
|
end
|
|
|
|
def __getobj__
|
|
raise NotImplementedError, "need to define `__getobj__'"
|
|
end
|
|
|
|
def marshal_dump
|
|
__getobj__
|
|
end
|
|
def marshal_load(obj)
|
|
initialize_methods(obj)
|
|
end
|
|
end
|
|
|
|
class SimpleDelegator<Delegator
|
|
|
|
def initialize(obj)
|
|
super
|
|
@_sd_obj = obj
|
|
end
|
|
|
|
def __getobj__
|
|
@_sd_obj
|
|
end
|
|
|
|
def __setobj__(obj)
|
|
raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
|
|
@_sd_obj = obj
|
|
end
|
|
|
|
def clone
|
|
super
|
|
__setobj__(__getobj__.clone)
|
|
end
|
|
def dup
|
|
super
|
|
__setobj__(__getobj__.dup)
|
|
end
|
|
end
|
|
|
|
# backward compatibility ^_^;;;
|
|
Delegater = Delegator
|
|
SimpleDelegater = SimpleDelegator
|
|
|
|
#
|
|
def DelegateClass(superclass)
|
|
klass = Class.new
|
|
methods = superclass.public_instance_methods(true)
|
|
methods -= ::Kernel.public_instance_methods(false)
|
|
methods |= ["to_s","to_a","inspect","==","=~","==="]
|
|
klass.module_eval {
|
|
def initialize(obj)
|
|
@_dc_obj = obj
|
|
end
|
|
def method_missing(m, *args)
|
|
unless @_dc_obj.respond_to?(m)
|
|
super(m, *args)
|
|
end
|
|
@_dc_obj.__send__(m, *args)
|
|
end
|
|
def respond_to?(m)
|
|
return true if super
|
|
return @_dc_obj.respond_to?(m)
|
|
end
|
|
def __getobj__
|
|
@_dc_obj
|
|
end
|
|
def __setobj__(obj)
|
|
raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
|
|
@_dc_obj = obj
|
|
end
|
|
def clone
|
|
super
|
|
__setobj__(__getobj__.clone)
|
|
end
|
|
def dup
|
|
super
|
|
__setobj__(__getobj__.dup)
|
|
end
|
|
}
|
|
for method in methods
|
|
begin
|
|
klass.module_eval <<-EOS, __FILE__, __LINE__+1
|
|
def #{method}(*args, &block)
|
|
begin
|
|
@_dc_obj.__send__(:#{method}, *args, &block)
|
|
rescue
|
|
$@[0,2] = nil
|
|
raise
|
|
end
|
|
end
|
|
EOS
|
|
rescue SyntaxError
|
|
raise NameError, "invalid identifier %s" % method, caller(3)
|
|
end
|
|
end
|
|
return klass
|
|
end
|
|
|
|
if __FILE__ == $0
|
|
class ExtArray<DelegateClass(Array)
|
|
def initialize()
|
|
super([])
|
|
end
|
|
end
|
|
|
|
ary = ExtArray.new
|
|
p ary.class
|
|
ary.push 25
|
|
p ary
|
|
|
|
foo = Object.new
|
|
def foo.test
|
|
25
|
|
end
|
|
def foo.error
|
|
raise 'this is OK'
|
|
end
|
|
foo2 = SimpleDelegator.new(foo)
|
|
p foo.test == foo2.test # => true
|
|
foo2.error # raise error!
|
|
end
|