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

週刊Railsウォッチ(20200518前編)スライド『令和時代のRails運用』、Ruby 3.0のキーワード引数変更リスケ、Action CableのCLIほか

こんにちは、hachi8833です。Rails 4.2.11.3セキュリティリリースが出ました(Rails公式ニュースより)。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

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

以下の更新情報とコミットリストから見繕いました。

なお「A May of WTFs」というカテゴリがDiscourseのRails SNSに追加されたそうです。


つっつきボイス:「A May of WTFsって新しいポエムの書き場所か何か?😆」「Railsのissueに書きにくいようなことを書ける場を提供するということだそうです: 何でWTF(What the Fxxk)なのかわかりませんけど😆」「これとか今週の更新情報がよく出るようになったあたり、情報発信とかフィードバックをもっと頑張ろうというRails公式の意思を感じますね💪」「そうかも」「前は今週の更新情報も出たり出なかったりでしたし😆」「そのあたりに手が回るメンバーが増えたかやる気が出たかのかなと☺️」「公式情報発信の頻度が上がるのはいいですね👍」

そして@kamipoさんがついに1000プルリク突破です🎉。

minimummaximumでタイムゾーンが取れない問題を修正

# activerecord/lib/active_record/relation/calculations.rb#L309
        type_cast_calculated_value(result.cast_values.first, operation) do |value|
-         if value.is_a?(String) &&
-             column = klass.columns_hash[column_name.to_s]
-           type = connection.lookup_cast_type_from_column(column)
+         if type = klass.attribute_types[column_name.to_s]
            type.deserialize(value)
          else
            value
          end
        end

つっつきボイス:「groupした日時とかをminimumで取ってもTimeWithZoneクラスになってなかったとかそういう感じかな↓」「今までは…Timeクラスだった😆」「そうそう😆」

# activerecord/test/cases/calculations_test.rb#L1075
+ def assert_minimum_and_maximum_on_time_attributes(time_class)
+   actual = Topic.minimum(:written_on)
+   assert_equal Time.utc(2003, 7, 16, 14, 28, 11, 223300), actual
+   assert_instance_of time_class, actual
+
+   actual = Topic.maximum(:written_on)
+   assert_equal Time.utc(2013, 7, 13, 11, 11, 0, 9900), actual
+   assert_instance_of time_class, actual
+
+   expected = {
+     false => Time.utc(2003, 7, 16, 14, 28, 11, 223300),
+     true => Time.utc(2004, 7, 15, 14, 28, 0, 9900),
+   }
+   actual = Topic.group(:approved).minimum(:written_on)
+   assert_equal expected, actual
+   assert_instance_of time_class, actual[true]
+   assert_instance_of time_class, actual[true]
+
+   expected = {
+     false => Time.utc(2003, 7, 16, 14, 28, 11, 223300),
+     true => Time.utc(2013, 7, 13, 11, 11, 0, 9900),
+   }
+   actual = Topic.group(:approved).maximum(:written_on)
+   assert_equal expected, actual
+   assert_instance_of time_class, actual[true]
+   assert_instance_of time_class, actual[true]
+ end

「このissueは実はBPSのbabaさんが上げてて↑、@kamipoさんが半日経たずに修正したものでした」「babaさんこれ踏んだのか😆」「昨日社内Slackに『変なバグ踏んだ』ってこれが上がってました😆」「自分もあんまり使わないメソッドだから踏んだ人少ないかも☺️」「Active Recordにminimumとかmaximumみたいなメソッドがあるとは😆」


これは#39039の逆方向になる。
#39111ではdateカラムのminimummaximumをデータベースのカラム型で型キャストすることで修正したが、カラム型にはタイムゾーンを認識している属性がないので、属性の型はカラム型より常に優先されるべき。これに関連する#39248でそのことに気づいた。
タイムゾーンコンシャスな属性が動くようにするため、#39039で期待される部分をrevertした。
同PRより大意

先行していた以下の#39039もつっつきで取り上げたのですが、上述のとおり取り消されています。

「Active Recordってやっぱ難しいよ🤣」「🤣」「だってRDBに型があってRubyの世界にも型があって、それを実行時に解決しようとしてるんだから、そりゃ難しい😆」

新機能: テストファイルのパターンを環境変数で設定可能に

