意識低いrubocopの運用の仕方

rails 界隈では Sider の普及に伴い rubocop を業務で使っている方も増えてきたのではないでしょうか?
しかしながら、一度荒れ果てた荒野にrubocopを放っても成果はほとんど上がらないことでしょう。
必要なのは、すべてのコードを綺麗にするぞ!という高尚な志ではなく、現実を少し改善すれば、それでOKという意識低い気持ちな気がします。

--lint オプション

rubocopには -l または --lint というオプションがあるのをご存知でしょうか?
こいつはlintオプションと呼ばれるやつで、bugに繋がりやすい警告レベルを指摘してくれます。

こいつをまずデフォルトにしましょう。
.rubocop ファイルに --lint と記述すると、rubocopのコマンドが常に--lintオプションがついた状態で実行されるようになります。
.rubocop ファイルを使うことで、プロジェクトのメンバー全員がそのオプションをつけてrubocopを実行することができるため、「 rubocop -l と実行してください!」などと呼びかける必要がなく便利です。

rubocop_todo.yml と向き合う

次にこの状態で、rubocop_todo.yml を生成します。
rubocop --auto-gen-config

プロジェクトにこれ以上 --lint に違反するコードを入れないようにするためです。

rubocopを入れると、rubocop対応で色々と対応工数が増えてしまうと恐れている方もいらっしゃると思いますが、
この --lint オプションは宗教戦争的なものはほぼなく、バグに繋がる恐れがあるか?という観点での指摘です。

さて、auto-genされた rubocop_todo.yml には問題が山積です。
これらは指摘事項を修正していかなくてはなりません。

todoファイルと向き合いきれなかった場合

rubocop_todo.yml ではルールをファイルあたりで抑制してしまうため、
todoファイルを全部修正できない場合は、問題の箇所を特定してコメントアウトで警告を抑制する方法をとったほうが良いでしょう。
じゃないと、 rubocop_todo.yml に記載されたファイルは治安が悪化して行きます。

一日1cop修正

私のプロジェクトでは1日に一つだけ、これを実行していくことを行いました。
修正はコミットを細かく分けて、tmpブランチからcherry-pickをしてmasterにマージする量を調整する形をお勧めします。
(OSSである dev.to では一つのpull requestがマージされたら新しいpull requestを作るという方法をとりました。)

rubocop_todo.ymlを消すことができれば、これでとりあえずrubocop -lに適応した状態を作ることができたということです。

--lint 以外のオプションと向きあう

次の段階としてrubocop -lをやめて、rubocop.ymlを設定していくというステップがありますが、これは次回以降に。
まずは、--lintで怒られないくらいを目指すのはいかがでしょうか?

FactoryBot.lintの困るところ。

associationをデフォルトのfactoryではnilを渡す

一般的にFactoryBotは大きなプロジェクトになってくると無駄なデータを作成する可能性が増えてきます。
例えば、こんな時。

factory :tag do
  article
end

it do
  create_list(:tag, 10)
  # 10個タグがある場合のなんらかのテスト
end

articleがtagを複数持てるDB構造になっているとして、
tagが10個以上付いているページのfeature specなどを書きたい場合がある時に、
上記のような書き方をしてしまうと、tagが10個だけできればいいものの、articleも10個できてしまうのです。

これを抑制するために、

factory :tag do
  article { nil }
end

このようにartilceは敢えてnilを渡して、validateエラーが起こるようにしておきます。
そうするとうっかり、 create_list(:tag, 10) なんて書いてもテストが落ちるようになり、、

it do
  create(: article)
  create_list(:tag, 10, article: article)
  # 10個タグがある場合のなんらかのテスト
end

おそらく、このようなテストになってくれるはずです。

あくまでデフォルトの話であって、traitを使う分には特定のcontextの名前を入れることができるので、問題ないと考えています。

factory :tag do
  trait :with_article do
    article
  end
end

↑みたいなのはOK

FactoryBot.lintの困るところ

