RailsのURL設計を考えてみる(7) リスト操作

リスト操作といえば昔からActsAsListが定番ですが、「上に移動」「下に移動」のリクエストをどのようなURLで実行していますか?

http://blog.takuyan.com/blog/2013/02/19/consideration-of-how-to-operate-the-list-on-rails/

この話を今週のSendagaya.rb@kattonさんとしていました。そのときはあまり時間がなく、中途半端な答えしかできなかったのですが、せっかく@kattonさんがわかりやすい形でブログに書いているので、もうちょっと突っ込んで考えてみました。

久しぶりの「URL設計を考えてみる」シリーズ記事も書けたw @kattonさんありがとうございます!

モデルはこのようなものを考えます。

class List < ActiveRecord::Base
  has_many :items, :order => "position"
end

class Item < ActiveRecord::Base
  belongs_to :list
  acts_as_list :scope => :list
end

itemに対する操作

Methods That Change Position and Reorder List
  • list_item.insert_at(2)
  • list_item.move_lower will do nothing if the item is the lowest item
  • list_item.move_higher will do nothing if the item is the highest item
  • list_item.move_to_bottom
  • list_item.move_to_top
  • list_item.remove_from_list
https://github.com/swanandp/acts_as_list
基本的な考え方
  • 操作がCRUDにおさまらないように見えるときは、そこに新たなリソースが隠れている
    • 名詞、形容詞、または動詞の現在分詞、過去分詞に着目
    • position, order, moved(moving), high(higher), top などが新たなリソースの候補
  • 新たなリソースをつくるとき、GETして意味があるかどうかを考える
    • GETする意味のないものはリソースとして不適格*1
HTTPメソッド

操作に対応するHTTPメソッドを考えます。冪等な操作かそうでないかを考えると見分けやすいです。(「冪等(べきとう)」についてはこちらの記事参照

  • insert_at(n) : PUT(追加の場合POSTだが考慮外)
  • move_lower : POST
  • move_higher : POST
  • move_to_bottom : PUT
  • move_to_top : PUT
  • remove_from_list : DELETE
考え方 その1

例:move_to_top
単純に、位置をさす数値positionをリソースとして使う。

PUT /items/123/position
position=0

もしくは

PUT /items/123
item[position]=0

これで insert_at(n) も実現可能。
ただし、move_to_bottomで破綻する(positionの下限がわからないため)。

考え方 その1-1

位置をさすpositionの値に、数値に加えて'bottom'を許す。

PUT /items/123/position
position=bottom

あまりきれいではないし、エラーハンドリングなど扱いが面倒になる可能性があるが、だいたいうまくいきそう。

考え方 その1-2

例:move_to_top
topかどうかをさすリソース position_top をつくる。
(フラグの考え方。これもパターンっぽいので、あとで名前つける)

PUT /items/123/position_top
position_top=true

ここからmove_to_bottomを考えると、topとbottomは排他的なため、リソースは1つにまとめられる。

PUT /items/123/position_edge
position_edge=bottom

topでもbottomでもないときはGETすると'other'や'false'が返される。
ただここまで来ると「その1-1」とあまり変わらなくなっている気も。

考え方 その2

例:move_higher
これはもう、POSTなのでしょうがないw

itemへのPOST

POST /items/123
position=move_higher

positionへのPOST

POST /items/123/position
move_higher=1

listの順序リソースordersをPOST(create)

POST /list/1/orders
operations[move_higher]=123

いろいろ考えられますが、決定打がない。逆に言うと単純な場合ならどれでも問題ない。(ほんとはできる限り整合性のとれた形でfixしたい)
move_lowerも同様。

雑感

毎回考えるのは不毛だし、routesを書くのも面倒なので、こういうリソース設計も、ある程度パターン化してgemに含まれていればうれしいですよね。
僕が目指しているのはそういう方向です。

*1:これはかなり原理主義的なスタンスではあります