ネットの海の片隅で

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

2016年振り返り

Twitter を見ていたら、いろんな人が今年のまとめを書いていたので僕も書いてみます。

実家に帰る近鉄特急の中で書いています。完全にチラ裏な感じです。

Timeline

卒論

多くの人に心配&迷惑をかけていた僕ですが、とうとう大学を卒業しました。

卒論のタイトルは『ビルドアップ法によるフェロセン微粒子の作製と消火性能評価』でした。

フェロセンというのは五員環と鉄イオンでできたハンバーガーで、それ自体は可燃性があるのに適切な濃度で水に分散させてやると負触媒として働いて消火に寄与s(ry

卒業 & 卒業旅行

(卒業に必要な最低単位数 + 1) 単位で無事卒業できることになったので、卒業旅行と称して旅行に行ってきました。一緒に行く人がいないので一人旅です。

海外に一人で行ったことがなかったので練習の意味も兼ねて台湾へ2日間、その後、一番行きたかったイタリアへ11日間くらい。生まれてはじめてヨーロッパに行ったわけですが、ヨーロッパは遠いですね。逆に言えば、日本はヨーロッパから遠いわけで自分が極東の島国の人間なんだなーと思ったりもしました。あと中国人めっちゃいる。

インターネットで航空券を取って、宿泊先は Airbnb で確保して、現地では Google でいろいろ調べられる。便利な世の中です。インターネット最高。

あと、帰りのトランジット待ちでドバイにも寄ってきました。オイルマネーすごい。ガシャを回して運営に貢献して欲しい。

就職

シャカイジンになりました。

入社1年前(内定をもらったタイミング)から今の会社でバイトしていたので、そういう意味では新生活感は薄かったです。 やっていることも大きくは変わらないんですが、自分の中での変化としてはコミット感とかオーナーシップみたいなものが大きくなった気がしています。

あと、就職にともなって横浜から目黒に引っ越したんですが、目黒めっちゃ便利ですね。山手線最高。徒歩通勤最高。

シンデレラ 4th(全日程 LV 😭)

最高だった。

感想とか

少しだけ真面目なことも書いておくと、なんか無難に過ごしてしまったなーというのが一番大きい。 特に大きい失敗はしていないし一定の成果はあげているけど、目立った成功もないという感じ。

失敗をしていないというのは挑戦をしていないということと高確率で同義なので、もっと挑戦しないといけない。

今年一年、半分冗談半分本気で「新卒特権主張しますっ!」とか言って失敗や無知のエクスキューズをしていたけど、それでもなお挑戦できていないのは問題。

僕は失敗することも質問することも万人に許されていると信じているので、3ヶ月後に「新卒」の肩書を失うとしても、そんなことは気にせずに挑戦しないといけない。

失敗を避け続けた先にあるのは、きっと穏やかな死。

今年1年間ありがとうございました。

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:テーブル定義はなんとなく察してください。