Skip to content

Commit 4bded3c

Browse files
committed
Module#delegate avoid creating a unique fstring for each delegator
For example: ```ruby delegate :negative?, to: :value, as: Numeric ``` Before: ``` def negative?(&block) _ = @value _.negative?(&block) rescue NoMethodError => e if _.nil? && e.name == :negative? raise DelegationError, "ActiveSupport::Duration#negative? delegated to @value.negative?, but @value is nil: #{self.inspect}" else raise end end ``` After: ```ruby def negative?(&block) _ = @value _.negative?(&block) rescue NoMethodError => e if _.nil? && e.name == :negative? raise DelegationError.nil_target(:negative?, :"@value") else raise end end ``` Before almost every delegator would generate a large unique string that gets interned for the error message that is rarely if ever used. Rather than to "hardcode" a unique string, we now only pass pre-existing symbols to a method helper that will build the error message. This alone saves about 160B per delegator, and the method bytecode is also marginally smaller (but it's harder to assess how much this actually saves)
1 parent 9dab3e4 commit 4bded3c

File tree

1 file changed

+9
-3
lines changed

1 file changed

+9
-3
lines changed

activesupport/lib/active_support/core_ext/module/delegation.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55
class Module
66
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
77
# option is not used.
8-
class DelegationError < NoMethodError; end
8+
class DelegationError < NoMethodError
9+
class << self
10+
def nil_target(method_name, target) # :nodoc:
11+
new("#{method_name} delegated to #{target}, but #{target} is nil")
12+
end
13+
end
14+
end
915

1016
RUBY_RESERVED_KEYWORDS = %w(__ENCODING__ __LINE__ __FILE__ alias and BEGIN begin break
1117
case class def defined? do else elsif END end ensure false for if in module next nil
@@ -266,7 +272,7 @@ def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil, as: n
266272
" _.#{method}(#{definition})" <<
267273
"rescue NoMethodError => e" <<
268274
" if _.nil? && e.name == :#{method}" <<
269-
%( raise DelegationError, "#{self}##{method_name} delegated to #{receiver}.#{method}, but #{receiver} is nil: \#{self.inspect}") <<
275+
" raise DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
270276
" else" <<
271277
" raise" <<
272278
" end" <<
@@ -347,7 +353,7 @@ def method_missing(method, ...)
347353
if #{allow_nil == true}
348354
nil
349355
else
350-
raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
356+
raise DelegationError.nil_target(method, :'#{target}')
351357
end
352358
else
353359
raise

0 commit comments

Comments
 (0)