で、この方針でfactorybotを運用すると困ることがあり、、
FactoryBot.lintは factory_botで作成されたDBテスト用のファイルの治安を守るためのFactoryBotがデフォルトで持っているlint機構です。

factory :tag do

  article { nil }
end

何と、こんなFactoryBot.lintは許されないのです。
validateエラーを敢えて起こしているのに、validateエラーになっちゃう。

ので、 strategy: :build のオプションを使ったlintを実行しています。
しかしながら、buildのlintは意味があるような無いような。。。
そもそも、関連にnilを渡すようにするのって多分気持ち悪いって方が多い気もします。(僕も最近まではそうでした。

皆さんはどのようにFactoryBot.lintを運用されていますか??

2018年夏フロントエンド×Railsの事情

このテーマ。僕はずっとずっと悩んで来た問題です。railsdm周りで話題になってたっぽい。

ベストプラクティスというものはまだ見えない。
でも、いろいろな実績が出て来たし、自分でもいろいろやって見たのでまとめて行きたいと思います。

1. sprokets + turbolinks + webpacker + stimulus

basecamp wayですね。
それぞれハマりどころが多くrailsに精通していないと使いにくいと思います。
DHH信者にオススメの設定です。

気持ちはわかるけど、染まって見るのもなしではないかな。

2. webpacker + react(or other javascript view library) (+ some cssloader || sprokets)

部分的にreactなどのviewを使う設定。場合によってはcssもstylesheet_pack_tagを使うパターン。
個人的にはこのパターンってreact使う必要ある?という気もします。
が、例えばKPIに直結する非常に重要な機能だけをSPAにして、
UXを最大化するのに使える構成ではあると思います。

この場合はsproketsを捨てない方が無難な気もしますが、
reactの中でcssを使いたい場合や、依存ライブラリをいい感じにシュッとしたい場合はwebpackerに頼ってしまうのもいいでしょう。

3. webpack + rails(without sprokets and webpacker)

例えばsproketsを捨てるのようなやつ。
railsでレンダリングするのは完全に捨てないまでも、
webpackerを通じてwebpackをコントロールするのに融通が効かないのは嫌だという場合にオススメな構成。

確かにハッシュダイジェストは捨ててもええっすね。

4. webpack + rails api

railsは完全にapiに成り下がるパターン。
フロントエンドは完全にjavascript wayに集中できる。
しかしながらSPAで全部作るのは大規模案件には骨が折れる作業にはなりそう。
特に管理画面系なんかはまだrailsに一日の長がある印象。

管理画面は別でrailsでサクッとmicroserviceで作ったりrails engineを採用したり工夫すると工数の削減も進むかも。
BFFとしてのrails apiといったところ。

ただ、そもそもBFFをrailsがある必要がどこにあるだろう。
他の言語でもswaggerやgraphqlを利用すればrails並みに工数を削減することもできるだろうしね。

けど、それでもrailsがいいって時はこれが無難ですよね。

まとめ

個人的にはオススメは4。逆に1という手もある。
一般的には2,3が多いけど、2,3が最も辛い。

再現テストの話

テストコードで再現テストをしてから、bugfixするやつ。

不具合にテストを書いて立ち向かう - t-wadaのブログ

これうまく行ったら超気持ちいいけど、いつも気持ちいいとは限らないのでメモ。

バグのパターンがユニットテストとしてエッジすぎ

だいたい再現テストコードがエッジなパターンすぎて、
仕様を説明する助けとして不適切になってしまう。

describe 'somemethod' do
  subject { describe_class.new(params).somemethod }
  context 'A' do
   let(:params) { 'a' }
   it { is_expected.to eq ('A') }
  end

  context 'B' do
   let(:params) { 'b' }
   it { is_expected.to eq ('B') }
  end

  # 再現テストのコンテキストがハイコンテキスト
  context 'B and hogehogehoe' do
   let(:params) { 'a' }
   before do
     # ハイコンテキストな記述 
   end

   it { is_expected.to eq ('A') }
  end
end

↑この程度なら全然いいんだけど野生のコードはこんなのじゃすまない。

このままマスターに入れてマージすると、仕様を説明することを阻害するよなぁ的に。

しかし、マージしておかないと同じバグを繰り返すかもしれないので、対策としてそのハイコンテキストなテストを別ファイルに切り出したりしてる。

コンテキストやフィクチャでテストのファイル分ける

1つクラスに1つのテストファイルがデフォルトですが、
テストファイルがって大きくなってきたら、コンテキストやフィクスチャ、メソッド単位でテストファイルを分割しても問題ないわけです。

なので、コンテキストが揃わないケースでは、別ファイルにコンテキストを分けてしまう。

そうすれば、仕様のキャッチアップにはよりプリミティブな浅いコンテキストのテストケースを読めるようにしておく。

そして再現テストは別ファイルで自動テストされるようにしておく。

というわけです。

なんかうまくいかない時に選ぶ他の手段

その他にもこのケースには下記の対応方法があるけど、場合によってはこれらも選んじゃう。

まとめ

基本的にバグに対して再現テストを書いて立ち向かって見てるけど、、、、
書きあがったテストコードがぐちゃぐちゃしちゃうのが悩み。

ぐちゃぐちゃでもいいから、別ファイルにテストコードを分けてコミットして再現テスト -> 修正すると良さそう。

そのやり方も万能じゃない。

そもそも再現テスト書いて、不具合に立ち向かうのをサボりたくなるときもある。

ブログを独自のCMSに移行しました

ブログ移管した

ブログを jekyll + github pages での運用から、独自のCMS(heroku + rails)に移行しました。

移管した理由

オンラインからサクッと編集したい

記事の雛形は make post みたいなコマンドを書いて、サクッと記事を書き始められるようにしてはいたのだけれども、、、
ちょっとした修正を出先や移動時間にするのにgithubに接続してブラウザから修正することにイライラしており、サクッと編集できるものが欲しかった。

jekyll疲れ

jekyllのプラグインとか拡張とかに限界を感じていた。

GitHub離れ

買収が発表される前から検討していたので、半分冗談ですが、移管完成がこのタイミングになったのはこれもある。
blogはコミットを外に公開したくなかったので、privateリポジトリで運用していましたが、
microsoft買収により自分が課金する必要もないかなと思いました。

このお金はherokuに払っていくぞ!頑張れheroku!

(あといくつかのprivateリポをgitlabかbitbucketに移管すれば、コスト削減も同時に達成できます。ありがとうmicrosoft。)

移管先の検討

wordpress

ブログサービス系 (medium/note/qiita/はてな/livedoor/line)

この辺りのツールはスマホからも修正がかなり簡単で圧倒的に使いやすいのです。
が、当たり前ですが融通が効かない点も多いので、断念しました。

自作

rails + herokuで自作しました

railsは自分のライスワークでありながら、ライクワークなので、
ブログを書きたくない時も、railsを書いてインプットができるのは良さそうと思いました。
あと、railsはブログみたいなツール作るのに向いてるんですよね。

以下、有益になるかもしれないことです。

markdownで記述は html-pipeline 周りでサクッと対応

html-pipeline というgemを使うとmarkdownをhtmlに変換するのは簡単です。
独自に拡張したい時もfilterをサクッと書くことができて最高です。

https://github.com/jch/html-pipeline

パーマリンクをそのまま移管

railsでブログ記事というとURLが /articles/1 みたいになりそうですが、
パーマリンクを過去の記事のまま移管する方法に rails engineを使用しました。
記事公開するためだけの rails engine を作り完全にroutesを分離し、それによりパーマリンクのためだけの一つのroutesを実現しました。

main appの config/routes.rb に下記を記述します。

  constraints(-> 'koheisg.dreamin.cc'.eql?(req.host)) do
    mount SomeEngine::Engine => "/"
  end

そして、SomeEngineのroutes、つまり some_engine/config/routes.rb

get '/:permalink', to: 'articles#show', permalink: /[^\s]+/

と記述しました。

今後について

という感じで考えてます。