テストファイルのパターンを環境変数で設定可能にする
次の2つの環境変数でテストファイルのパターンを設定できる: DEFAULT_TESTはテストするファイルの設定、DEFAULT_TEST_EXCLUDEはテストから除外するファイルの設定。
従来これらの値はハードコードされていたので、デフォルトで除外してはいけないテストカテゴリ(スモークテストなど)を追加できなかった。
同PRより大意


つっつきボイス:「テストのデフォルト対象ファイルを環境変数で指定できるようになってる」「通常は実行したくないテストを除外したりとか」「今までだとRSpecでタグを使って解決していた話なのかも?🤔」「それっぽいですね」「実行がめちゃめちゃ遅いテストってありますよね😆」

type_castにカラムを渡すことが非推奨化

# activerecord/lib/active_record/connection_adapters/abstract/quoting.rb#L20
      def type_cast(value, column = nil)
        value = id_value_for_database(value) if value.is_a?(Base)

        if column
-         value = type_cast_from_column(column, value)
+         ActiveSupport::Deprecation.warn(<<~MSG.squish)
+           Passing a column to `type_cast` is deprecated and will be removed in Rails 6.2.
+         MSG
+         type = lookup_cast_type_from_column(column)
+         value = type.serialize(value)
        end

        _type_cast(value)
      rescue TypeError
        to_type = column ? " to #{column.type}" : ""
        raise TypeError, "can't cast #{value.class}#{to_type}"
      end


つっつきボイス:「今までそんなことができてたとは😳」「ああなるほど、SQLiteのデータは内部的に全部stringになってるので、type_castはそういうキャストをサポートするためのメソッドなのか」「あ〜そういえばそんな話ありましたね😳」

値をデータベースが理解可能な型にキャストする。たとえばSQLiteはdateを理解できないので、このメソッドでDateをStringに変換する。
type_cast APIドキュメント

「そういえば今週はSQLite絡みの改修がちらほらあった気がします」「プルリクメッセージからリンクされてるAPIを読むと、type_castを呼ぼうとしていたら既に何かがおかしいと書いてある😆」「まったくそのとおり😆」「そういう間違いが起きないように非推奨化すると」「データベースには型があるんだからそれを使いましょうと」

型キャストに使う型情報は型オブジェクトとは完全に別物なので、Rails 6でカラムをtype_castに渡すとおかしなことになりやすい。詳しくは以下のコメント参照:
rails/quoting.rb at 28d815b89487ce4001a3f6f0ab684e6f9c017ed0 · rails/rails
これはレガシーなバインド(4.2形式の[column, value]という配列)をコネクションのクエリメソッドに渡すことも非推奨化する。このようなレガシーフォーマットは後方互換性のために維持されていたが、自分はキャスト済みバインド形式(キャスト済みの値の配列)をサポートしているので、既存のバインド形式2種類を構築するよりはこのバインド形式を構築する方がやりやすい。
同PRより大意

UrlGenerationエラーで”did you mean”をサポート


つっつきボイス:「UrlGenerationって何だっけ、あルーティングか↓」「これをdid you meanしてくれるようになったの嬉しい😂」「これはたしかにありがたい🙏」「いい👍」


同PRより

「ほら、こういうルーティングって『もしかしたらこの書き方でイケるかも?』ってエイヤで書くことあるじゃないですか😆」「やるやるやります😆」「やってみて『この書き方やっぱダメか〜ゴメンナサ〜イ』って😆」「URLヘルパーに『こんなハッシュを渡したらうまくやってくれるかな?』って渡してみると案外拾ってくれたりすることありますし😆」

「Railsのキャリアが浅い人にはこういう機能は特にありがたいでしょうね: 逆にRailsでさんざんつらい目に遭った人だと『やっぱダメだよね😇』って諦めがち🤣」「『きっとできないと思ってたし😎』とか🤣」「こうやってRailsの機能がいろいろ至れり尽くせりになるのってスゴいことだなって思います😋」

「これはさっきのA May Of WTFsの以下のエントリをきっかけに改修したそうです↓」「Railsのリポジトリもissueが大量に増えまくってますし、ふわっとしたissueって立てづらくなってるので、そういうのを投げられるところができるというのはいいですね👍」

参考: Provide a hint when unable to pluralize a route? - A May Of WTFs - Ruby on Rails Discussions


今見ると、その後で「初心者向けにインスタンス変数のタイポもdid you meanして欲しい」というリクエストとプルリクも上がってますね。

Rails

Ruby 2.7〜3.0のキーワード引数変更がリスケに

こちらについては一足先に別記事を出しました。

