where all_month の罠

Rails (ActiveRecord) で月の集計を取りたい場合、all_month メソッドを使って、MyModel.where(created_at: time.all_month) のようにすると BETWEEN '2020-04-01 00:00:00' AND '2020-04-30 23:59:59.999999' のようなSQLになるので便利です。

ここで集計対象が DateTime なのに間違えて Date の all_month を使ってしまうと、SQLが BETWEEN '2020-04-01' AND '2020-04-30' となってしまい、月末が集計から漏れてしまうので注意!

System spec で CSRFトークンが取得できない

症状

Rails6 の system spec で、Ajax での POST処理のテストを書いていたら、meta タグの csrf-token が取得できずにエラーとなった。

const csrfToken = document.querySelector('meta[name=csrf-token]').getAttribute('content');
// => Uncaught TypeError: Cannot read property 'getAttribute' of null

原因

そもそも RAILS_ENV=test の時は allow_orgery_protection が false に設定されているので csrf_meta_tags が何も出力しないようだった。

config/environments/test.rb で以下のようになっているところを true に変更したら csrf_meta_tags で meta タグが出力されるようになった。

  # Disable request forgery protection in test environment.
  config.action_controller.allow_forgery_protection = false

Math.floor を使っていると webpack の minify で Error: Couldn’t find intersection が発生する

症状

Rails5.2 で ./bin/webpack 実行時に Error: Couldn't find intersection というエラーが発生する。

再度 ./bin/webpack を実行すると発生しなくなる。 tmp/cache/webpacker を削除すると再現する。

キャッシュが無い状態で ./bin/webpack もしくは rails assets:precompile 時に発生するため、開発環境では2回目のコンパイルでうまくいくのでスルーしてしまい、CIやデプロイ時にエラーになってしまう。

assets:precompile の時はログに Compilation failed: とだけ出力されるので webpack のフェーズでのエラーだと気づくのに時間がかかってしまった。

原因

https://github.com/babel/minify/issues/904

.babelrc の presets から minify を外すと再現しなくなるため、こちらの issue の通り、minify に問題があるようだった。

2019/11 時点で issue は対応されておらず、問題が発生する条件はいくつかあるようだが、Math.floor を使っていると発生する、ということに心当たりがあった。

解決?(応急処置)

minify が呼び出しているbuiltIns というプラグインで Math.floor の変形をしている。

https://babeljs.io/docs/en/babel-plugin-minify-builtins

ひとまず、.babelrc の presets で minify の builtIns を無効にすることでエラーは発生しなくなった。

["minify", {"builtIns": false }]

rails assets:precompile 時に yarn install が実行されない

Rails5をHerokuにデプロイしようとした際、rails assets:precompile 時に yarn install が実行されず、node_module ディレクトリをassetsから読み込むことができずにビルドエラーになってしまった。

そもそも assets:precompile 時に yarn install が実行される条件ってなんだろうと思って探してみると、bin/yarn ファイルがあることが条件のようだった。

https://github.com/rails/rails/blob/f99b3c5f9759baffec5c1f7abf74e108e2fb1c77/railties/lib/rails/tasks/yarn.rake#L11

bin/yarn ファイルを作成すると無事に yarn install が実行されてデプロイできた。

Rubyの system メソッドは、デフォルトでは実行ファイルがない場合は何も出力せずに nil を返すだけのよう。

https://docs.ruby-lang.org/ja/latest/method/Kernel/m/system.html

irb(main):005:0> system("hogehoge")
=> nil

PHPでスクリプトが無言で死ぬ

状況

CakePHPのプロジェクト。
自分で作った機能ではないが、数年前に作ってほったらかしだったCI環境のアップデートの必要があり、他に手が空いてる人がいないとのことで私が担当していた。テストを実行すると、スクリプトのプロセスがエラーログも何も出さずに突然終了してしまう事象が発生した。

原因

PHPエクステンションにインストール漏れがあった。エクステンションでインストールされる関数を利用している箇所でエラー制御演算子(@)が使われていたため、プロセスが落ちた時にログが出なかった。
テストケースをコメントアウトしながら実行して落ちるテストケースを絞り込み、実行されているメソッドのコードリーディングで発見した。

エラー制御演算子のドキュメントには以下の記述がある。

警告現在、エラー制御演算子プレフィックス”@”は、スクリプトの実行を 終了するような致命的なエラーの出力さえ抑圧します。このため、ある関数の エラー出力を抑制するために “@” を使用した場合、その関数が 利用できなかったり、ミスタイプがあった場合でも、原因を示すことなく その場所でスクリプトは終了してしまいます。

エラー制御演算子を外してテストを実行すると以下のようなエラーが出た。

Fatal error: Call to undefined function exif_imagetype() 

ログを見た瞬間に原因が分かる。エラー制御演算子が使われていたために3秒で原因が分かるエラーの特定に3日かかってしまった。まじ💩

メモ

PHPのプロセスが無言で死ぬ時はまず最初にエラー制御演算子を疑う。

