Hatena::ブログ(Diary)

ぶろぐ。@はてな Twitter

Web関係の技術記事中心のblog

2014-04-17 (Thu)

RESTful Meetup vol.3 を開催しました #RWABookja

http://restfulwebapis.com/

4/12(土)の夜に『RESTful Meetup vol.3』を開催しました。

昨年の記事の通り『RESTful Web APIs』の読書会を月2回ペースで開催してきましたが、その後、著者のMike Amundsen(@mamund)さんから、ワークショップのために東京へ行くという知らせがあったので、これはイベントをやるしかない!ということで企画しました。

vol.3ということで、実は過去2回開催しています。そのときは『RailsにおけるRESTfulなURL設計勉強会』というタイトルで、かなりターゲットを絞っていたのですが、今回は「REST」「Web API」というかなり広いテーマにしました。このことでRuby/Railsに限らず多様な言語の人に集まってもらえたのがとてもよかったです。

ビールビザをつまみながら、というビアバッシュ形式にしたのですが、有料イベントなので少ないだろうと思っていたら予想を上回る多くの参加者が集まり、当初15人程度の予定が30人以上になりました。

@tkawa による紹介

まず、イントロダクションとして、『RESTful Web APIs』がどのような内容の本かを簡単に説明しました。

これ、実はイベント前日までもっと専門用語だらけの意味不明で長い内容だったのですが、@ruziaさんのレビューのおかげでわかりやすくすることができました。ありがとうございました!

最後のほうに入っているRubyのコードの部分は、自分の構想としてLTしようと思っていたものです。そのスライドはこちら。

実はアイデアはmicrodata DOM APIそのままだったのでした。

@mamund さん「Reusable APIs」

APIでやってはいけない10の話。簡単な日本語字幕をつけたバージョンでした。

@t_wada さん「Reviewing RESTful Web Apps」

@zigorou さん「How to bake delicious cookie

@koriym さん「HTTP IS ONE THING」

http://koriym.github.io/http-is-one-thing/

@Jxck_ さん「PUT & DELETE why html form doesn't support?」

@josolennoso さん「Scale your Service with RESTful API」

(公開なし)


非常に熱くて内容の濃い発表ばかりですごかったです。

運営的にはこういううれしい悲鳴も。

イベント運営は実はSikachu Meetupを参考にさせていただきました。アドバイスをくれた@sanematさん、@machidaさんありがとうございました。

当日の様子はRESTful Meetup vol.3 - Togetterまとめ#RWABookjaにまとまっています(@NEKOGETさんまとめありがとうございます)。

参加者のみなさん、発表者のみなさん、そして@mamundさん、本当にありがとうございました。

RESTful Meetup was the great event, thanks to the attendees, speakers, and @mamund!

あまりに濃すぎたので、ライトなものをもう一度やろうかなと思っていますがいかがでしょう?

2013-10-11 (Fri)

RESTful Web APIs 読書会を開催しました&第2回募集 #RWABookja

http://restfulwebapis.com/

春ごろからずっと待っていた“RESTful Web APIs”がようやくリリースされたので、10/10に第1回の読書会を開催しました。

よく行くShibuya.rbや、主催のひとりであるSendagaya.rbで告知したので、Rubyを使っている方が中心でしたが、PHPPerlを使っている方もいらっしゃって、初回から10人で開催することができました。ありがとうございます。


なんと本の公式アカウントで紹介されて、著者のMike Amundsenにもメッセージをもらいました!


進め方は、僕がかいつまんで文章の訳を読んでいきつつ、随時みんなで気になったことや疑問点などを話し合いました。

予習必須にしないで、できるだけ気軽に参加してもらいたいという考えでこのようにしたのですが、僕の英語力が貧弱なせいで、ところどころ訳に詰まってまわりの方にサポートしてもらうことに…。すみません&ありがとうございました。もう少しがんばりますのでまたサポートいただければうれしいです。

とりあえず次回も同じスタイルでやろうと思いますが、セクション単位で参加者の方にお任せしようかな、とも考えています。

次回は10/24(木) 19:30から開催します。まだSection 1の途中なので入りやすいです。本文には特定のプログラミング言語の記述はほとんど出てこないので、言語は関係なく、RESTやWeb APIに興味のある方は下記のイベントページからどしどしご参加ください!

2013-08-12 (Mon)

Deviseのルーティングが微妙なのでgemを作ってみた

Devise便利ですよね。簡単にリッチな認証機能がつくれます。