Ruby 3.0のキーワード引数変更のスケジュールが変更に


つっつきボイス:「つい2時間ぐらい前に出たスクープです」「あ、これマジで?!😳」「しかもMatzのエントリ自体もさっきのA May of WTFsに投稿されていました」「へぇ〜、MatzがRailsのところに書き込むのは珍しいかも😳」「自分も初めて見たかも😳」

「DHHを含むRailsコミッターから陳情が出るとは😳」「それだけキーワード引数変更の対応がつらかったんでしょうね」「それもさることながら、Rubyをアップグレードしない人たちが出てくることの方を懸念したのかもしれませんね🧐」「あ〜たしかに」「自分らも何かあったらコワイのでまだRuby 2.7にアップグレードしてませんし😆」

「ところで今新しくRailsアプリ作るときはどうしてます?」「Rails側の対応もだいぶ進んできてはいるので、新しくやるならRuby 2.7でもいいんじゃない?って最近は思ってます😋」「なるほど!」

「来たる2.7.2ではキーワード引数のwarningをなくすか減らすかもしれないそうです」「キーワード引数の変更によってgemで何かが壊れること自体はそうそうないんじゃないかなと思うんですけど、自分たちにとってはどちらかというとキーワード引数のdeprecation warningが大量に出ることの方が問題なんですよね😆」「それです😆」「あのwarningが出ないのであれば2.7に上げてもいいんじゃないかって思いますし☺️」

「自分らとしてはあのキーワード引数のdeprecation warningだけを黙らせたいんですよ: 他のdeprecation warningはむしろ黙らせたくない」「それはたしかに」「今出回ってる解決方法のひとつにdeprecation warning自体を止めるというのがありますけど、やりたいのはそれじゃないんだよぉぉ😆」「必要なwarningは鳴ってくれないと困る〜😆」「というような話が巡り巡って今回の結論に至ったのかなと思いました☺️」「上の投稿では、どの辺がつらいかをコメントで教えて欲しいとのことでした」

「コメント見るととりあえずRSpecの人たちが困ってるようです」「それは間違いない😆」「😆」「RuboCopとかもそうですけど、ASTを追いかけている人たちはいろいろつらいでしょうね😭」


なお今後のキーワード引数変更の進め方の議論は以下のissueで進められるそうです。

スライド『令和時代のRails運用』


つっつきボイス:「社内Slackにあげていただいたスライドです」「そうそう@joker1007さんのスライド、これどこで発表したんでしょうね?🤔」「明日(5/15)の銀座Rails…じゃないか😆」

「古代、中世、近代、現代という区分😆」「自分とこって中世なの〜?😆」「反論出そう😆」「まあ中世であることがいけないわけではなくて、どれであっても運用をヘルシーに保つのは大変だよねと言いたいんじゃないかと思いますし🧐」「だと思いますけど😆」「インフラエンジニアを潤沢に投入できる案件ならいいんですけど潤沢じゃない方が普通なので、ヘルシーな運用管理はいろいろ大変😅」「ですよね😅」「この運用でしかやってはいけないというレベルまで運用ワークフローをがっちり固められれば古代や中世でも別にいいと思いますし、想像ですけどNetFlixとかAWSなんかは内部でそういう感じでやってるんじゃないかなって思いますし☺️」「なるほど!」

「スライドはいろいろ納得ですね😋: 結局一番厄介なのはスライドにもあるように『秘匿情報をどう管理するか』になりますし」「ですね」「やっぱりKMSが必要ということでしょうか?」「いえ、KMSに拘る必要はありませんね: 大事なのはKMSには暗号化設定そのものは入れてないというところで、KMSには鍵だけを入れて、暗号化した設定情報はファイルの方に存在します🧐」「なるほど😅」「ともあれだいたいこういう形になるでしょうね: secretsは設定ファイルなり環境変数なりに入れてプロセスからは自由に読める形にするしかないよね、ということをスライドで納得できたのが自分としてはよかったと思います😋」「ふぅむ」

「スライドのロギングドライバの話も数年前から割とこんな感じで行われてますね↓」

「デプロイの話ではCapistranoのプラグインを自作してる↓: インターフェースはCapistranoだけどやってることはCapistoranoのビルトイン機能とだいぶ違うものになるわけですけど、それも含めてだいたいこういう形になりますよね😆」

「コンテナに全部を乗っけるとコンソールを取るにもコンテナ立ち上げが必要になる、という話もありますね」

