Ruby勉強会を開催しました(2015#6)

6月6日に今年6回目のRuby勉強会を開催しました。
そろそろ梅雨入りか?!と思わせる天気予報もありましたが、まだ大丈夫のようですね。
今回も新入社員3名を含む9名が参加しました。着実にRuby人口を増やしていきたいと思います。

新入社員のみなさんには、まずはRuby Silverの資格取得に向けて学習してもらっています。
ブロックについては仕様は理解できたけど、なぜ必要なのか?などの質問も出てきました。
いい加減な回答はできませんので、旧人もしっかり回答できるよう準備しておくかないと^^;)

ブロックについては、語るとクロージャやら、Procやらいろいろとと出てきてしまいますので、改めてリファレンス的なものや、コールバックの実装のスニペットのようなものをまとめたいと思います。

只今アプリケーションを開発しているのですが、今回の参加メンバの一人にRailsのモデルの作成に協力してもらいました。
ドキュメントをフォルダに保存する機能を設けるのですが、このフォルダをチームメンバでシェアするためのいわゆる「共有フォルダ」です。

このモデルのリレーションのイメージは次のようなものになります。

Folder—-Sharing—-Team

  • Folder : フォルダ・・・文書の保存先
  • Team : チーム情報・・・ユーザは複数のチームに所属可能
  • Sharing: フォルダの共有情報・・・FolderとTeamの中間テーブル

ここでFolderの一覧を検索する場合、そのユーザの共有フォルダのみ抽出するスコープをFolderモデルに設定することを考えてみましょう。
その場合、抽出条件がFolderモデルの属性ではなく、関連しているモデルに対して抽出条件を指定しなければなりませんので、scopeメソッドの第2引数(ラムダ式)に若干複雑な取得条件を指定することになります。

# Folder Model


has_many :sharings
has_many :teams, through: :sharings

scope :shared, -> { joins(:sharings).where(sharings: { team_id: [ユーザの所属しているチームIDの配列] }) }

フォルダの共有情報に対して、条件を指定しますので上記のようなコードになるかと思います。

ここで、共有情報の指定をべたに記述してしまうと、抽出条件が変わった場合Folderモデルの修正が必要になってしまいます。
この例では仕様が変わるようなケースは少ないかもしれませんが・・・。

そこで利用すると便利なのがmergeです。

# Sharing Model


scope :teams -> { where(team_id: [チームIDの配列]) }

# Folder Model


scope :shared, -> { joins(:sharings).merge(Sharing.teams) }

共有情報の抽出条件をSharingモデルの scope に指定します。すると上記のように書き換えることができ、コードの可読性も良くなるかと思います。
これで Folder.shared と書けば共有フォルダの一覧を取得することができるようになりました。

また、Sharingの抽出条件の仕様が変わっても修正箇所が1ヶ所で済む点と、更に複雑なテーブルの結合の条件であれば更にメリットを享受できると思います。ぜひ merge を活用してみてください。