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

週刊Railsウォッチ(20200928前編)Ruby 3.0.0 Preview 1リリース、Rails Guidesにconcernsのドキュメント追加、Sidekiq 2020エディションほか

こんにちは、hachi8833です。次はKaigi on Railsですね。


つっつきボイス:「10月3日といえばもうすぐか」「Reject Kaigi on Railsも前日に開催されるそうです↓」「お〜Reject Kaigiもやるとは!」「Rejectが金曜日夜でKaigi on Railsが土曜日終日ですね」

「グッズも出てるそうです↓」「このスズリみたいに1個単位で扱えるところは運営の負担が小さいのがいいですよね☺️」

参考: kaigionrailsのオリジナルアイテム通販 ∞ SUZURI(スズリ)


なお、つい先ほど以下の見どころ記事を知りました。ご参考まで。

参考: Kaigi on Rails に koic, 9sako6, yucao24hours が登壇!見どころをご紹介します - esm アジャイル事業部 開発者ブログ


  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

臨時ニュース: Ruby 3.0.0 Preview 1がリリース

つっつき会後の先週土曜日にリリースされました。

RBS、Ractor、Thread#scheduler、右代入、endレスメソッド定義など盛りだくさんのPreview 1です。

ruby/rbs - GitHub

参考: ruby/ractor.ja.md at ractor · ko1/ruby

私も早速以下の手順で3.0.0-preview1をインストールしてみました。

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

以下のコミットリストのChangelogを中心に見繕いました。


つっつきボイス:「due dateは設定されてないけどマイルストーンが73%まで完了してる」「issueの件数がちょっと増えましたね」

RailsのGetting Startedガイドにconcernsセクションを追加

以下のディスカッションで知りました。

参考: Helping devs understand concerns faster - A May Of WTFs - Ruby on Rails Discussions

なお、Edgeguidesでこのconcernsドキュメントを読めます↓。

参考: 7.3 Using Concerns -- Getting Started with Rails — Ruby on Rails Guides


つっつきボイス:「Railsのconcernsをどう使うかという詳しい情報がついにRailsガイドに公式に追加されるそうです」「あれ、今までなかった?」「concerns、自分はあんまり好きじゃないけど、Railsではデフォルトでconcerns/ディレクトリが作られるし、concernsの公式ドキュメントはある方がいいでしょうね」

「ところで、concernsがぴったり合うコードというか、concernsで何かをきれいに解決できたのを見たことってごくごくまれにしかないんですよね😆」「たいてい特定のコントローラや特定のモデルのためのconcernsだったりしますし😆」「ソースコードを分割するためだけのconcernsとか、まあそれはそれでありかなと思いますけど」


「以下のAppSignalのconcerns記事↓はまだ読んでませんが、内容が近そうなのでここに貼っておきます」


肥大化したActiveRecordモデルをリファクタリングする7つの方法(翻訳)

ActionDispatch::Http::Request#POSTで無効なUTF-8エンコーディングを検出


つっつきボイス:「テストコードを見ると、invalidなUTF-8文字がPOSTパラメーターに含まれるときにうまくいかなかったらしい」「ははぁ、この%81EっていうのがUTF-8にない文字か↓」「"application/x-www-form-urlencoded; charset=utf-8"を指定してるのにinvalidなUTF-8文字でエラーが正しく出てなかったのを対応したんでしょうね」「%81Eみたいな文字がPOSTされることってありそう...」

# actionpack/test/dispatch/request_test.rb#L1054
+ test "POST parameters containing invalid UTF8 character" do
+   data = "foo=%81E"
+   request = stub_request(
+     "REQUEST_METHOD" => "POST",
+     "CONTENT_LENGTH" => data.length,
+     "CONTENT_TYPE" => "application/x-www-form-urlencoded; charset=utf-8",
+     "rack.input" => StringIO.new(data)
+   )
+
+   err = assert_raises(ActionController::BadRequest) { request.parameters }
+
+   assert_predicate err.message, :valid_encoding?
+   assert_equal "Invalid request parameters: Invalid encoding for parameter: �E", err.message
+ end

