Skip to content

Instantly share code, notes, and snippets.

@TrumpClone
Created May 25, 2018 14:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TrumpClone/fbabb4dd0e837d1d63573bccdff2ae21 to your computer and use it in GitHub Desktop.
Save TrumpClone/fbabb4dd0e837d1d63573bccdff2ae21 to your computer and use it in GitHub Desktop.
# rubocop:disable all
class Module
# TODO: (@exclusive) refactor and make PR to Ruby on Rails (ActiveSupport)
# NOTE: rails => /v4.2.10/activesupport/lib/active_support/core_ext/module/delegation.rb
module FixNumberNamedMethodsDelegation
# NOTE: rails => /v4.2.10/activesupport/lib/active_support/core_ext/module/delegation.rb
def delegate(*methods)
options = methods.pop
unless options.is_a?(Hash) && to = options[:to]
raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
end
prefix, allow_nil = options.values_at(:prefix, :allow_nil)
if prefix == true && to =~ /^[^a-z_]/
raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
end
method_prefix = \
if prefix
"#{prefix == true ? to : prefix}_"
else
''
end
file, line = caller.first.split(':', 2)
line = line.to_i
to = to.to_s
to = "self.#{to}" if RUBY_RESERVED_WORDS.include?(to)
methods.each do |method|
definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
# NOTE: our fix
full_method_name = "#{method_prefix}#{method}"
# NOTE: our fix
method_definer = begin
if !!(full_method_name[0] =~ /\A\d\z/)
"define_method '#{full_method_name}' do |#{definition}|"
else
"def #{full_method_name}(#{definition})"
end
end
# NOTE: our fix
method_definer = "define_method '#{full_method_name}' do |#{definition}|"
if allow_nil
# NOTE: our fix
method_def = <<~RUBY_EVAL_CODE.squish
#{method_definer};
_ = #{to};
if !_nil? || nil.respond_to?('#{method}');
if !_.nil? || nil.respond_to?('#{method}');
_.send('#{method}', #{definition});
end;
end
RUBY_EVAL_CODE
else
exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
# NOTE: our fix
method_def = <<~RUBY_EVAL_CODE.squish
#{method_definer};
begin;
_ = #{to};
_.send('#{method}', #{definition});
rescue NoMethodError => e;
if _.nil? && e.name == '#{method}';
'#{exception}';
#{exception};
else;
raise;
end;
end;
end
RUBY_EVAL_CODE
end
else
# NOTE: old implementation
if allow_nil
method_def = [
"def #{method_prefix}#{method}(#{definition})",
"_ = #{to}",
"if !_.nil? || nil.respond_to?(:#{method})",
" _.#{method}(#{definition})",
"end",
"end"
].join ';'
else
exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
method_def = [
"def #{method_prefix}#{method}(#{definition})",
" _ = #{to}",
" _.#{method}(#{definition})",
"rescue NoMethodError => e",
" if _.nil? && e.name == :#{method}",
" #{exception}",
" else",
" raise",
" end",
"end"
].join ';'
end
end
module_eval(method_def, file, line)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment