今日の俗世:
- 3500万円返金
- イーロンマスク エルデの王へ
- 知床観光船事故 海底182m
Sustainable Railsを読んでいく
logrageはtest環境で使う
development環境以外でlogrageを有効化している。test環境ではlograge有効化してほしくないな。production環境みたいな絶対に機械的しか見ないような場所では良いと思う。
app以下にapp/models以外にドメインモデルの亜種を増やす
ビジネスロジックを書くときはそれ専用の app/services みたいなディレクトリを使うらしい。自分はディレクトリを増やすことには反対で、すべてのビジネスロジック用クラスはドメインモデルの亜種だと思うので、app/models以下に定義した方が良いと考えている派。autoloaderの厄介事も増えて複雑になるし、ディレクトリが増えれば増えるほど命名規則がブレるようになるし。ActiveRecordにロジックを盛り込むかどうかという議論はまた別の話。
ビジネスロジックを記述するクラスを細かく分ける
ビジネスロジックを書くときにクラスを細かく分けろという話がある。これには賛成。なぜなら、人はプログラムの大まかな構造を理解するときに、クラス間の関係性に着目して理解することが多いから。よく参照されているクラスに不具合があると、確認しなければいけない可能性のあるクラスが増える……という例が示されている。構造や関係性を表明するための手段としてクラスを細かく分ける。
常にresource-fulなroutingを使うべき
resources :foos
と get ...
とどっちを使うべきかという話について、後者を使うのは全くもって無益だとしている。そこまでか?でも、どちらにするかはともかく、どちらか片方に寄せるという姿勢は好ましいと思う。
Don’t Create Custom Actions, Create More Resources
"create more resources"という表現は良い。"コントローラーを分けた方がいい"、とするよりもこっちのほうが納得感が出るかも?
Resources should never be nested more than 1 level deep
という記述がガイドにあるのか。確かにこのルールが丁度良いと思う。
Bet you didn’t think routing was such a deep topic!
わかる。ルーティングは超大事な部分だが、かなり軽視されていてつらくなりがち。
Ideally, Expose One Instance Variable Per Action
可能なのか?そんなルールを掲げることが。 コントローラーからテンプレートにインスタンス変数で値を渡すという仕組みはひどい仕組みだとは思っているので、まああんまり多用しない方が良いよなという意味では賛成寄りではあるが、1個にしよう、とかあえて言うべきことなのだろうか。コントローラーでmemoizeしててヘルパーから透過的に触ってるやつ、とかもあるしなあ。
Helpers are Best at Exposing Global UI State and Generating Markup
ヘルパーは、Railsの提供するやつのようなグローバルなタイプのヘルパーとしては上手く働くという話。
Just Use ERB
そうだね。自分もERBの方が総合的には上手くいくと思っている。
テンプレートエンジンに関する話題で言うと、自分の中では、erbcopとslimcopの存在が大きくて、これが無いとテンプレートエンジン中のRubyコードに対してRuboCopで自動置換が出来ない。hamlcopもつくろうとしてみたんだけど、hamlパーサーを書くのが大変で実用段階には至らなかった (誰か完成させてほしい)。話を戻すと、RuboCopと親和性が最も高いということが、自分の中でERBを推す理由の一つになっている。
Presenter
Presenterの問題点:
- 直接モデルにアクセスするか、Presenterを通してアクセスするか、2つの方法が生まれてしまう。2つの方法が生まれるとね3つめもどうせ生まれてしまう
- Viewのコードを見ても、値がPresenterなのかただのModelなのか分からない
…としていて、これには同意するところではあるが、でもそれってお前の言っているサービスクラスってやつについても言えるんじゃないか?
変数の命名
I strongly recommend you name objects based on their actual class and do not pretend presenters are actually domain objects. For example, use @widget_presenter if the object is a WidgetPresenter, @widget if it’s a Widget.
それ。
ということはつまり、Foo::Barがあるとしたらbarという名前を使うべきという話だよな。ということは、Services::FooではなくServices::FooServiceとしなければならない。
あと個人的に名前について言いたいことがあるとすると、現代はエディタが十分にまともなので、名前は十分に長くても構わないと思う。英単語以上の省略は絶対にするべきではないし (例: eventをeとするなど)、widget_idをidとすべきでもない。
テスト時間かかりすぎ問題
If tests are too brittle, duplicative, slow, or focused on the wrong things, the test suite will drag the team down.
巷のRailsアプリは変なところにテスト書きすぎてて効率悪くなってる印象がある。ので引用した。悪いテストはチームの足を引っ張る。
The strategy I recommend is to have a system test for every major user flow, use unit tests to get coverage of anything else that is important, and closely monitor production for failures.
"major user flow" の例として、ログインページが良い例、FAQページが良くない例だと言っている。つまり全部ではなく、本当にコアなところにだけsystem-specを書くべき。じゃあ本当にコアなところって何やねんって言うと…? 特に深掘りされてなかった。
System Testについてもこう書けるよとしか紹介されてなくて、そのまま書き続けていくとマジで時間が掛かる遅いテストスイートが出来上がるぜ的なことは書かれていなかった。
HTMLのテストの小技
data-attributeにテストで検証する用の値 (IDとか) を埋め込んでおく小技が紹介されている。マジで小技だな…
ビジネスロジック
もしRailsというものが無くて、普通のRubyのコードだったらどうする? 普通にクラスを書いて、普通にメソッドを定義して、普通に呼び出すよね? Railsであってもそうすべき、と言っている。
サービスクラス
FooCreator.new.create_foo(...)
と呼び出せるようなクラスが理想的だ、と言っている。
一方、自分は次のようなものが理想的だと思っている。
FooCreator.call(...)
本書的には、.call だと内部で一時的に状態を持ちたいときに使えなくて不便だと言っている。自分的には、それは内部実装次第で解決できることだと思っている。
class FooCreator
class << self
def call(...)
new(...).call
end
end
def initialize(...)
...
end
def call
...
end
end
また本書的には、本書のようにしないと2つ目のメソッドを足したくなったときに困り、引数を足すか別のクラスをつくらないといけなくて不便だと言っている。
Further, the command pattern makes it difficult or impossible to add a second method on the service class if that should later make sense. For example, if there comes a need for a second widget creation process, by following the guidelines I’ve laid out, you could conceivably make a new method on WidgetsCreator and share any needed logic privately and internally. If you’ve used the command pattern, you either need to pass some behavior-modifying flags to the constructor or make a new class and figure out how to share needed logic publicly. This is a more complex result that the command pattern more or less forces you into.
自分的には、これは引数を足すか別のクラスをつくるべきだと思う。もしここで同じクラスに複数のメソッドを足していくべきだと言うのなら、1つのControllerにいろいろなActionを定義すべきでないという話と矛盾すると思っている。
とはいえ、似ているが少し違うクラスが沢山あって好きに選んでくださいというパターンは、認知負荷が高まるとも思うので、一概に最高かというとそうでもないかもしれない。ソフトウェア設計についてtwada技術顧問と話してみた 〜 A Philosophy of Software Design をベースに 〜 - NTT Communications Engineers' Blog の話が少し関連している。
齟齬があるかもしれないが、本書は大クラス主義を推している一方、Railsのコントローラーについてだけは小クラス主義を推している、というところに矛盾を感じるのだろうか。そして、自分はサービスクラスについては小クラス主義を推している一方で、ActiveRecord::Baseを継承するようなモデルクラスでは「別に必要ならメソッド足しまくればええやん」ということを思っていて、矛盾している。こういう反論したい意見が出てきてこそ、読む価値のある本と言える。
Validations Are Awesome For User Experience
Validationはデータの整合性を完璧に保つものではないから、UXのために使うべきだよとまとめている。いいまとめ方。ActiveRecordでのvalidationって、フロントエンドでのエラーチェックに似てるところがあるよね。
How to (Barely) Use Callbacks
あんまり使うなと言っている。あるあるですな。自分の話だけど、既にコールバックが多用されてるRailsアプリから、コールバックを引き剥がしたいという要求が開発者から挙がるのを、10社ぐらいで見ている気がする。
Scopes are Often Business Logic and Belong Elsewhere
3回書いたらscope化しろと言っている。ただあまり考察はなされていなくて、「普通にスコープをつくることもできるし、サービスクラスをつくってそこでスコープ的なこともできるよね。まあいろんなところで何度も使われるスコープっていうのはビジネスロジック色は薄くてデータ操作的だと言えるから、これはスコープで良いんじゃないか」と言っている。そういう温度感で良いのか?
Don’t Over-use Callbacks
Railsのscaffoldが生成する before_action :set_article
のようなcallbackが批判されていて、それはそのアクションの主たる部分だからアクションの中に置くべきだと言っている。これは自分的にも賛成。
一方で、巷のRailsアプリだとbefore_actionで書いてほしいようなものをアクション内に記述していて分かりづらい (しアクション本体で処理を中断してレスポンスを返すとその事実がログに残らないのでデバッグしづらい) という問題があったりもするので、Don't Over-useと喧伝するのもどうかなと思う。本文中でもそう言っているけど、気を付けながら使おう、ぐらいの見出しが丁度良いと思う。
Queue Jobs Directly, and Have Them Defer to Your Business Logic Code
ActiveJobを使うと引数のシリアライズとかで悩まされるから、それは避けてSidekiqとかのAPIを直接叩けとのこと。マジか。何か過去にジョブ周りでミスって重大な損失を被った悲しい経験があるのか……?
Rake Tasks Should Not Contain Business Logic
コントローラーやジョブと同じように、Rakeタスクもビジネスロジックを含めないべき (サービスクラスを呼ぶなどしろ) としている。これには同意。直接ビジネスロジックが書かれているRakeタスクとかどうせテストも禄にされてないしな。
A Philosophy of Software Designは読んでみたいところ。
Sustainable Web Development with Ruby on Railsを読み終えた。後半はなんかこう言及すべきトピックがあまり無かったのでざーっと読み流した。
腹筋ローラー 10回3セット