# config/routes.rb
devise_for :users
        new_user_session GET    /users/sign_in(.:format)          devise/sessions#new
            user_session POST   /users/sign_in(.:format)          devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)         devise/sessions#destroy
           user_password POST   /users/password(.:format)         devise/passwords#create
       new_user_password GET    /users/password/new(.:format)     devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format)    devise/passwords#edit
                         PUT    /users/password(.:format)         devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)           devise/registrations#cancel
       user_registration POST   /users(.:format)                  devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)          devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)             devise/registrations#edit
                         PUT    /users(.:format)                  devise/registrations#update
                         DELETE /users(.:format)                  devise/registrations#destroy
       user_confirmation POST   /users/confirmation(.:format)     devise/confirmations#create
   new_user_confirmation GET    /users/confirmation/new(.:format) devise/confirmations#new
                         GET    /users/confirmation(.:format)     devise/confirmations#show

いまいちなところ

Devise::RegistrationsControllerの役割があいまい

/usersが Devise::RegistrationsController に取られてるのはひどい。普通 UsersController になるはずでしょう。

PUT /usersDELETE /usersも地味にひどい)

さらにここによく使うresources :usersを足すとカオスに…。

cancel_user_registration GET    /users/cancel(.:format)          devise/registrations#cancel
       user_registration POST   /users(.:format)                 devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)         devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)            devise/registrations#edit
                         PUT    /users(.:format)                 devise/registrations#update
                         DELETE /users(.:format)                 devise/registrations#destroy
                   users GET    /users(.:format)                 users#index
                         POST   /users(.:format)                 users#create
                new_user GET    /users/new(.:format)             users#new
               edit_user GET    /users/:id/edit(.:format)        users#edit
                    user GET    /users/:id(.:format)             users#show
                         PUT    /users/:id(.:format)             users#update
                         DELETE /users/:id(.:format)             users#destroy

何をどう使えばいいんでしょうか、これ…。


何が問題なのか

  • 複数形であるusersは普通「ユーザー全体」をさすはず(コレクションリソース)。なのに registrations#update は「自分自身を更新」つまり「自分自身」をさしている。 #cancel, #edit, #destroy も同様
  • そもそもURLにも出てこない“registrations”っていったい何?

「自分自身」をさすリソースは1つしかないので単数形で表現できます。

resource  :user  # (a)単数
resources :users # (b)複数
resource  :users # (c)まぎらわしいのでよくない

(a)と(b)は別のリソースなので区別しましょう。

解決策(案)

devise-better_routes gemを使うと、ルーティングはこのようになります。

# Gemfile
gem 'devise-better_routes'
              users POST   /users(.:format)               users#create
           new_user GET    /users/new(.:format)           users#new
cancel_current_user GET    /current_user/cancel(.:format) current_users#cancel
  edit_current_user GET    /current_user/edit(.:format)   current_users#edit
       current_user PATCH  /current_user(.:format)        current_users#update
                    PUT    /current_user(.:format)        current_users#update
                    DELETE /current_user(.:format)        current_users#destroy

謎のregistrationsは消えて、コントローラがusersとcurrent_usersに整理されました。

このコントローラは用意されていませんが、基本は単にこんな感じで作ってやればOKです。

# app/controllers/users_controller.rb
class UsersController < Devise::RegistrationsController
end

# app/controllers/current_users_controller.rb
class CurrentUsersController < Devise::RegistrationsController
end

同様に、passwordとsessionもパスが変更されます。

オプション

/current_userって名前はいまいち…、というときは、オプションで変更することができます。

# config/routes.rb
devise_for :users, path_names: {current_user: 'me'}
    users POST   /users(.:format)     users#create
 new_user GET    /users/new(.:format) users#new
cancel_me GET    /me/cancel(.:format) me#cancel
  edit_me GET    /me/edit(.:format)   me#edit
       me PATCH  /me(.:format)        me#update
          PUT    /me(.:format)        me#update
          DELETE /me(.:format)        me#destroy

いい感じじゃないですか?

Deviseへissueを投げてみた

Thanks for the proposal. Yes, it make sense, even though we have originally decided to not pollute two namespaces for the same Devise controller. Unfortunately, this is a very backwards incompatible change, so any change of this dimension would have to wait until Devise 4.0 (and we just released 3.0). Fortunately, you can customize it on your own, as you did. :)


So I am closing this but leaving a note to myself that we could possibly revisit this (in a year or two).

Registration routes are confusing ? Issue #2505 ? plataformatec/devise ? GitHub

