跟踪一个简单的resources命令:

Rails.application.routes.draw do
  binding.trace_tree(htmp: 'routes/resources') do
    resources :students do
  end                                                                                                                                                                                                                                   
end


得调用栈如下

20171029_121440_332_resources.html

本质上它也只是调用了collection、member、get、post等基本的路由定义命令

def resources(*resources, &block)
  options = resources.extract_options!.dup

  if apply_common_behavior_for(:resources, resources, options, &block)
    return self
  end

  with_scope_level(:resources) do
    options = apply_action_options options
    resource_scope(Resource.new(resources.pop, api_only?, @scope[:shallow], options)) do
      yield if block_given?

      concerns(options[:concerns]) if options[:concerns]

      collection do
        get  :index if parent_resource.actions.include?(:index)
        post :create if parent_resource.actions.include?(:create)
      end

      new do
        get :new
      end if parent_resource.actions.include?(:new)

      set_member_mappings_for_resource
    end
  end

  self
end

def set_member_mappings_for_resource
  member do
    get :edit if parent_resource.actions.include?(:edit)
    get :show if parent_resource.actions.include?(:show)
    if parent_resource.actions.include?(:update)
      patch :update
      put   :update
    end
    delete :destroy if parent_resource.actions.include?(:destroy)
  end
end


但有点奇怪的是,它这样配合collection、member定义的index、create、show、update、destroy,却没有出现在url的匹配模式上,如/students/index,为什么呢?

一路跟踪到add_route,可见url模式是由path_for_action产生的

def path_for_action(action, path)
  return "#{@scope[:path]}/#{path}" if path

  if canonical_action?(action)
    @scope[:path].to_s
  else
    "#{@scope[:path]}/#{action_path(action)}"
  end
end

def canonical_action?(action)
  resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
end


根据其判断,若当前scope在RESOURCE_METHOD_SCOPES = [:collection, :member, :new]之中,且action为CANONICAL_ACTIONS = %w(index create new show update destroy)之一的时候,则匹配的路径省略该动词

但同时又发现,这里也包含了new,而现实中新建资源的页面url是带new的,唯有new并没有省略,怎么回事呢?

其实collection、member、new所创建的scope的路径一开始就不一样了

def collection                                                                              
  unless resource_scope?
    raise ArgumentError, "can't use collection outside resource(s) scope"
  end

  with_scope_level(:collection) do
    path_scope(parent_resource.collection_scope) do
      yield
    end
  end
end

def member
  unless resource_scope?
    raise ArgumentError, "can't use member outside resource(s) scope"
  end

  with_scope_level(:member) do
    if shallow?
      shallow_scope {
        path_scope(parent_resource.member_scope) { yield }
      }
    else
      path_scope(parent_resource.member_scope) { yield }
    end
  end
end

def new
  unless resource_scope?
    raise ArgumentError, "can't use new outside resource(s) scope"
  end

  with_scope_level(:new) do
    path_scope(parent_resource.new_scope(action_path(:new))) do
      yield
    end
  end
end


collection是用原资源的路径,member则是拼上:id,而new则是拼上new

alias :collection_scope :path

def member_scope
  "#{path}/:#{param}"
end

alias :shallow_scope :member_scope

def new_scope(new_path)
  "#{path}/#{new_path}"
end


所以path_for_action因canonical_action?忽略action而直接使用当前scope的path时,其实已带上new在末尾