- Ruby / Rails関連
週刊Railsウォッチ(20201208前編)レガシーRailsアプリを引き継ぐときの6つの作業、サーバーレスプロジェクトをRailsに移行ほか
こんにちは、hachi8833です。
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
- お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙇
今回は、BPS昼の定例勉強会でつっつき会を行いました。
⚓Rails: 先週の改修(Rails公式ニュースより)
公式の更新情報から見繕いました。
- 元記事: New Active Record and Action View capabilities, bug fixes and more! | Riding Rails
-
6.1.0マイルストーン: 6.1.0 Milestone 99%、オープンのissue残り1件(つっつき直前の時点)
つっつきボイス:「6.1 RC2が出ましたし、最終リリースまであとちょっとという感じになってきましたね」「お、今見るとマイルストーンでオープンのissueが0件になってる」「RC2でまたいくつかissueが追加される可能性はありそう」「最終リリースは年内ぐらいかな?案外来週ぐらいにしれっと出たりするかもしれませんけど」「Rubyとどっちが早く出るかな?」
その後、ウォッチ公開日のマイルストーンでオープンのissueは3件になっています。
⚓ 新機能: ERBでHashをHTML属性に変換できるようにする
ERBで以下のようにHashをHTML属性に変換して展開可能にする。
<input <%= tag.attributes(type: :text, aria: { label: "Search" }) %>>
<%# => <input type="text" aria-label="Search"> %>
ActionView::Helpers::TagHelper#tag_options
の実装を利用してERBと属性変換を組み合わることで、テンプレートでHTML文字列をtag
やcontent_tag
で置き換えなくてもやれるようにする。
同PRより大意
つっつきボイス:「attributes
ヘルパーメソッドが追加されたんですね↓」「上のようにハッシュをHTML属性として展開するのは前からできてたような気もしたけど、あれはSlimの書き方だったか」「ERBでもこれが使えるようになったのはいいですね👍」
# actionview/lib/action_view/helpers/tag_helper.rb#L56
+ def attributes(attributes)
+ tag_options(attributes.to_h).to_s.strip.html_safe
+ end
参考: slim/README.jp.md at master · slim-template/slim
以下の記事によるとHaml 4以降でもできるそうです。
参考: HAML 4+ expands nested element attributes - makandra dev
⚓ 新機能: where.associated
で関連付けにデータが存在するかどうかをチェックできるようになった
- PR: Add where.associated to check association presence by kaspth · Pull Request #40696 · rails/rails
以下は自分たちのアプリからの抜粋。
class Account < ApplicationRecord
has_many :users, -> { joins(:contact).where.not(contact_id: nil) }
end
この
user
は、contact
のdelegatedy typeであるcontactable
。contactable
を置き換えて1件のuser
をバックグラウンドで削除するというのはよくあるパターン。上の書き方では関連付けの先にデータが存在するかどうかだけを知りたい場合に構文が少々煩雑になるが、これを以下のように書けるようになる。
class Account < ApplicationRecord
has_many :users, -> { where.associated(:contact) }
end
これは#34727で追加された
where.missing
の鏡写しになる。
同PRより大意
つっつきボイス:「元のjoins(:contact).where.not(contact_id: nil)
は、joinした先のcontact_id
が存在するかどうかだけを確認したいときに使いそうなクエリですね: これはたしかに悩ましくて、contactに制約が付いていない場合なんかだとcontactにcontact_idの外部キーが存在しない可能性があるので、関連付け先にデータが確実に存在するかどうかを確認するにはjoins
を書かないといけなくなりますが、改修後はwhere.associated(:contact)
のように既に関連付けにデータが存在するという意味でassociated
を使って簡潔に書けるようになったということでしょうね」「なるほど」「こう書きたい気持ちはわかります」
「慣れるまではassociated
がコードで使われていてもすぐにピンとこないかも」「制約を付けて回避できるなら制約でやる方がいいでしょうね」「プルリクメッセージで引用されているwhere.missing
↓はassociated
と対照的に、関連付けが存在しないものをフィルタで取り出すメソッド」
⚓ rails stats
にCSSやERBの情報も表示するようになった
GitHubのモノリスのサイズ情報を得る方法を探していて、
rails stats
ではapp/viewsディレクトリやapp/assets/stylesheetsディレクトリの情報が含まれていないことに気づいた。
これらのフォルダについても情報を出せば便利だと思う。このプルリクはこれらをViewsとStylesheetsという項目として追加する。
つっつきボイス:「rails stats
の出力項目に情報が増えましたね」「ビューとCSSが今までなかったのか」
# railties/lib/rails/code_statistics.rb#L43
- def calculate_directory_statistics(directory, pattern = /^(?!\.).*?\.(rb|js|ts|coffee|rake)$/)
+ def calculate_directory_statistics(directory, pattern = /^(?!\.).*?\.(rb|js|ts|css|scss|coffee|rake|erb)$/)
なお手元のRails 6.0.3では以下のように表示されました。ここにはminitestのディレクトリも出力されていますが、試しにtestディレクトリを削除したら出なくなりました。
⚓ 新機能: リッチテキスト関連付けを一括でeager loadingできるようになった
Action Textは
with_rich_text_#{name}
ヘルパーを提供して、リッチテキストの関連付けを楽にプリロードできるようになっている。これは1個のモデル上の1個のリッチテキストフィールドではうまくいくが、リッチテキストフィールドが複数の場合は個別のフィールドを読み込むためにActionTextテーブルへのクエリが繰り返される。以下のようにモデルの中にユーザーが生成した動的なコンテンツが多数ある場合を考える。
class Page < ApplicationRecord
has_rich_text :header
has_rich_text :sub_header
has_rich_text :content
has_rich_text :aside
has_rich_text :footer
...
end
Page
のすべてのコンテンツをeager loadingで表示しようとすると以下のようになる。
Page
.with_rich_text_header
.with_rich_text_sub_header
.with_rich_text_content
.with_rich_text_aside
.with_rich_text_footer
.find(params[:id])
この場合Railsが6回もクエリを実行するとことになる(1回は
Page
の読み込み、5回は個別のActionText
読み込み)。
このプルリクはwith_all_rich_text
を追加する。これはeager_load
を使い、has_rich_text
関連付けへのリフレクションを行って、すべてのリッチテキストの関連付けを一括読み込みする。
Page.with_all_rich_text.find(params[:id])
その他
#37976によると現在のActionTextの内部は流動的とのことだが、この機能を今後のリリースに追加することに関心があるか、あるいはアプリケーション固有のヘルパーの方がよいかどうかをチェックして欲しい。
同PRより大意
つっつきボイス:「with_rich_text_*
はテンプレートで複数使うこともありそうなので、それをwith_all_rich_text
でひとつのクエリで書けるようにしたということか」「項目の数だけクエリが発行されなくて済むのはいいですね👍」
# actiontext/lib/action_text/attribute.rb#L50
+ def with_all_rich_text
+ eager_load(rich_text_association_names)
+ end
+
+ private
+ def rich_text_association_names
+ reflect_on_all_associations(:has_one).collect(&:name).select { |n| n.start_with?("rich_text_") }
+ end
⚓ travel_to
ブロックで日時をStringで取るときにアプリのタイムゾーンが使われるよう修正
バグのように見えるが、ドキュメントにこの機能の説明が見当たらなかった。
travel_to
は"2004-11-24 01:04:44"のようなstringを引数に取れるが、Stringで追加定義されるto_time
メソッドによってアプリケーションのタイムゾーン情報が失われてローカルに設定されてしまう。
# 現状
travel_to "2004-11-24 01:04:44" do
Time.zone.now.to_s(:db) # => "2004-11-24 06:04:44"
end
# 期待する動作
travel_to "2004-11-24 01:04:44" do
Time.zone.now.to_s(:db) # => "2004-11-24 01:04:44"
end
同PRより大意
参考: ActiveSupport::Testing::TimeHelpers
# activesupport/lib/active_support/testing/time_helpers.rb#L157
if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
now = date_or_time.midnight.to_time
+ elsif date_or_time.is_a?(String)
+ now = Time.zone.parse(date_or_time)
else
now = date_or_time.to_time.change(usec: 0)
end
つっつきボイス:「バグ修正のようです」「例を見るとたしかにtravel_to
にStringを渡した場合にタイムゾーンが変わってる」「内部でto_time
が呼ばれるとString形式の日時からタイムゾーンが落ちてたのか」「ここを意識したことがなかったということは、今までの自分はTimeオブジェクトを渡していたということかも」
「travel_to
はテストで使うメソッドでしたっけ?」「travel_to
のブロック内だけ日時を変更してテストしたいときなどに使いますね」
⚓Rails
⚓ レガシーRailsアプリを引き継いだときにやること6つ
つっつきボイス:「記事の冒頭に、Rails 1.0がリリースされてから15周年とある」「もうそんなに経つんですね」「動画配信サービスのHuluもRailsとは知らなかった」「おそらくすべてではなくユーザーが目にするフロント部分などで使っているんでしょうね」
「ドキュメントの確認や整備、カスタムフォルダが追加されているかの確認などはいずれも大事」「カバレッジを100%に持っていくのは大変ですけどね」「ルーティングやDB構造、デプロイやstagingサーバーの構築なども同じく重要」
「この記事ではそうした作業が終わってからLinterを入れるのか: 動くことを確認してからlintをかけないとどこで壊れたかわかりにくくなることを考えれば、一理ある」「たしかに」「そうしてひととおり動くようになってコードをきれいにしてからRailsやgemのアップグレードを始める」「レガシーアプリに対応するときに順序としては基本的にこういう形になるでしょうね」「定番の作業項目をチェックできるのはよさそう👍」
同記事見出しより:
- 1. コードレビューとローカルセットアップ
- ドキュメントの概要をレビュー(技術的負債もチェック)
- テストをレビュー
- ルーティングやデータベース構造をレビュー
- 残っているカスタムフォルダをレビュー
- 2. テストのカバレッジを100%にする(理想的には)
- 3. デプロイ方法のチェックとstagingサーバーのセットアップ
- 4. RuboCopとPrettierでコードベースにlintをかける
- 5. stagingとproductionにデプロイする
- 6. Rails、Ruby、gemをアップグレードする
⚓ サーバーレスプロジェクトをRailsに移行する(Ruby Weeklyより)
つっつきボイス:「AWS Lambdaで動かしていたプロジェクトがつらくなってきたのでRailsに引っ越したという記事です」「たとえばサーバーサイドで複雑な処理を行うような場合はLambdaに合わないことは考えられますね」「たしかに」「AWS Lambdaはマイクロなプロセスを発行することを主に想定していますし、マイグレーション的なしくみが組み込まれていないので、CRUD的なデータ操作や重たいバッチ処理を多用するような複雑な処理をLambdaですべてまかなうのはしんどいでしょうね」
参考: AWS Lambda(イベント発生時にコードを実行)| AWS
⚓ Matestack: HTMLやJSを書かずにRailsをリアクティブにするエンジン(Ruby Weeklyより)
つっつきボイス:「書き方がちょっと面白かったので拾ってみました↓」「Matestackを見た感じでは、Railsに独自フロントエンドを入れてすべてをRubyで書きたいという人は今も結構いるようですね」「Basecampが出しているstimulus jsも使いたくなかったりするのかな?」「まだ新しそうなので日本語圏では情報がなさそうですが、英語情報はそこそこ出始めてるみたい」
# 同リポジトリより
class Components::Card < Matestack::Ui::Component
requires :body
optional :title
optional :image
def response
div class: "card shadow-sm border-0 bg-light" do
img path: image, class: "w-100" if image.present?
div class: "card-body" do
heading size: 5, text: title if title.present?
paragraph class: "card-text", text: body
end
end
end
end
参考: Stimulus: A modest JavaScript framework for the HTML you already have.
以下のツイートはつっつき後に見つけました。
We're already excited! :) https://t.co/VeeayaPWbs
— matestack (@matestack) September 15, 2020
前編は以上です。
バックナンバー(2020年度第4四半期)
週刊Railsウォッチ(20201124)strict loading violationの振る舞いを変更可能に、Railsモデルのアンチパターン、quine-relayとさまざまなクワインほか
- 20201117後編 Rubyのパターンマッチングが3.0で本採用に、AWS Lambdaサイズを縮小する、AppleのM1チップほか
- 20201116前編 6.1のActive Storageでimage_processing gemが必須に、Webアプリ設計の変遷とフロントエンド領域の再定義ほか
- 20201111後編 RubyConf 2020が11/17〜19オンライン開催、GitHub Container Registryベータ開始、スマートロックほか
- 20201110前編 Rails 6.1 RC1がリリース、Railsアプリに最適なEC2インスタンスタイプ、n_plus_one_control gemほか
- 20201028後編 RuboCop 1.0.0 stable版がリリース、Ruby DSLのGUIフレームワークGlimmer、Keycloakほか
- 20201026前編 Shopifyのerb-lint gem、Form Objectを使いやすくするyaaf gem、railsrcの機能追加ほか
- 20201021後編 webpack 5リリースでWebpacker対応開始、AWS Lambda Extensions発表、Pythonにマクロ構文追加提案ほか
- 20201020前編 Percona Toolkitは優秀、Active Admin非公式ガイド、Railsをリアクティブにするガイドほか
- 20201013後編 ruby-type-profilerがtypeprofにリネーム、AWS API Gatewayの実行ログは便利、M5Stackほか
- 20201012前編 Railsの隠し機能routing visualizer、action_args gem、N+1用goldiloader gemほか
- 20201006後編 Rubyの
defined?
キーワード、Ractorベースのジョブスケジューラ、Caddy Webサーバーほか - 20201005前編 Ruby 2.7.2がリリース、Shopifyのモジュラー化gem「packwerk」、stimulus_reflexほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。