ってことで、リップサービスかもしれないけどDevise 4.0に期待w

2013-06-04 (Tue)

RubyKaigi 2013 で発表しました

RubyKaigi 2013 の最終日、6/1に「Rails Gems realize RESTful modeling patterns」というタイトルで発表させていただきました。

Ustream RubyKaigi Channel 2

相変わらずコードの実例に乏しく、idealisticなくせにemotionalでもない話になってしまい、自分の力のなさを痛感するばかりなのですが、まわりの方々には良かったという言葉をいただいて、とても感謝しています。

発表内容について

昨年の「RailsにおけるRESTfulなURL設計勉強会」で発表した「リソースモデリングパターンの提案」およびそれをまとめたブログリソースモデリングパターン」と基本的な論旨は変わりません。RubyKaigiなので、よりRuby(というかRails)に密接した内容ということで、gemからパターンを見出す、という流れにしました。思い返してみれば、自分がパターンを考えたときもそうしていたはずで、Railsプログラマにとっては自然な流れなのです。

とはいえ、ルーティングまでカバーしたgemの数はそれほど多くなく、有名なものをいくつか取り上げただけに終わってしまいました。そこでちゃんと自分で作ったものを紹介すべき(CFPにはそう書いた…)だったのですが、ほんの1ページCollectionFilterを紹介しただけになってしまったのは日頃の怠惰が原因です。反省。

最終日の朝11時というのは、慣れてない自分にとってはベストな枠で、他の人の発表を見て、脳内リハーサルを繰り返すこともできたし、最後までスライドや内容を手直しすることができました。

リハーサルでアドバイスをくれたSendagaya.rbのみなさん、スライドの英語をレビューしてくださった早坂さん鵜飼さん(+ネイティブの方々)、ありがとうございました。

また、ろくに口頭発表の原稿を出しておらず、直前打ち合わせでもグダグダだったのにプロフェッショナルに対応してくださった同時通訳のお二方、ありがとうございました。

参加者として

今回は発表に採択していただけたこともあって、仕事も気にせず迷わず全日参加しました(朝ちょっと寝坊したのを除く)。

正直なところ、チケット高いし(買ってないけど)インターナショナルになったので、少し入りづらいかなぁとも思っていたのですが、RubyKaigi 2011のときと同じような居心地のいい空間でした。これもTEAMのみなさんのお力だと思います。本当にありがとうございました。

ほかの方の発表を聞いて

@hsbt さん

仕事が幅広すぎ。すごい。

@shugomaeda さん

refinementsやっぱり使いたいので2.1期待ですね。

@kyanny さん

内容がすごく実践的で、自身の経験を挙げて根拠も示していて、僕の対極にあるようなすばらしい発表。

“Daily Pull Request gave me the chance”

いい言葉だなー。Open source contributionだからって大げさに思わなくても、普段使いでバグを直していけば大きな貢献なんですよね。そのチャンスは毎日来るんだよ、と。

@a_matsuda さん

最初は観光案内か?と思ったけど、すごい内容(しかも全部英語だしわかりやすいし)。やっぱりgem作るべきなんだよ、と改めて痛感しました。

@moro さん

ActiveSupport::Concern自体は自分もかなり使っていましたが、controllerにもできたconcernsってどう使うんだろうと思っていました。Nested Resourceで使うというのはとてもわかりやすい。

こういう発想ができるようになりたい。

@brynary さん

Sendagaya.rbでは何回かブログを読んだのでおなじみのCode Climate。こういう純OO的手法は、大クラス主義のRubyやRailsに慣れた身としてはちょっともやっとするところもあるのだが、手法としては理解しておく必要があるし、バランスを考えて適用していくべきだろうな。

@yotii23 さん

“Application give me a shape to the cultural system”

これもいい言葉だなー。

@sonots さん

fluentdはまだ興味段階でしかないけど、それのWeb UIというのがわくわくする。あと英語が流暢!

Committerトーク

いつもの。グダグダなんだけど楽しい。

@ktou さん

これが須藤ワールドかー。library developerになりたい。

TRICK

実は一番楽しみにしていました。裏のfluentdも聴きたかったので重なって残念だったのだけど。内容は期待通り抜群に面白かった。

$ruby.is_a?(Object){|oriented| language} # => true

とか美しい!

@tanaka_akr さん

すげえ、の一言。

あ、それから

RubyHirobaも楽しかった!

2013-02-20 (Wed)

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

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

no title

この話を今週の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:これはかなり原理主義的なスタンスではあります