SimpleCovでHelperのカバレッジが測定されない

環境

  • Rails 5.2.3
  • Rspec 3.8.0
  • simplecov 0.16.1

事象

ApplicationHelperのspecを書いてrspecを実行したところ、Helperのカバレッジが0%になっていた。他のファイルのカバレッジは測定されていた。Rspecの実行ログから、ApplicationHelperのspecが実行されていることは確認できている。

原因

SimpleCov.start を rails_helper.rb の先頭に書いてなかったのが原因だった。
ドキュメントにも太文字でtopだと書いてあった。

Similarly to the usage with Test::Unit described above, the only thing you have to do is to add the SimpleCov config to the very top of your Cucumber/RSpec/whatever setup file.
Add the setup code to the top of features/support/env.rb (for Cucumber) or spec/spec_helper.rb (for RSpec).

https://github.com/colszowka/simplecov#use-it-with-any-framework

対処

SimpleCov関連の設定処理は spec/support/simplecov.rb にまとめていて、 Dir[Rails.root.join(‘spec/support/**/*.rb’)].each { |f| require f } で実行していたが、rails_helper.rb の先頭で require ‘./spec/support/simplecov’ を実行するようにしたらApplicationHelperのカバレッジが測定されるようになった。

メモ

SimpleCovはstartメソッドの中でRuby標準ライブラリのCoverage.startを実行している。Helper(module)がCoverage.startの実行前に読み込まれてしまってカバレッジの測定対象に入っていなかったのだろうか。(未確認)

RailsのSystem specで Net::ReadTimeout が発生する

症状

RailsのSystem specを実行すると、以下のエラーで spec が失敗する

     Net::ReadTimeout:
       Net::ReadTimeout with #<TCPSocket:(closed)>
  • ローカル環境では最初のテスト実行時だけ発生する。2回目から発生しなくなる。
  • CI環境でたまにランダムで発生する。

原因

どうやらassetsのコンパイルに時間がかかっているためにSeleniumがタイムアウトしているようだった。(確かにこのサイトはassetsのscss, javascriptが巨大で、最初のアクセスにかなりの時間がかかっていた)

ローカル環境では一度assetsがコンパイルされていればキャッシュがあるため1度だけ発生していたようだ。

CI環境では以下の記事と同様に事前にassets:precompileすることで解決した。
https://jtway.co/4-lines-to-speed-up-your-rails-test-suite-on-ci-744e4326e8a3

# config/environments/test.rb
Rails.application.configure do
  :
  if ENV['CI']
    config.assets.digest = true
    config.assets.debug = false
    config.assets.compile = false
    config.assets.js_compressor = Uglifier.new(harmony: true)
  end
end
# .circleci/conf.yml
    :
    steps:
    :
    - restore_cache:
        keys:
          - asset-cache-{{ arch }}-{{ .Branch }}
          - asset-cache-
    - run: bundle exec rails assets:precompile
    - save_cache:
        key: asset-cache-{{ arch }}-{{ .Branch }}-{{ epoch }}
        paths:
          - public/assets
          - public/packs
          - tmp/cache/assets/sprockets

ThinkPad Carbon で仮想化を有効にできない

ThinkPad X1 Carbon 6th Gen (2018モデル) の Windows10 Pro。Dockerを入れようとしたところ、仮想化が無効になっているためインストールに失敗してしまった。

Windows10のヘルプの通りに「PowerShell を使用して Hyper-V を有効にする」を実行したところ、「Windowsの機能」のHyper-Vにチェックは入っているが、タスクマネージャーのパフォーマンスでは仮想化は「無効」のまま、という状況になってしまった。Dockerはインストールできないまま。

「Windowsの機能」で一度Hyper-Vのチェックを外してみると、Hyper-Vにチェックが入れられない状態になった。

原因はBIOSの設定で仮想化機能が無効化されていることのようだった。
BIOSのVirtualization設定でCPU Virtualization Technologyを有効化すると、「Windowsの機能」でHyper-Vにチェックを入れることができるようになり、タスクマネージャーで仮想化が「有効」になったのが確認できた。Dockerも無事インストールができた。

「Windowsの機能」のGUIでHyper-Vにチェックが入れられない状態でもPowerShellを使うと有効化できないにも関わらずチェックが入ってしまうようだった 😇

firebase init で認証エラーが出る

firebase init コマンドでプロジェクトを初期化しようとしたらエラーになり、ログを見ると認証エラーのようだった。(エラーログは勝手に消えてしまった)

認証トークンがおかしくなったのかなと思い、 firebase login –help を見てみると、–reauth といオプションがあるのに気がついた。

$ firebase login --help
Usage: login [options]

log the CLI into Firebase

Options:
  --no-localhost  copy and paste a code instead of starting a local server for authentication
  --reauth        force reauthentication even if already logged in
  -h, --help      output usage information

これで認証トークンのリフレッシュができるのかな、と思い、firebase login –reauth を実行してみたら無事に firebase init ができるようになった。