-
-
Save dhh/981520 to your computer and use it in GitHub Desktop.
# Bulk API design | |
# | |
# resources :posts | |
class PostsController < ActiveController::Base | |
# GET /posts/1,4,50,90 | |
# post_url([ @post, @post ]) | |
def show_many | |
@posts = Post.find(params[:ids]) | |
end | |
# PUT /posts/1,4,50,90 | |
def update_many | |
@posts = Post.find(params[:ids]) | |
Post.transaction do | |
@posts.each do |post| | |
post.update_attributes!(params[:posts][post.id]) | |
end | |
end | |
end | |
# POST /posts/many | |
def create_many | |
end | |
# DELETE /posts/1,4,50,90 | |
def destroy_many | |
end | |
end |
...though I got mixed feelings on the show vs. index, as I used edit action for "edit multiple". =S
grimen's solution seems much cleaner to me. Some changes on Relation methods would also help:
Relation#all(params[:ids]) - would do same as find, but if you pass nil it would return all records. This is to avoid:
@posts = Post.scoped
@posts = @posts.where(:id => params[:ids]) if params[:ids]
Relation#update_attributes (and similar methods) - this could be used instead of:
Post.transaction do
@posts.each do |post|
post.update_attributes!(params[:posts][post.id])
end
end
@sinsiliux, the first case could be avoided by having params[:ids]
to be equivalent to params[:id].split(',')
. That's how I handled it at least, but maybe I was playing with fire. :)
@grimen what I wanted was to return all records when params[:ids] is empty and selected records when it's present.
@sinsiliux I just noticed where(:id => params[:ids])
!= where(:id => nil)
- I think this was not the case in Rails 2.3.x (old finders). Anyway, my basic point was that for me params[:ids]
should be same as params[:id]
to avoid singular vs. plural getting in the way.
@grimen I too wonder if handling the one-or-many cases using a single action makes sense. I'm experimenting with this before_filter:
def separate_bulk_ids
if params[:id].present?
params[:ids] = params[:id].split(',')
params[:id] = params[:ids].first
end
end
Is it surprising that params[:id] is always the first of many IDs? Aside from the obvious case of overwriting an explicitly passed :ids parameter, when would this cause problems? Anyhow, here's how I make use of it in a trivial #update example.
def update
# Gracefully ignores invalid IDs
@posts = Post.where(:id => params[:ids])
@posts.each do |post|
# Enforce policy if desired on each post...
post.update_attributes(params[:posts][post.id.to_s])
end
# What results are returned considering some updates may have failed?
end
I've created a little Rails app with some different examples and tests.
https://github.com/jmorton/bulk-example/blob/tolerant/app/controllers/posts_controller.rb
https://github.com/jmorton/bulk-eaxample/blob/strict/app/controllers/posts_controller.rb
If I recall correctly, Richardson's Restful Web Services recommends ,
for dimensional things (like /locations/lat,long/
) and ;
for lists of things (like /posts/1;2;3
). Allamaraju's Restful Web Services Cookbook, on the other hand, doesn't express a preference between the two.
For the record: In my impl. I had something like:
GET /posts/1,2,3,4,... => index
GET /posts/1,2,3,4,.../edit => edit
PUT /posts/1,2,3,4,... => update
POST /posts => create
DELETE /posts/1,2,3,4,... => destroy
So I agree that the proposal is a bit overkill unless I've missed something.