method
generate
v7.2.3 -
Show latest stable
- Class:
ActiveSupport::Delegation
generate(owner, methods, location: nil, to: nil, prefix: nil, allow_nil: nil, nilable: true, private: nil, as: nil, signature: nil)public
No documentation available.
# File activesupport/lib/active_support/delegation.rb, line 23
def generate(owner, methods, location: nil, to: nil, prefix: nil, allow_nil: nil, nilable: true, private: nil, as: nil, signature: nil)
unless to
raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
end
if prefix == true && /^[^a-z_]/.match?(to)
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
location ||= caller_locations(1, 1).first
file, line = location.path, location.lineno
receiver = if to.is_a?(Module)
if to.name.nil?
raise ArgumentError, "Can't delegate to anonymous class or module: #{to}"
end
unless Inflector.safe_constantize(to.name).equal?(to)
raise ArgumentError, "Can't delegate to detached class or module: #{to.name}"
end
"::#{to.name}"
else
to.to_s
end
receiver = "self.#{receiver}" if RESERVED_METHOD_NAMES.include?(receiver)
explicit_receiver = false
receiver_class = if as
explicit_receiver = true
as
elsif to.is_a?(Module)
to.singleton_class
elsif receiver == "self.class"
nilable = false # self.class can't possibly be nil
owner.singleton_class
end
method_def = []
method_names = []
method_def << "self.private" if private
methods.each do |method|
method_name = prefix ? "#{method_prefix}#{method}" : method
method_names << method_name.to_sym
# Attribute writer methods only accept one argument. Makes sure []=
# methods still accept two arguments.
definition = if signature
signature
elsif /[^\]]=\z/.match?(method)
"arg"
else
method_object = if receiver_class
begin
receiver_class.public_instance_method(method)
rescue NameError
raise if explicit_receiver
# Do nothing. Fall back to `"..."`
end
end
if method_object
parameters = method_object.parameters
if parameters.map(&:first).intersect?([:opt, :rest, :keyreq, :key, :keyrest])
"..."
else
defn = parameters.filter_map { |type, arg| arg if type == :req }
defn << "&"
defn.join(", ")
end
else
"..."
end
end
# The following generated method calls the target exactly once, storing
# the returned value in a dummy variable.
#
# Reason is twofold: On one hand doing less calls is in general better.
# On the other hand it could be that the target has side-effects,
# whereas conceptually, from the user point of view, the delegator should
# be doing one call.
if nilable == false
method_def <<
"def #{method_name}(#{definition})" <<
" (#{receiver}).#{method}(#{definition})" <<
"end"
elsif allow_nil
method = method.to_s
method_def <<
"def #{method_name}(#{definition})" <<
" _ = #{receiver}" <<
" if !_.nil? || nil.respond_to?(:#{method})" <<
" _.#{method}(#{definition})" <<
" end" <<
"end"
else
method = method.to_s
method_name = method_name.to_s
method_def <<
"def #{method_name}(#{definition})" <<
" _ = #{receiver}" <<
" _.#{method}(#{definition})" <<
"rescue NoMethodError => e" <<
" if _.nil? && e.name == :#{method}" <<
" raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
" else" <<
" raise" <<
" end" <<
"end"
end
end
owner.module_eval(method_def.join(";"), file, line)
method_names
end