ネットの海の片隅で

技術ネタの放流、あるいは不法投棄。

gemをあまり使わずにFat Modelを整理するための本「Growing Rails Applications in Practice」

タイトルの通りで、 Growing Rails Applications in Practiceを読みました。

Fat controllerやFat modelに代表されるように、少し気を抜くとカオスになりがちなRailsアプリケーションの整理方法についての本です。

表紙とか目次も含めてPDFで88ページしかなくて比較的読みやすかったです。*1

全体的な構成

章立ては次のようになっています。

  1. Introduction
  2. Beautiful controllers
  3. Relearning ActiveRecord
  4. User interactions without a database
  5. Dealing with fat models
  6. A home for interaction-specific code
  7. Extracting service objects
  8. Organizing large codebases with namespaces
  9. Taming stylesheets
  10. On following fashions
  11. Surviving the upgrade pace of Rails
  12. Owning your stack
  13. The value of tests
  14. Closing thoughts

このうち、2章から4章が「New rules for Rails」、5章から9章が「Creating a system for growth」、10章から14章が「Building applications to last」という節に含められています。

各章について

各章の内容について簡単に触れてみます。

1. Introduction

基本的に https://leanpub.com/growing-rails に書かれている「About the Book」の内容です。 ここでDCI、CQRS、SOAなどに一瞬触れられていますが、その後の本文中で対比されることはありませんでした。

2. Beautiful controllers

はじめに「Controllerでいろんなことをせずに他のオブジェクトに適切に委譲しよう、Skinny controllerだ!」みたいなことを言った上で、そのための新しいルールを紹介してサンプルコードが提示されるんですが、そのサンプルコードが独特で面食らいます。 このときはちょっとした違和感を覚えるくらいでしたが、この独特なコードが第4章で思わぬ形で再登場します。

3. Relearning ActiveRecord

ActiveRecord継承モデルが仕事をしすぎている話が語られます。

ARモデルはupdate_attributeshoge=のようなメソッドでいろいろなところからattributeを変更されうるので、attributeや他のモデルとの関係の整合性が損なわれる可能性に触れて、validationとcallbackを使って整合性を保つ方法が紹介されます。

4. User interactions without a database

ログイン画面を例にとって、ActiveTypeを使ったフォームが紹介されます。 validationをフォームに切り出すというのはオーソドックスで良いと思うのですが、入力されたメールアドレスとパスワードが正しいかどうかを検証するために@sign_in.saveするというインターフェイスになっていて、saveとはなんだったのかみたいな気持ちになりました。

あと、本筋とは関係ないですがログイン失敗時に「User not found」とか「Incorrect password」といったエラーを返していてセキュリティ的に厳しい気持ちにもなりました。

5. Dealing with fat models

Fat modelが生まれる理由について語られます。

いろいろなコンテキストにおいてUserクラスに求められる責務が例示され、その責務を分割していくという方針が語られます。

例として挙がっているUserクラスの責務が具体的で「なるほど。たしかにこれはFatにならざるを得ない……」みたいな気持ちになれるのでオススメです。

6. A home for interaction-specific code

ARモデルが持つべき責務を

  • データ整合性担保のためのvalidation
  • associationの管理
  • レコードを探したり操作するための汎用的なメソッド

のみであるとし、それ以外のユーザーとやりとりするためのロジックをform modelに抽出しなければならないとのこと。

そのためのモデルとして、Userを継承してUser::AsSignUpを作るという考えが紹介され、そのままだとクラス名が変わったことによりform_forなどが動かなくなるため、UserではなくActiveType[User]を継承する方法なども紹介されます。

これは便利そうな気がすると同時に、どこが引っかかっているのかはわかりませんが、ちょっとびみょい感じもしています。

7. Extracting service objects

controllerにベタで書いているロジックをservice objectに切り出そうという話がされます。テストしやすくなる点などにも触れられている。

「ロジックを別の場所に移しただけだよね?「うんそうだよ」的なことが書かれていて良い。

8. Organizing large codebases with namespaces

Invoiceの子リソースであるItemInvoice::Itemにするといった感じで、app/models/直下を整理する試みが実例とともに語られます。

昔、自分でやってみていろいろハマった記憶があるんですが、ちゃんとやればうまくできるものなんだろうか……というのが正直な感想。 (試してみたのがかなり前なので僕がRailsをよくわかってなかっただけという説もある)

9. Taming stylesheets

アプリケーションコードだけではなく(もっとカオスになりがちな)CSSを整理する方法についても触れています。

といっても、基本的にはBEMを紹介しているだけなので、ここは飛ばしてWebを漁っても良さそう。

10. On following fashions

流行っているgemを採用するかどうかの基準について。

導入に際して書き換えるコードが多いgemは捨てるときにもコストがいるし、銀の弾丸はないんだから簡単に飛びつかないように注意が必要とのこと。

また、イケてるgemは裏でごにょごにょやってたりしてつらみを生むこともあるし、開発者が飽きてメンテされなくなることも多いから気をつけないといけないといったことも語られている。

