Railsのxxx_pathとかxxx_urlの引数を調べた

リファレンス読んでもいまいちわからなかったので、そういうときはRailsそのもののソースを読むに限る。

_pathgrepしてみると、

# actionpack/lib/action_dispatch/routing/route_set.rb

  def add(name, route)
    key       = name.to_sym
    path_name = :"#{name}_path"
    url_name  = :"#{name}_url"

    if routes.key? key
      @path_helpers_module.send :undef_method, path_name
      @url_helpers_module.send  :undef_method, url_name
    end
    routes[key] = route
    define_url_helper @path_helpers_module, route, path_name, route.defaults, name, PATH
    define_url_helper @url_helpers_module,  route, url_name,  route.defaults, name, UNKNOWN

    @path_helpers << path_name
    @url_helpers << url_name
  end

bundle exec rake routes したときに出てくるルート情報っぽいものを作ってる場所なんだけど、そこで define_url_helper ってのがあった。きっとこれだ。

# actionpack/lib/action_dispatch/routing/route_set.rb

  # Create a URL helper allowing ordered parameters to be associated
  # with corresponding dynamic segments, so you can do:
  #
  #   foo_url(bar, baz, bang)
  #
  # Instead of:
  #
  #   foo_url(bar: bar, baz: baz, bang: bang)
  #
  # Also allow options hash, so you can do:
  #
  #   foo_url(bar, baz, bang, sort_by: 'baz')
  #
  def define_url_helper(mod, route, name, opts, route_key, url_strategy)
    helper = UrlHelper.create(route, opts, route_key, url_strategy)
    mod.module_eval do
      define_method(name) do |*args|
        last = args.last
        options = \
          case last
          when Hash
            args.pop
          when ActionController::Parameters
            args.pop.to_h
          end
        helper.call self, args, options
      end
    end
  end

ここでさらに UrlHelper というのが出てくる。

# actionpack/lib/action_dispatch/routing/route_set.rb

  def initialize(route, options, route_name, url_strategy)
    @options      = options
    @segment_keys = route.segment_keys.uniq
    @route        = route
    @url_strategy = url_strategy
    @route_name   = route_name
  end

  def call(t, args, inner_options)
    controller_options = t.url_options
    options = controller_options.merge @options
    hash = handle_positional_args(controller_options,
                                  inner_options || {},
                                  args,
                                  options,
                                  @segment_keys)

    t._routes.url_for(hash, route_name, url_strategy)
  end

handle_positional_args… (そろそろメタっぽいコードを見るのが辛くなってきた)

# actionpack/lib/action_dispatch/routing/route_set.rb

  def handle_positional_args(controller_options, inner_options, args, result, path_params)
    if args.size > 0
      # take format into account
      if path_params.include?(:format)
        path_params_size = path_params.size - 1
      else
        path_params_size = path_params.size
      end

      if args.size < path_params_size
        path_params -= controller_options.keys
        path_params -= result.keys
      end
      inner_options.each_key do |key|
        path_params.delete(key)
      end

      args.each_with_index do |arg, index|
        param = path_params[index]
        result[param] = arg if param
      end
    end

    result.merge!(inner_options)
  end

本気で困ったときにもう一回見直すことにしよう・・。