このプルリクでは2つのことを行っている。
1. この修正と同様に、ActionDispatch::Http::Request#POSTRequest::Utils.check_param_encoding(params)を呼ぶべき。現在はPOSTリクエストで不正な入力がコントローラに到達してしまうので、スタックでさらに例外をraiseすることでコントローラにこういうものが到達しないようにして不正なエンコーディングの入力を禁止すべき。
2. バイナリエンコーディング周りのロジックを若干変更。従来ActionDispatch::Http::Parameters#parametersのコントロールフローは以下のような感じだった。

  • query_paramsを取得(paramエンコーディングをチェックしていた)
  • request_paramsでマージ(これはparamエンコーディングをチェックして「いなかった」)
  • path_parametersでマージ(これはバイナリにエンコードされ、その後エンコーディングが設定された時点でエンコーディングをチェックする、こちらを参照)
  • バイナリエンコーディングを実行(必要な場合)

今はGETPOSTで無効なエンコーディングをraiseするようになっているので、ActionDispatch::Http::Requestでバイナリエンコーディングが行われるようにして、コントローラが望んでいないエンコーディングがバイナリにある場合にのみinvalid encoding例外を出すようにする必要がある。そのためには以下の変更が必要だった。

  • バイナリエンコーディングのロジックをActionDispatch::Request::Utilsに切り出す
  • GETPOSTでエンコーディングをチェックするより前にActionDispatch::Request::Utils#set_binary_encoding呼び出しを追加する
  • ActionDispatch::Http::Parameters#path_parameters=を変更して新しいインターフェイスを用いるようにし、ActionDispatch::Http::Parametersからバイナリエンコーディングのロジックを削除する
  • ActionDispatch::Http::Parameters#parameters#set_binary_encodingの余分な呼び出しを削除する(GETPOSTpath_parametersはすべてエンコーディングチェックより前の段階でのバイナリへのエンコーディングを義務付けられたため)
    同PRより大意

SKIP_TEST_DATABASE環境変数を追加


つっつきボイス:「なるほど、dbのセットアップでテスト用データベースを作らないようにしたい、それを環境変数で制御したいと」「Dockerでこのオプションが使いたいときとかありそう」

➜ SKIP_TEST_DATABASE=true bundle exec rake db:create
Created database 'db/development.sqlite3'

➜ bundle exec rake db:create
Created database 'db/development.sqlite3'
Created database 'db/test.sqlite3'

「Railsでdb:createをするとテスト用dbも作られちゃうんですよね」「たしかに」「そういうのが要らないときにこうやって指定できるといいでしょうね👍」

Mysql2Adapter#quote_stringのMySQL2エラーを変換


つっつきボイス:「一度取り下げたプルリクをリトライしてマージされたそうです」「take 2ってそういうことですか」「コネクションアダプタごとに振る舞いが異なっていた部分を揃えたように見える」

  • すべてのコネクションアダプタのexecuteが、コネクションエラー時にActiveRecord::InvalidStatementではなくActiveRecord::ConnectionNotEstablishedをraiseするようになった。
  • Mysql2Adapter#quote_stringが、MySQLサーバーに接続できなかった場合にActiveRecord::InvalidStatementではなくActiveRecord::ConnectionNotEstablishedをraiseするようになった。
    同PRより大意

「どういうときにquote_stringが失敗するんだろう?」「#40148を見るとMysql2::Client#escapeではmysql_real_escape_stringというC APIを使っていて、これは健全なコネクションが存在しないと動かないからって書かれている」「へぇ〜!」「その理由はコネクションのロケールによって振る舞いが変わるからか、なるほどなるほど」

「つまりこのAPIが失敗するということは健全なコネクションが存在しないことが原因なので、ステートメントが間違ってないのにInvalidStatementを返すのは不適切で、ConnectionNotEstablishedの方が適切という判断なんでしょうね」「なるほど!」

ActiveSupport::Subscriber.attach_toinherit_allオプションを追加


つっつきボイス:「あるとたぶんうれしい機能」「へぇ〜、こういうユースケースがあるとは」

