Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ: システムテスト用headlessドライバにCupriteが追加、rails-mini-profiler、Jeremy Evansインタビューほか(20210810)

こんにちは、hachi8833です。今回は短縮版でお送りします。

週刊Railsウォッチについて

  • 各記事冒頭には🔗でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙏

TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

🔗Rails: 先週の改修(Rails公式ニュースより)

前回の公式情報の続きです。

🔗 redirect_toallow_other_hostオプションが追加

許可されていないオープンリダイレクトでエラーをraiseする。
redirect_toallow_other_hostオプションを追加。ActionController::Base.raise_on_open_redirects = trueでこの振舞いをオプトインできる。
Gannon McGibbon
同Changelogより


つっつきボイス:「許可されていないオープンリダイレクトをraise_on_open_redirectsコンフィグでエラーをraiseできるようになった」「ここで例外を出してくれるのは優しいですね」「基本的にはraise_on_open_redirects = trueにしたうえで、オープンリダイレクトを許す場合にのみredirect_toreirect_back_or_toの引数にallow_other_host: trueを追加する感じで使うんでしょうね」

「URL直書きでよそのサイトにリダイレクトするのは基本的にはイレギュラーですが、その必要が生じることもありうるので、許可されてないサイトへのリダイレクトをraiseするかどうかを選べるのはいい👍」

参考: オープンリダイレクトとは(Open-Redirect) | 【情報処理 用語集】

後でガイドの更新を見ると、現行はデフォルトがfalseで、7.0になったらデフォルトでtrueにするようです。

# guides/source/configuring.md#1098
#### For '7.0', defaults from previous versions below and:

+- `config.action_controller.raise_on_open_redirects`: `true`
...

#### Baseline defaults:

+- `config.action_controller.default_protect_from_forgery`: `false`

🔗 システムテスト用に登録可能なheadlessドライバリストのpoltergeistとcapybara-webkitが非推奨化されてcupriteが追加

Poltergeistとcapybara-webkitは既にメンテされなくなっている。
* https://github.com/teampoltergeist/poltergeist
* https://github.com/thoughtbot/capybara-webkit

たまたま以下の利用法を見かけた。

https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/system_testing/driver.rb

近年はもっぱらSeleniumとHeadless Chromeが使われており、poltergeistやcapybara-webkitの利用は推奨されない。
CupriteはPoltergeistに代わるよい選択肢(参考)。
ガイドのPoltergeist関連部分はCupriteに置き換えるのがよいだろう。
同PRより


つっつきボイス:「お〜poltergeistとcapybara-webkitがついに非推奨化」「poltergeistはさすがに非推奨化でもいいかな」「今後はCupriteですか」「poltergeistとcapybara-webkitもしばらくは使えますね」「プルリクメッセージで引用されているEvil MartiansのCuprite記事は翻訳版が以下にあります↓」

2020年のRailsでブラウザテストを「正しく」行う方法(翻訳)

「お、このプルリクを出したYusukeIwakiさんは、まさにこの間の銀座Rails#34でPlaywrightやCupriteなどのドライバについて発表していた人ですね↓(ウォッチ20210803)」「あ、ホントだ」

参考: なんだかんだで1年半くらいRuby向けにブラウザの自動操作ライブラリを作っている - YusukeIwakiのブログ

🔗 Float::NaNBigDecimal::NaNのダーティーチェックを修正

RubyのFloat::NaNBigDecimal::NaN特殊な値であり、==で比較できない。
Marcelo Lauxen
同PRより


つっつきボイス:「Float::NaNBigDecimal::NaNがゼロ除算などで生成されたときの問題が修正されたんですね」「#41663のバグを踏んで見つけたのか」

      def test_changed?
        type = Decimal.new
        assert type.changed?(0.0, 0, "wibble")
        assert type.changed?(5.0, 0, "wibble")
        assert_not type.changed?(5.0, 5.0, "5.0wibble")
        assert_not type.changed?(5.0, 5.0, "5.0")
        assert_not type.changed?(-5.0, -5.0, "-5.0")
        assert_not type.changed?(5.0, 5.0, "0.5e+1")
+       assert_not type.changed?(BigDecimal("0.0") / 0, BigDecimal("0.0") / 0, BigDecimal("0.0") / 0)
+       assert type.changed?(BigDecimal("0.0") / 0, 0.0 / 0.0, 0.0 / 0.0)
      end

