Rails如何自定义默认资源路由路径?

6

简而言之

我想改变Rails资源路由的默认行为,将所有资源的create路径移动到/resources/new的POST,而不是/resources


设置

让我们假设已经指定了一个资源路由:

# routes.rb

resources :events

实际生成的路由如下:
$ rake routes

    Prefix Verb   URI Pattern                Controller#Action
    events GET    /events(.:format)          events#index
           POST   /events(.:format)          events#create
 new_event GET    /events/new(.:format)      events#new
edit_event GET    /events/:id/edit(.:format) events#edit
     event GET    /events/:id(.:format)      events#show
           PATCH  /events/:id(.:format)      events#update
           PUT    /events/:id(.:format)      events#update
           DELETE /events/:id(.:format)      events#destroy

请注意,create操作由对/events路径的POST触发。


现在,如果我想更改该路径,我可以按资源自行进行:

# routes.rb

# I've placed the routes in this order, and used `as: "new_event"`,
# to avoid generating an `events_new` path helper.

post 'events/new' => 'events#create', as: "new_event"
resources :events, except: [:create]

以下为生成的路由:
$ rake routes

    Prefix Verb   URI Pattern                Controller#Action
 new_event POST   /events/new(.:format)      events#create
    events GET    /events(.:format)          events#index
           GET    /events/new(.:format)      events#new
edit_event GET    /events/:id/edit(.:format) events#edit
     event GET    /events/:id(.:format)      events#show
           PATCH  /events/:id(.:format)      events#update
           PUT    /events/:id(.:format)      events#update
           DELETE /events/:id(.:format)      events#destroy

太好了!现在create操作由/events/new路径的POST触发,而不是/events路径。
每个其他的路由/助手都与以前完全一样,包括对/events/new的GET和new_event路径/url助手的支持。
问题
是否有一种方法可以仅更改用于该特定操作的默认路径,而不必手动覆盖每个create操作?
如果无法实现上述功能,则我可以使用哪些其他手段来更改一堆资源路由,以便将其创建操作移动到如上所示的/new
谢谢!

5
我猜你需要进行monkey patch resources来完成这个操作,但我__强烈建议__你_不要_这样做 - Rails是关于约定的,这会让其他开发人员在阅读你的代码时感到很困惑。另一种更明确的方法是使用包含修改后的post操作的routing concern,并在每个resources调用中添加except: :create - m_x
5
此外,您还可以定义一个custom_resources方法,它会自动执行您手动执行的操作,并在过程中调用原始的resources方法。 - m_x
你在第二条评论中提出的建议是我最终采取的。它运行得足够好。如果你想将其重写为答案,我会标记它为正确的。 - ivanreese
1个回答

2

您可以通过添加一个模块来简化您的生活,该模块可以在一个地方为您执行覆盖操作。例如:

module MyResources
  def my_resources(resource_name, opts = {}, &block)
    opts = opts.merge(except: [:create])

    resources(resource_name, opts, &block)
    post "#{resource_name}/new" => "#{resource_name}#create", as: "new_{resource_name}"    
  end
end

ActionDispatch::Routing::Mapper.__send__ :include, MyResources

然后在你的routes.rb文件中,你可以这样做:

my_resources :events

你会建议将这样的模块放在哪里?在路由文件中?比如说在app/services中?还是在lib中?或者在初始化程序中?另外,我对Ruby不是很熟悉。你使用__send__的方式是否合适?这似乎像是在针对一个猴子进行修补,可能违背了它的意愿。 - ivanreese

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接