Railsで一度に全部処理するときのRoutes問題

TODO管理をするとして「すべてのタスクを削除したい」とか「すべてのタスクを終了にしたい」っていう要件、普通によくありますね。
これって簡単なくせに「どうあるべきなの?」と迷うことが多くて悩んでいたところ、@tkawaさんがサクッと解決してくれたのでここに残しておきます。
考え方と使われ方
すべてのリソースに対するアクションなのでDELETE /tasks
とかPUT /tasks
で済ませたいというREST脳が働きますね。
どうやって使いたいか、というと、こんな感じ。
app/views/tasks/index.html.erb
普段はHamlですけど、今回はERBで(手抜き)
<h1>Listing Tasks</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Memo</th>
<th>Done</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @tasks.each do |task| %>
<tr>
<td><%= task.name %></td>
<td><%= task.memo %></td>
<td><%= task.done %></td>
<td><%= link_to 'Show', task %></td>
<td><%= link_to 'Edit', edit_task_path(task) %></td>
<td><%= link_to 'Destroy', task, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Task', new_task_path %>
<%= link_to 'Destroy All', tasks_path, method: :delete %>
<%= link_to 'Done All', tasks_path(task: {done: true}), method: :put %>
もちろん注目すべきはココですね。
<%= link_to 'New Task', new_task_path %>
<%= link_to 'Destroy All', tasks_path, method: :delete %>
<%= link_to 'Done All', tasks_path(task: {done: true}), method: :put %>
はい、これがシンプルです。
これがやりたいです。
これにしましょう。
Routesの設計
まず「すべて削除」「すべて更新」をconfig/routes.rb
で表現します。
これはtasks
というリソースに対するDELETE
およびPUT
操作ですので、次のように書きました。
config/routes.rb
Rails.application.routes.draw do
resources :tasks
delete :tasks, to: 'tasks#destroy_all'
put :tasks, to: 'tasks#update_all'
root to: 'tasks#index'
end
これがどのようなURLになるか、見てみましょう。
$ rake routes
Prefix Verb URI Pattern Controller#Action
tasks GET /tasks(.:format) tasks#index
task /tasks(.:format) tasks#create
new_task GET /tasks/new(.:format) tasks#new
edit_task GET /tasks/:id/edit(.:format) tasks#edit
task GET /tasks/:id(.:format) tasks#show
PATCH /tasks/:id(.:format) tasks#update
PUT /tasks/:id(.:format) tasks#update
DELETE /tasks/:id(.:format) tasks#destroy
DELETE /tasks(.:format) tasks#destroy_all
PUT /tasks(.:format) tasks#update_all
root GET / tasks#index
注目はココ。
DELETE /tasks(.:format) tasks#destroy_all
PUT /tasks(.:format) tasks#update_all
とってもスッキリですね!
なお、昔は次のように書いていました。
# モヤモヤver
Rails.application.routes.draw do
resources :tasks do
collection do
delete :destroy_all
put :update_all
end
end
root to: 'tasks#index'
end
これでも同じように使えるとは思いますが、Generateされるhelperにすごいモヤモヤ感がありました。なので、いまでは最初に書いたやり方にしています。
# モヤモヤver
destroy_all_posts DELETE /posts/destroy_all(.:format) posts#destroy_all
update_all_posts PUT /posts/update_all(.:format) posts#update_all
コントローラ側の実装
さて、コントローラを実装します。 ここでは大雑把にやりますが、実際に作りこむ際は次の点を注意するようにしましょう。
app/controllers/tasks_controller.rb
class TasksController < ApplicationController
# ...
def destroy_all
Task.destroy_all
redirect_to root_path
end
def update_all
Task.update_all(task_params)
redirect_to root_path
end
# ...
private
def task_params
params.require(:task).permit(:name, :memo, :done)
end
end
なお、検証環境は以下のとおりです。
- Ruby 2.1.2
- Rails 4.1