Skip to content

Commit 73f7795

Browse files
Use alias_method for invalid names
This allows using the same code for both "specially named" and regular methods. This uses :__delegate as the name, which may be visible to users in backtraces for these methods. Co-authored-by: Luke Gruber <luke.gruber@shopify.com>
1 parent 90ffceb commit 73f7795

2 files changed

Lines changed: 37 additions & 10 deletions

File tree

lib/delegate.rb

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -400,14 +400,29 @@ def DelegateClass(superclass, &block)
400400
public_instance_methods = superclass.public_instance_methods
401401
public_instance_methods -= ignores
402402

403-
normal, special = public_instance_methods.partition { |m| m.match?(/\A[a-zA-Z]\w*[!\?]?\z/) }
403+
methods_to_define =
404+
public_instance_methods.map{|x| [x, true] } +
405+
protected_instance_methods.map{|x| [x, false] }
404406

405-
source = normal.map do |method|
406-
"def #{method}(...); __getobj__.#{method}(...); end"
407-
end
407+
source = []
408+
409+
methods_to_define.each do |target_name, is_protected|
410+
unless target_name.match?(/\A[a-zA-Z]\w*[!\?]?\z/)
411+
placeholder_name = :__delegate
412+
end
408413

409-
protected_instance_methods.each do |method|
410-
source << "def #{method}(...); __getobj__.__send__(#{method.inspect}, ...); end"
414+
send_source =
415+
if is_protected || placeholder_name
416+
"__getobj__.__send__(#{target_name.inspect}, ...)"
417+
else
418+
"__getobj__.#{target_name}(...)"
419+
end
420+
source << "def #{placeholder_name || target_name}(...); #{send_source}; end"
421+
422+
if placeholder_name
423+
source << "alias_method #{target_name.inspect}, :#{placeholder_name}"
424+
source << "remove_method :#{placeholder_name}"
425+
end
411426
end
412427

413428
klass.module_eval do
@@ -426,10 +441,6 @@ def __setobj__(obj) # :nodoc:
426441

427442
class_eval(source.join(";"), __FILE__, __LINE__)
428443

429-
special.each do |method|
430-
define_method(method, Delegator.delegating_block(method))
431-
end
432-
433444
protected(*protected_instance_methods)
434445
end
435446

test/test_delegate.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,22 @@ def test_call_visibiltiy
170170
assert_raise(NoMethodError) { obj.__send__(:parent_private) }
171171
end
172172

173+
class ClassWithInvalidName
174+
define_method(:" ") { :space }
175+
define_method(:"\t") { :tab }
176+
protected :"\t"
177+
end
178+
179+
def test_delegateclass_invalid_name
180+
delegate = DelegateClass(ClassWithInvalidName)
181+
instance = delegate.new(ClassWithInvalidName.new)
182+
assert_equal :space, instance.send(:" ")
183+
assert_equal :space, instance.__send__(:" ")
184+
185+
assert_equal :tab, instance.send(:"\t")
186+
assert_equal :tab, instance.__send__(:"\t")
187+
end
188+
173189
class IV < DelegateClass(Integer)
174190
attr_accessor :var
175191

0 commit comments

Comments
 (0)