「知見のある人が年に1度ぐらいこういう話をしてくれるのはありがたい🙏: 変わっていない知見があっても、それが変わっていないことがわかるのが助かります😋」「そうですね😋」「そして変わった部分を知ってショックを受ける人たちもいたりするという😆」


あとでわかりましたが、@joker1007さんが発表したスライドは以下の「シューマイ」というイベントでした(また見逃してた…😭)。他にもいろいろ発表されていました。

rodauth 2.0がリリース(Ruby Weeklyより)


つっつきボイス:「rodauthはy-yagiさんの好きなRodaというフレームワークを使った、Deviseのオルタナ的な認証gemです」「速いという噂のヤツですね」「設計もキレイみたいです❤️」

Ruby: 認証gem ‘Rodauth’ README(翻訳)

「以前のroda-railsがRails 4.2までしか使えなかったんですけど、rodauth 2.0はRails 6でも動くようになっているみたいです」「へぇ、rodauthはJWTやWebAuthとかもサポートしてるのか、なかなか頑張ってる感: 登録画面みたいなものがあまり要らなくて純粋に認証+JWT基盤があればいいような案件でうまくはまるかも😍」「おぉ」「Deviseだと登録画面とかにもいろいろ絡んでくるから複雑になりやすいんですよね😢」「たしかに」

「Deviseのメリットは、登録画面やログイン画面やパスワード再設定画面みたいな認証関連の画面を一から作るのが面倒なときにdevise.ja.ymlを引っ張ってくれば圧倒的な短期間で作れるということでしょうね😆」「そうそう😆」「だからこそそういう部分が足を引っ張ったりもするんですけど😆」

「この間Deviseでちょっとテスト書こうと思ったら書きづらいのなんの😅」「Deviseのテストはもう地獄👿」「ほんと地獄でした😭、ログイン認証のテストとかどう書けばいいの?って」「その辺はDeviseのHow-To Wiki↓にバッドノウハウの塊みたいなのがひととおり載ってますけど🤣」「ははは😅」「ともあれ、本来の認証gemとしてこのrodauthみたいなものが欲しいというのはありますね☺️」

[Rails] Devise Wiki日本語もくじ1「ワークフローのカスタマイズ」(概要・用途付き)


changelogより抜粋:

  • Ruby 1.8のサポート終了
  • 2要素認証の追加
  • WebAuthnの追加
  • フィールドのオートコンプリート属性サポートを追加
  • ルーティングを呼ぶ前にデフォルトでCSRFトークンを自動チェック
  • jwt_refreshactive_sessionsでリフレッシュ後に直前のJWTアクセストークンの利用を防止
  • その他多数

render_async 2.1.6がリリース(RubyFlowより)


つっつきボイス:「render_asyncもバージョンアップしたそうです」「render_asyncにバージョンアップの必要なところってそんなにあるのかしら?😆」「Turbolinks周りとかネステッドパーシャルの対応が改善されたりしてるのね」「Turbolinksなつかしい😆」「なつかしいですよね😆」「今もナチュラルに入ってるんでしょうか?」「入ってると思いますよ☺️」「なくなったという話はなさそう」「最近のTurbolinksはそんなに悪くないという話も聞きますけど、Turbolinksが入ってることを意識したくないというのはありますね😆」「それはそう😆」

Rails: render_async gemでレンダリングを高速化(翻訳)

「ちなみにrender_asyncでパーシャルの読み込みコードを書くと、従来のようにRails側でパーシャルをレンダリングしてビューに埋め込む代わりに、JavaScriptのAjaxコードを使って返してくれるようになります」「おぉ」「まあrender_asyncは非同期読み込みを頑張らずにやるという目的に非常に合致してると思いますし😆」「いいヤツなんですね😋」「いえ、どちらかというと付け焼き刃的に使えるツール🤣」「そっちですか🤣」「やりすぎると後々収拾がつかなくなったりしますし😇」「まさに付け焼き刃なツール😆」


同記事より:

  • Turbolinksを用いたページナビゲーション時にポーリングを停止するようになった
  • X-Requested-Withヘッダーがデフォルトで設定されるようになった
  • render_asyncによる読み込みのトグルでイベントの委譲ができるようになった
  • ネストしたパーシャルをTurbolinksで読み込めるようになった
  • 最後に

acli: Action CableのCLI

# 同リポジトリより
# Subscribe to channel (without parameters)
\s channel_name

