Indeed, alias_method
is another possibility! And also Jean's suggestion of defining the delegator methods in a module.
Unfortunately, my suggestion of evaluating the block in a module does not work for metaprogramming within the block, such as in this test.
I had not really dug into DelegateClass
to understand why the super
in Delegate1
still works after redefining foo
. I see now that it's because the class is a subclass of Delegator
, which also responds to foo
via method_missing
. Therefore, redefining foo
causes super
to fall back to a slower execution path.
So I benchmarked the solutions with this script:
# frozen_string_literal: true
require "benchmark/ips"
$LOAD_PATH.prepend(".../delegate/lib")
require "delegate"
Base = Class.new { def foo; end }
Overridden = DelegateClass(Base) { def foo; super; end }
overridden = Overridden.new(Base.new)
Benchmark.ips do |x|
x.report("overridden") { overridden.foo }
end
With this alias_method
patch:
index 70d4e4a..af95c86 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -412,10 +412,12 @@ def DelegateClass(superclass, &block)
end
protected_instance_methods.each do |method|
define_method(method, Delegator.delegating_block(method))
+ alias_method(method, method)
protected method
end
public_instance_methods.each do |method|
define_method(method, Delegator.delegating_block(method))
+ alias_method(method, method)
end
end
klass.define_singleton_method :public_instance_methods do |all=true|
the result is:
Warming up --------------------------------------
overridden 76.674k i/100ms
Calculating -------------------------------------
overridden 765.489k (± 0.9%) i/s - 3.834M in 5.008559s
With this delegator methods module patch:
index 70d4e4a..4311bb5 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -410,6 +410,8 @@ def DelegateClass(superclass, &block)
__raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj)
@delegate_dc_obj = obj
end
+ end
+ klass.include(Module.new do
protected_instance_methods.each do |method|
define_method(method, Delegator.delegating_block(method))
protected method
@@ -417,7 +419,7 @@ def DelegateClass(superclass, &block)
public_instance_methods.each do |method|
define_method(method, Delegator.delegating_block(method))
end
- end
+ end)
klass.define_singleton_method :public_instance_methods do |all=true|
super(all) | superclass.public_instance_methods
end
the result is:
Warming up --------------------------------------
overridden 183.938k i/100ms
Calculating -------------------------------------
overridden 1.830M (± 0.9%) i/s - 9.197M in 5.026683s
Comparison: the alias_method
solution is 2.39x slower than the delegator methods module solution.
Is there another reason to prefer the alias_method
solution?