🔗 ActiveModel::Errors#inspectのメッセージをスリム化

# 同PRより: 従来
#<ActiveModel::Errors:0x00007ff68cda24f8 @base=#<Foo id: 6, created_at: "2021-07-09 04:28:48.056662000 +0000",
updated_at: "2021-07-09 04:28:48.168576000 +0000", email: "user@example.com", name: "Foo Bar", company: "Foo",
activated_at: "2021-07-09 04:28:39.039853000 +0000">, @errors=[]>

# 修正後: Errorsが空の場合
#<ActiveModel::Errors []>

# 修正後: Errorsが空でない場合
#<ActiveModel::Errors [#<ActiveModel::Error attribute=base, type=invalid, options={}>]>

つっつきボイス:「ActiveModel::Errors#inspect@errorsがある場合はその配列を出力して、@baseを出力しないように変更したのか」「通常ActiveModel::Errorsのオブジェクトをinspectするときは@errorsをチェックしたいはずなので、inspectではActiveModel::Errors@errorsに限定して配列を出力したいということかなと思いました」

🔗 Middleware#removeを追加

削除するミドルウェアが存在しない場合にraiseするMiddleware#removeを追加。
Middleware#removeの動作はMiddleware#deleteと同様だが、ミドルウェアが存在しない場合はエラーをraiseする。
Alex Ghiculescu, Petrik de Heus
同PRより


つっつきボイス:「以前は存在しないミドルウェアを削除しようとしても何も起きなかったのをエラーをraiseするMiddleware#removeが追加された」「割とシンプルな改修ですね」

「ところでMiddleware#removeは最近見たような気がする🤔」「あ、以前の#42655↓はMiddleware#deleteを修正して存在しないミドルウェアでエラーをraiseするようにしたけど破壊的変更の影響範囲が大きかったので、#42821ではMiddleware#deleteを元に戻してからMiddleware#removeを追加してこちらでエラーをraiseするようにしたのか」

🔗Rails

🔗 rails-mini-profiler(Ruby Weeklyより)

hschne/rails-mini-profiler - GitHub


つっつきボイス:「rack-mini-profilerはよく使われていますけど、それとは違うんでしょうか?」「READMEによるとrack-mini-profilerやScount APMなどのAPMツールに強くインスパイアされてこのrails-mini-profilerを作ったそうです」

MiniProfiler/rack-mini-profiler - GitHub

「お〜、なかなかいい感じにビジュアライズしている」「どのAPIが何秒かかるみたいなのが一覧できるんですね」


rack-mini-profiler READMEより

# rack-mini-profiler READMEより
# routes.rb
Rails.application.routes.draw do
  ...

  mount RailsMiniProfiler::Engine => '/rails_mini_profiler'
end

「APIのみのRailsアプリでも使えるみたい」「さすがにproduction環境は想定してないようです」「でしょうね」「ローカル開発で便利そうだし、プロファイリングするリクエスト数を絞り込めるみたいなのでproductionでも使いようがあるかも: とりあえず入れてみてもよさそう👍」「後で入れてみようっと」「ハリネズミがかわいい❤️」

# rack-mini-profiler READMEより
RailsMiniProfiler.configure do |config|
  config.enabled = proc { |env| env.headers['RMP_ENABLED'].present? }
end

🔗 RSpecでbullet gemを無視する方法(Ruby Weeklyより)

flyerhzm/bullet - GitHub


つっつきボイス:「この記事は、bullet gemに限らず、RSpecのテストで特定の機能を動かしたくないときに使える簡単なベストプラクティスという感じかな」「そうそう、こうやってテストヘルパーで無効にしたりしますね」

# 同記事より
config.before(:each, bullet: :skip) do
  Bullet.enable = false
end

config.after(:each, bullet: :skip) do
  Bullet.enable = true
end

🔗Ruby

🔗 Rubyの新しいデバッガを紹介

ruby/debug - GitHub


つっつきボイス:「先週紹介しそびれましたが、最近ruby/debugで頻繁にコントリビュートしているst0012さんの記事です」「お〜スタンくんが中の人になってる」「ruby/debugが触れるレベルになってきたところなのでこういう記事はありがたいですね👍」