11. Surviving the upgrade pace of Rails

Railsのバージョンを上げていくときに、gemが意外と足を引っ張るから注意してねといった話。

12. Owning your stack

gemを採用するならちゃんと責任をもってやれよといった内容。

言っていることは至極真っ当だとは思うんですが、前の2章と合わせて若干くどいというか目の敵にしすぎな感じが否めません。

13. The value of tests

UnitテストとIntegrationテストを書こうという話。

コードベースを改善していく上でテストがあるメリットは計り知れないので重要な話ではあるが、この本に必要かと問われると微妙なところ。*2

14. Closing thoughts

「thought」と書いてあるから著者の深淵なる考えでも書かれているのかと思いきや、連絡先の記載や拡散の依頼など事務連絡的なことが書かれている1ページ未満の章。

さいごに

サクッと読めて、問題点の整理ができるという点は非常に良い本だった。

一方、問題に対する解決策については良いと思う点があった一方で、首をかしげるところも多かったのでそのまま使うかどうかは検討する必要がありそう。

*1:薄いわりには$15で他の本とあまり価格が変わらず、相対的に少し高い。

*2:しかも結構長い。

排他的リソースに対するスケジュールの重複判定をSQLでシュッとやる

突然ですが、排他的にしか利用できないリソースに対するスケジュールの重複判定の問題を考えてみようと思います。 典型的には「ある会議室では同時に複数の会議を開催できない」というようなスケジューリングの問題です。

ある既存のスケジュールを下の図のピンクで示します。

すると、そこに対して新規にスケジュールを追加するときのパターンは既存のスケジュールの始点と終点のまたぎ方によって分けられ、黄色と青色を合わせて全部で6パターンあります。 *1

f:id:s_osa:20161126233115p:plain

このうち、黄色はピンクと重複している部分があるもの(invalidなスケジュール)、青色は重複していないもの(validなスケジュール)です。

新規に追加するスケジュールがinvaidな黄色の場合にエラーを返したいのですが、4パターンもあって場合分けが大変そうです。

というわけで、青色のパターンから考えます。

青色になる条件

黄色と違って、青色になる条件は比較的簡単です。

  • 新規追加するスケジュールの終点が既存のスケジュールの始点よりも前にある
  • 新規追加するスケジュールの始点が既存のスケジュールの終点よりも後にある

のいずれかですね。

したがって、新規追加するスケジュールの始点と終点をそれぞれSn, En、既存のスケジュールの始点と終点をそれぞれSe, Eeとすると、青色になる条件はEn < Se OR Sn > Eeとなります。

黄色になる条件

青色になる条件がわかったので、元々求めたかった黄色になる条件を求めます。

新規追加するスケジュールは黄色か青色のいずれかになるので、青色にならなければ黄色です。

したがって、さっき調べた青色になる条件を使うとNOT (En < Se OR Sn > Ee)と書けます。

ド・モルガンの法則を使って変形するとNOT (En < Se) AND NOT(Sn > Ee)

もう一歩整理してEn > Se AND Sn < Eeです。

シンプルになりました。

SQL

この条件を使って、これから挿入しようとするスケジュールが既存のスケジュールと重複していないかどうか調べるSQLを書くことができます。 *2

select
  *
from
  rooms
  inner join events on rooms.id = events.room_id
where 
  roooms.id = ROOM_ID
  and En > events.start_at
  and Sn < events.end_at
;

さいごに

このような重複判定をApp側で複雑に場合分けしてバリデーションしていたコードがあったのでこのエントリを書きました。

手続きで温かみある感じではなく、スパッと宣言的に判定していきたい気持ちです。

*1:議論が煩雑になって面倒なので、ここでは境界値を含むことはないこととします。

*2:テーブル定義はなんとなく察してください。

MIX PENLa-PRO のカスタム色設定

ミックスペンラプロといえば全国のP御用達のペンライトですが、このペンライトは色の並びがよくわからない感じになっています。

参考:コンサートペンライト/MIX PENLa-PRO

MIX PENLa力が低いと、「あの色どこだっけ?」とか「あ、このピンクじゃない! もう少し淡い色が欲しい!」みたいなことがよくあります。

そこで、カスタム色設定機能を使って、色相環的に色が並ぶようにしました。 直感的に色を探すことができるようになったことによって、ガールズの応援に集中することができます。

順序 色番号 色名
1 1 レッド
2 2 エンジレッド
3 3 ローズ
4 18 ピンク
5 19 ピーチ
6 20 サクラピンク
7 23 ラベンダーパープル
8 22 パープル
9 21 バイオレット
10 4 ブルー
11 7 アクアブルー
12 5 ライトブルー
13 6 アイスブルー
14 24 ホワイト
15 17 エメラルドグリーン
16 16 ペパーミントグリーン
17 13 グリーン
18 15 ライトグリーン
19 14 イエローグリーン
20 8 イエロー
21 9 ライトイエロー
22 12 ヤマブキオレンジ
23 11 パッションオレンジ
24 10 オレンジ