ActiveSupport::Subscriber.attach_toinherit_allオプションを受け取れるようにする。これは以下のように、サブスクライバーがその先祖クラス内のメソッドのイベントにもサブスクライブできるようにすべきかどうかを指定する。

class ActionController::LogSubscriber < ActiveSupport::LogSubscriber
  def start_processing
  end
end

class CustomLogSubscriber < ActionController::LogSubscriber
  attach_to :action_controller, inherit_all: true
end

これでCustomLogSubscriberstart_processingのイベントにもサブスクライブする。
ただし新しいイベントサブスクライバーを登録する場合はActiveSupport::SubscriberActiveSupport::LogSubscriber内のpublicメソッドを無視している。
同PRより大意

Rails

小さくてレビューしやすいプルリクのガイド

クックパッドの中の人の記事です。


つっつきボイス:「以下の2つを実践したらプルリクがapproveされるまでの時間などが改善されたそうです」「"less than 100 additions"ってあるのはたぶん行数のことかな」「そんな感じでしょうね」「あ、そうかも」「単位がないと意味わかりませんし😆」

  • プルリクの追加は100行以下にすることを推奨
  • 完成していない機能でもmasterへのマージを許す


同記事より

「実際、masterに入れても影響が生じないようにプルリクを作るのは大事ですよね👍」「特に大人数になればなるほどこういう縛りが必要になってきたりしますし」

「先行してmasterに入れておかないといけない機能ってよくあるので、これはとてもわかる」「そうしないとrebaseがどんどん重くなっちゃいますし」「rebaseを小さくするには、マージしても壊れない程度の粒度でプルリクして欲しいですよね」

Railsでconstantizeを避けるべきもうひとつの理由(Ruby Weeklyより)


つっつきボイス:「Railsのconstantizeはセキュリティに注意しようってよく言われるヤツですね」

「ははぁ、const_missingを使っているgemがたまにあるから、const_missingがオーバーライドされると思わぬ挙動を招くということか」「簡単にexploitできちゃうって書かれてますね」

# 同記事より
2.7.0 :001 > module X
2.7.0 :002 >   def self.const_missing(name)
2.7.0 :003 >     puts "You tried to load #{name.inspect}"
2.7.0 :004 >   end
2.7.0 :005 > end
 => :const_missing 
2.7.0 :006 > X::Hello
You tried to load :Hello
 => nil

「安全のためにはRailsのclassifyを使いましょうということらしい↓」「このメソッド初めて知りました」「constantize、実は使ったことありますけど、constantizeが危険なのは前々からわかっているので、ちゃんとホワイトリストと照合してから使いましたし」「任意の文字列を取れるように書いたらダメですよね😆」


結論: Railsでconstantizeの利用は避けること。どうしても必要な場合はconstantizeを呼ぶ前に使ってよいクラス名リストと照合すること(チェックの前にclassifyを呼び出すのは問題ないが)。
(中略)
編集: 本記事の書き方の一部が少々曖昧だったようなので修正した。classifyを呼んでもコードが安全になるわけではない。classifyの呼び出し自体に害はないが、その後でconstantizeを呼ぶことが危険だと言いたかった。つまりclassifyを安全に呼び出して、許可されたクラス名リストと照合してから適切な操作を行える。
同記事より抜粋・大意

Sidekiq 2020エディションの新機能(Ruby Weeklyより)


つっつきボイス:「Sidekiq 2020の新機能だそうです」「ジョブエンジンをどれにするかは悩ましいんですよね、どうやっても複雑になるので」

mperham/sidekiq - GitHub

「SidekiqにPro版とEnterprise版もあるって初めて知りました」「へぇ〜、Enterpriseだとrate limitやsidekiqswarmも使えるのね」「Sidekiqの無料版でもプロセス数を増やせるようになったらいいな〜と思ったら、マルチプロセスはEnterproseのみか、残念😢」


sidekiq.orgより


前編は以上です。

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

週刊Railsウォッチ(20200915後編)RubyKaigi 2020 Takeoutの動画出揃う、イタリア語でRuby、AWS Summit Online開催中ほか

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

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

Rails公式ニュース

Ruby Weekly


CONTACT

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