# Subscribe to channel with params

\s+ channel_name id:1

# or interactively

\s+
Enter channel ID:
...
# Generate params object by providing keys and values one by one
Enter key (or press ENTER to finish):
...
Enter value:
# After successful subscription you receive a message
Subscribed to channel_name


# Performing actions
\p speak message:Hello!

# or interactively (the same way as \s+)
\p+

つっつきボイス:「Evil Martiansの人が作ったgemだそうです」「あ〜、対話型でAction Cableやれるのね」「俺たちが欲しかったのはもしかするとこれかもしれないというのはある😆」「おぉ😍」

「たしかにAction Cableで作っているときにRailsコンソールに相当するものがクライアント側に欲しいときってありますし😋」「これmrubyで書かれてるみたいですね😳」「これは嬉しいヤツでしょうか?」「まあAction Cableをちょっと試すのにいちいちWebSocketのJSコードを書くのがとにかく面倒😭」「みんなJavaScriptがキライ説😆」「JavaScript脳に切り替えるのが面倒だったりして😆」「あ、そっちか😆」「Action CableのコードをRubyで書いているのにデバッグコードをJSで書くのは面倒でしょう😆」

Rails 6にWebpackerとSprocketsが両方入ってる理由

「WebpackerとSprocketsが両方入ってる理由😆」「『DHHがそう言ったから』だそうです」「移行パスを考えたらいきなりSprockets消せないでしょう普通😆」「実際Sprocketsでないとどうしてもできない部分はWebpackerでは実装し直しが必要になりますよね🧐」「あ、そういうことか😆」「アセットプリコンパイルのタイミングでRubyのコンテキストを参照するようなコードはそのままだとWebpackerで処理できませんし、マニフェストファイルを書き出してそれを読み込ませたりするのは結構大掛かりな修正が必要になると思うんですよ☺️」「う〜む😅」

「そもそもRailsにWebpackerが入ることになったのってなぜなんでしょう?」「流行りだから😆」「流行りだからって入れていいんでしょうか😆」「まあWebpackそのものじゃなくてWebpackerという形で入ったのはどうかと思いますけど、エンジニアが全員Railsの人ならWebpackerはいい選択肢だと思いますね🧐」「そうかも」「だってWebpackerはRailsでしか使わないから😆」

「Webpackerが流行ってるのはわかるんですけど個人的にはいろいろ疑問が😅」「Railsのビューでjavascript_pack_tagみたいなものを書けるようにしたかったんでしょうね: いずれにしろWebpackとRailsをつなぐレイヤは必要だったでしょうし☺️」「でWebpackerがどこまでWebpackのマウントを取るかという部分で不評を買っているというか😆」「😆」「WebpackerがもっとピュアなWebpackを触るだけみたいなつくりになってたら違ったかもしれませんけど、WebpackerでWebpackのコンフィグを制御する方向に行ったのがちょっと残念ではありますね😢」


同記事より:

  • 両方入っているのを不思議に思うのはあなただけではない
  • 両方入っている理由
  • 両方あるのはどうかと思いませんか?
  • WebpackerまたはSprockets(または両方)を使う理由があるとすれば
  • 自分はSprocketsをやめるけど、皆さんはどうですか?

その他Rails


同サイトより


つっつきボイス:「Community Survey 2020というアンケートを募集していて、とりあえず自分は入れてみました😋」「もう10年も継続してるんですって」「2年おきにやってる?」「でも3年空いてるところもあるし😆」「ノリでやってる感😆」「そこそこ項目多いな〜」「後で気が向いたら入れてみよっと😆」「自分も気が向いたら😆」


「こちらは新人向けの記事です」「jnchitoさんはこういう記事をマメに書いてくれるのがいいですね❤️」「なるべく早く反応しようとか期限を切ろうとか」「つらいことがあったのかな😆」「つらいと思いますよ実際😭」「個別に指示していられなくなって記事書いてここ読んでくれという感じでできたのかなと☺️」「まあ新入社員にはこういうことをひととおり伝えないといけませんよね☺️」「たしかに」「Railsエンジニアに限らず広く一般に通用する話ですし👍」「プログラマーにも限りませんし😋」「社会人として働くうえで必要👍」


前編は以上です。

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

週刊Railsウォッチ(20200512後編)RubyのPStoreライブラリ、Lambda StoreのサーバーレスRedisは有能、Amazon Linux 2のライブパッチほか

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

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

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h


CONTACT

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