🔗 Ruby 3でHTTPサーバーをスクラッチから作った(Ruby Weeklyより)


つっつきボイス:「RubyのFiberやRactorなども使ってHTTPサーバーをスクラッチから書く記事か」「久々のマルチスレッドサーバー系記事ですね」「こういうシンプルなコード例を手元で動かして試せるのがよさそう」

# 同記事より(コメントは省略)
def start
  queue = Ractor.new do
    loop do
      conn = Ractor.receive
      Ractor.yield(conn, move: true)
    end
  end

  WORKERS_COUNT.times.map do
    Ractor.new(queue, self) do |queue, server|
      loop do
        # this method blocks until the queue yields a connection
        conn = queue.take
        request = RequestParser.call(conn)
        status, headers, body = server.app.call(request)
        HttpResponder.call(conn, status, headers, body)
      rescue => e
        puts e.message
      ensure
        conn&.close
      end
    end
  end

  listener = Ractor.new(queue) do |queue|
    socket = TCPServer.new(HOST, PORT)
    socket.listen(SOCKET_READ_BACKLOG)
    loop do
      conn, _addr_info = socket.accept
      queue.send(conn, move: true)
    end
  end
  Ractor.select(listener)
end

「これで思い出したんですが、最近のPHP 8.1にもついにfiberが入ったという記事を見かけました」「お、このPHPのRFCですね↓」「fiberってRuby独自の概念かと思ってたけどコンピュータサイエンスの用語だったのか〜」

参考: PHP: rfc:fibers
参考: ファイバー (コンピュータ) - Wikipedia

🔗 Jeremy Evansインタビュー


つっつきボイス:「少し前の記事ですが、『Polished Ruby Programming』の著者であるJeremy Evansさんのインタビューです」「お顔初めて見ました」「Jeremy EvansさんはSequelのメンテナーやっているのが有名かも」

「記事の方は、どうやらJeremy Evansさんが昨年のRubyPrize 2020で受賞したタイミングでのインタビューみたいですね」「へ〜、Jeremy EvansさんはOpenBSD版のRubyもメンテしているのか」「OpenBSD版のRubyはこの人にかかっているんですね」「この記事訳してみたいです」

参考: OpenBSD - Wikipedia

🔗『Polished Ruby Programming』

「『Polished Ruby Programming』は自分も読んでいるところですが、Ruby中級者がスキルアップするのにいい本だと思いました」「自分も読んでみて同感です」

「同書の特徴は、Rubyに特化していて他の言語にはなかなか応用できなさそうなところ: Java的なオブジェクト指向やデザインパターンの流儀をRubyに持ち込んだ形で書かれた本はよくありますけど、この本はそれをいったん忘れてRubyならではの最適化された書き方を再定義しているところがありますね」「あ〜そういう感じですか」「この本でのRubyの書き方はたとえばJavaにはなかなか持ち込めないんじゃないかなと思ったりしました」「そうかも」「内容はとてもいいです👍」「今ポチりました〜」

「同書はRubyKaigiについてこられる中級者上級者なら問題なく読めると思います」「そうですね」「逆にRuby初心者にはハードでしょうね」「この本の書き方が当たり前だと思うと他の言語がやりにくくなったりして」

🔗 その他Ruby(Ruby公式ニュースより)


つっつきボイス:「Ruby Award 2022の募集が始まった🎉」「毎年12月に福岡県で開催されているRubyのイベントですね」「自社でRubyやRailsを使った試みをやっていれば応募できます」

🔗 その他

🔗 n月刊ラムダノート


つっつきボイス:「Rubyコミッターのmametterさんがこの『n月刊ラムダノート』に寄稿していたのでさっきポチりました」「計算機好きのための技術解説情報誌ですか」「紙でしか買えないのかと思ったらPDF版も買えることにやっと気づきました」「PDF版の方がちょっと安い」「ページが多すぎなくて気軽に読めるのがよさそう」「まとめ版もあるのでポチってみた」


今回は以上です。

バックナンバー(2021年度第3四半期)

週刊Railsウォッチ: Rubyの可変長アロケーションプロジェクト、サーキットブレーカーgem、EC2-Classicが終了へほか(20210804後編)

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

Ruby 公式ニュース

Rails公式ニュース

Ruby Weekly


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。