module Roda::RodaPlugins::Base::ClassMethods

def define_roda_method(meth, expected_arity, &block)

expected arity is 0 or 1.
Roda does not support blocks with required keyword arguments if the

fixes Roda uses for regular blocks would not work for lambda blocks.
Roda only checks arity for regular blocks, not lambda blocks, as the

to adjust arity dynamically.
:check_dynamic_arity app option can be to :warn to warn if Roda needs
set to false to turn off the dynamic arity checks. The
where the arity matches. The :check_dynamic_arity app option can be
when the method is called, which can hurt performance even in the case
If the expected arity is :any, Roda must perform a dynamic arity check

cases where the arity does not match what is expected.
cases where it does not. If it is set to :warn, Roda will warn in the
the arity of the block matches the expected arity, and compensate for
If the :check_arity app option is not set to false, Roda will check that

1 (single argument), or :any (any number of arguments).
that string. The expected arity should be either 0 (no arguments),
If the name is given as a String, a unique name will be generated using
expected arity. If the name is given as a Symbol, it is used directly.
Define an instance method using the block with the provided name and
def define_roda_method(meth, expected_arity, &block)
  if meth.is_a?(String)
    meth = roda_method_name(meth)
  end
  call_meth = meth
  if (check_arity = opts.fetch(:check_arity, true)) && !block.lambda?
    required_args, optional_args, rest, keyword = _define_roda_method_arg_numbers(block)
    if keyword == :required && (expected_arity == 0 || expected_arity == 1)
      raise RodaError, "cannot use block with required keyword arguments when calling define_roda_method with expected arity #{expected_arity}"
    end
    case expected_arity
    when 0
      unless required_args == 0
        if check_arity == :warn
          RodaPlugins.warn "Arity mismatch in block passed to define_roda_method. Expected Arity 0, but arguments required for #{block.inspect}"
        end
        b = block
        block = lambda{instance_exec(&b)} # Fallback
      end
    when 1
      if required_args == 0 && optional_args == 0 && !rest
        if check_arity == :warn
          RodaPlugins.warn "Arity mismatch in block passed to define_roda_method. Expected Arity 1, but no arguments accepted for #{block.inspect}"
        end
        temp_method = roda_method_name("temp")
        class_eval("def #{temp_method}(_) #{meth =~ /\A\w+\z/ ? "#{meth}_arity" : "send(:\"#{meth}_arity\")"} end", __FILE__, __LINE__)
        alias_method meth, temp_method
        undef_method temp_method
        private meth
        alias_method meth, meth
        meth = :"#{meth}_arity"
      elsif required_args > 1
        b = block
        block = lambda{|r| instance_exec(r, &b)} # Fallback
      end
    when :any
      if check_dynamic_arity = opts.fetch(:check_dynamic_arity, check_arity)
        if keyword
          # Complexity of handling keyword arguments using define_method is too high,
          # Fallback to instance_exec in this case.
          b = block
          block = if RUBY_VERSION >= '2.7'
            eval('lambda{|*a, **kw| instance_exec(*a, **kw, &b)}', nil, __FILE__, __LINE__) # Keyword arguments fallback
          else
            # :nocov:
            lambda{|*a| instance_exec(*a, &b)} # Keyword arguments fallback
            # :nocov:
          end
        else
          arity_meth = meth
          meth = :"#{meth}_arity"
        end
      end
    else
      raise RodaError, "unexpected arity passed to define_roda_method: #{expected_arity.inspect}"
    end
  end
  define_method(meth, &block)
  private meth
  alias_method meth, meth
  if arity_meth
    required_args, optional_args, rest, keyword = _define_roda_method_arg_numbers(instance_method(meth))
    max_args = required_args + optional_args
    define_method(arity_meth) do |*a|
      arity = a.length
      if arity > required_args
        if arity > max_args && !rest
          if check_dynamic_arity == :warn
            RodaPlugins.warn "Dynamic arity mismatch in block passed to define_roda_method. At most #{max_args} arguments accepted, but #{arity} arguments given for #{block.inspect}"
          end
          a = a.slice(0, max_args)
        end
      elsif arity < required_args
        if check_dynamic_arity == :warn
          RodaPlugins.warn "Dynamic arity mismatch in block passed to define_roda_method. #{required_args} args required, but #{arity} arguments given for #{block.inspect}"
        end
        a.concat([nil] * (required_args - arity))
      end
      send(meth, *a)
    end
    private arity_meth
    alias_method arity_meth, arity_meth
  end
  call_meth
end