- Ruby / Rails関連
週刊Railsウォッチ: ActiveRecord::QueryLogs追加、spring gemがデフォルトから削除、fast_gettextほか(20210906前編)
こんにちは、hachi8833です。今週はいよいよRubyKaigi Takeout 2021ですね。
コミュニティ有志のみなさんへ: Day 3 後 #rubykaigi の内容を踏まえ、パーティやミートアップを開催しませんか? 関連イベントをリストアップするスプレッドシートを用意しているので、賛同いただけるイベントなどご自由にご掲載ください。会期中都度周知を予定しています! https://t.co/gcquu2WMat
— RubyKaigi (@rubykaigi) September 2, 2021
お知らせ: 来週の週刊Railsウォッチはお休みいたします🙇。
🔗Rails: 最近の改修(Rails公式ニュースより)
公式に追いつくべく今回は2回分取り上げます。
- 更新情報: ESM importmap and Marginalia | Riding Rails
- 更新情報: Good-bye classic mode, –skip-puma, –skip-gemfile.. hello weekday_options_for_select! | Riding Rails
import-mapsは先週取り上げたので(ウォッチ20210831)、importmaps-railsリポジトリを貼ります。
つっつきボイス:「DHHは今後このgemを推していくんでしょうね」「予想はしていたけどRails 7以上なのか〜」「今やってるプロジェクトでimport mapsが使えたらと思ったんですが残念」
🔗 Marginalia gemがActiveRecord::QueryLogs
として追加
つっつきボイス:「Marginalia?」「Basecampのgemだそうです」「そのgemがネイティブのActiveRecord::QueryLogs
として追加されたみたい」
「MarginaliaはSQLクエリのログにどのアプリのどのコントローラのどのアクションからのクエリかというコメントをこういうふうに追加するのね↓: そういえばこんなgemがあった」「名前でぱっとわかりにくそう」「造語かと思ったら"傍注"という意味でした」「入っていたら使うかも👍」
# basecamp/marginalia READMEより
Account Load (0.3ms) SELECT `accounts`.* FROM `accounts`
WHERE `accounts`.`queenbee_id` = 1234567890
LIMIT 1
/*application:BCX,controller:project_imports,action:show*/
ActiveRecord::QueryLogs
を追加Active Recordで生成されるすべてのSQLクエリにコンフィグ可能なタグが自動追加可能になった。
# config/application.rb
module MyApp
class Application < Rails::Application
config.active_record.query_log_tags_enabled = true
end
end
デフォルトでは、クエリタグに「アプリケーション」「コントローラ」「アクション」の詳細が追加される。
class BooksController < ApplicationController
def index
@books = Book.all
end
end
GET /books
# SELECT * FROM books /*application:MyApp;controller:books;action:index*/
静的な値やProcを含むカスタムタグをアプリケーション設定で定義可能。
config.active_record.query_log_tags = [
:application,
:controller,
:action,
{
custom_static: "foo",
custom_dynamic: -> { Time.now }
}
]
Keeran Raj Hawoldar, Eileen M. Uchitelle, Kasper Timm Hansen
同Changelogより
以下は作成中の移行ガイドだそうです。
参考: upgrade.md
🔗 マルチDBのrails db:setup
やrails db:reset
で特定のデータベースを指定できるようになった
これは、データベース固有のセットアップとリセットのタスクを可能にする試み。自分たちはマルチプルデータベースを広範囲に使っていて、すべてのデータベースを一度にリセットすることは避けたいと考えている。この変更により、名前空間ごとにデータベース固有のセットアップやリセットのタスクが追加される。デフォルトのセットアップタスクやおよびリセットタスクは、説明文を除いて変更していない。
ひとつ問題があるとすれば、
db:seed
タスクはデータベースに依存しないため、データベースに依存するタスクはすべてをseedすることになる点。特定のデータベースだけをseed可能だろうか?見たところ、seedファイルはどのActive Recordモデルでも参照できるので、できなさそうに思える。たとえば、primaryとeventsという2つのデータベースがある場合、以下のタスクが利用できる。
rails db:reset # Drops and recreates all databases from their schema for the current environment and loads the seeds
rails db:reset:events # Drops and recreates the events database from its schema for the current environment and loads the seeds
rails db:reset:primary # Drops and recreates the primary database from its schema for the current environment and loads the seeds
rails db:setup # Creates all databases, loads all schemas, and initializes with the seed data (use db:reset to also drop all databases first)
rails db:setup:events # Creates the events database, loads the schema, and initializes with the seed data (use db:reset:events to also drop the database first)
rails db:setup:primary # Creates the primary database, loads the schema, and initializes with the seed data (use db:reset:primary to also drop the database first)
同PRより
つっつきボイス:「マルチプルDBで特定のデータベースだけを設定したりリセットしたりできるrakeタスクが追加されたんですね」「今までできなかったとは」「これは欲しい機能👍」
🔗 spring gemをデフォルトインストールから削除
つっつきボイス:「development環境やtest環境でRailsをプリロードして起動を速くするspring gemがRailsのデフォルトから消えるそうです」「いいねがかなり多いですね」
コンピュータが高速になったので、小〜中規模アプリでspringを使う大きなメリットがほぼなくなった。よって、たまに問題を起こすspringをデフォルトで入れてつらい思いをする必要はもうない。
同PRより
「自分もspringはいつも真っ先にオフにしてる」「私も」「テストが理由なく落ちるときにspringを無効にすると解消するということがちょくちょくありましたね」「マイグレーションがなぜか失敗したときもよくありました」「ネイティブ環境だとspringもそれなりに有用なのかもしれないけど、最近はDocker環境で使う人が増えていますし、それもあって外したのかもしれませんね」
🔗 classicモード廃止に伴う孤立メソッド削除
つっつきボイス:「ActiveSupport::Dependency
からorphanな(孤立した)メソッドが続々削除されたそうです」「privateなインターフェイスだし消しても大丈夫そう」
参考: 定数の自動読み込みと再読み込み (Classic) - Railsガイド
「もうひとつのプルリクはActiveSupport::Dependency
にあるsafe_constantize
が削除された」「constantize
は使っていたけどsafe_constantize
は知らなかったな〜」
(これはclassicモードの削除に伴う
ActiveSupport::Dependencies
のお掃除の一環)
ActiveSupport::Dependencies
のconstantize
メソッドとsafe_constantize
メソッドはprivateでオートローディングとは関係しておらず、単にinflectorに転送される。これらは歴史的な理由で残されていたが、今や既に不要。
publicなインターフェイスはStringクラスにある。
model_name.constantize
したがって以下の代わりに上のように書ける。
ActiveSupport::Dependencies.constantize(model_name)
既にフレームワークの大半がこのようになっており、残りはわずか。
#43058より
🔗 weekday_options_for_select
ビューヘルパーが追加
つっつきボイス:「ブラウザで平日の曜日選択のプルダウンを表示するweekday_options_for_select
ってありそうでなかったのか」「ビューのフォームヘルパーなんですね」「これはあっていいメソッド👍」
自分はこれまで多くのRailsアプリで平日の曜日を選択するヘルパーを手作りしなければならなかった。Railsには優秀なヘルパーがほとんど揃っているので、このヘルパーがないことにいささか驚いていた。ちょうどヘルパーをまた実装しなければならなくなり、他の開発者もこのヘルパーがないことに驚いていたので、Railsにプルリクを投げてもいい頃合いだと思った。
このプルリクは
FormOptionHelper
とFormBuilder
に2つのメソッドを追加し、Tags::WeekdaySelect
クラスを追加する。
weekday_options_for_select
# => "<option value=\"Sunday\">Sunday</option>\n<option value=\"Monday\">Monday</option>\n
# <option value=\"Tuesday\">Tuesday</option>\n<option value=\"Wednesday\">Wednesday</option>\n
# <option value=\"Thursday\">Thursday</option>\n<option value=\"Friday\">Friday</option>\n
# <option value=\"Saturday\">Saturday</option>"
weekday_options_for_select
ではselected
値を受け取るほかに:index_as_value
オプションと:day_format
オプションも受け取れる。
weekday_options_for_select(nil, day_format: :abbr_day_names)
# => "<option value=\"Sun\">Sun</option>\n<option value=\"Mon\">Mon</option>\n
# <option value=\"Tue\">Tue</option>\n<option value=\"Wed\">Wed</option>\n
# <option value=\"Thu\">Thu</option>\n<option value=\"Fri\">Fri</option>\n
# <option value=\"Sat\">Sat</option>"
weekday_options_for_select(nil, index_as_value: true)
# => "<option value=\"0\">Sunday</option>\n<option value=\"1\">Monday</option>\n
# <option value=\"2\">Tuesday</option>\n<option value=\"3\">Wednesday</option>\n
# <option value=\"4\">Thursday</option>\n<option value=\"5\">Friday</option>\n
# <option value=\"6\">Saturday</option>"
weekday_options_for_select
は以下のような場合で使うヘルパーメソッド。
weekday_select(:model, :weekday)
# => "<select name=\"model[weekday]\" id=\"model_weekday\"><option value=\"Sunday\">Sunday</option>\n
# <option value=\"Monday\">Monday</option>\n<option value=\"Tuesday\">Tuesday</option>\n
# <option value=\"Wednesday\">Wednesday</option>\n<option value=\"Thursday\">Thursday</option>\n
# <option value=\"Friday\">Friday</option>\n<option value=\"Saturday\">Saturday</option></select>"
weekday_select
メソッドはFormBuilder
でも使われるので、以下のように書くと
<!-- 同PRより -->
<%= form_for @digest do |f| %>
<%= f.weekday_select :weekday %>
<%= f.submit %>
<% end %>
以下のようなHTMLが生成される。
<!-- 同PRより -->
<select name="digest[weekday]" id="digest_weekday">
<option value="Sunday">Sunday</option>
<option value="Monday">Monday</option>
<option value="Tuesday">Tuesday</option>
<option value="Wednesday">Wednesday</option>
<option value="Thursday">Thursday</option>
<option value="Friday">Friday</option>
<option value="Saturday">Saturday</option>
</select>
同PRより
🔗 特定データベースのyaml設定にdatabase_tasks: false
オプションが追加
つっつきボイス:「通常はたとえばrails db:migrate
を実行するとすべてのデータベースにコネクションを張るけど、database_tasks: false
を指定したデータベースにはコネクションを張らなくなるようですね」「ふむふむ」「以下のmy_animals_database
でアクセスを一切行いたくない場合に使うということだと思います: コマンドラインでもいいような気はしますが、yamlで設定できるとより便利そう👍」
データベース設定オプション
database_tasks
を追加
「スキーマ管理」「マイグレーション」「seed」などの管理タスクがない外部データベースに接続したい場合、データベースごとにdatabase_tasks: false
オプションを設定できるようになった。
# config/database.yml
production:
primary:
database: my_database
adapter: mysql2
animals:
database: my_animals_database
adapter: mysql2
database_tasks: false
Weston Ganger
同Changelogより
🔗 削除されたオプション
- PR: Remove legacy --skip-gemfile option from the days of Bundler opposition by dhh · Pull Request #42996 · rails/rails
- PR: Puma is the only option used for the web server by dhh · Pull Request #42998 · rails/rails
つっつきボイス:「どちらもDHHによるプルリクです」「#42996で--skip-gemfile
オプションが削除されて、#42998で--skip-puma
オプションが削除されたんですね」「歴史的なオプションみたい」
いにしえの小競り合いの記念碑をいつまでも残しておく必要はない。
#42996より
必要な設定を削除するためのオプションには意味がない。
#42996より
🔗Rails
🔗 大量のActionMailジョブをSidekiqで一括処理する(Ruby Weeklyより)
つっつきボイス:「deliver_later
で個別のジョブを大量に投げて詰まるのはよくあるヤツ」「1万通のメールで40分待ちはつらそう...」「この記事ではジョブの登録の詰まりが問題になっているみたいですね」
「Sidekiqのpush_bulk
を使うと複数ジョブをまとめて投げられるのか」「メールジョブごとにパラメータを渡したい場合はActionMailer::Parameterized::DeliveryJob
を使う必要があるのね」「メールによって送り先や文面をパラメータで変えるのはよくありますね」「ジョブを用意する段階でパラメータを渡しておいてからpush_bulk
でまとめてプッシュする方が負荷が小さくなる、たしかに」「アトミックな処理を行う大量のジョブを1個ずつ登録するのはやりたくないですね」
参考: Method: Sidekiq::Client#push_bulk
— Documentation for mperham/sidekiq (master)
# 同記事より
def enqueue_many_parametrized_mails(mail_class, template, args_array)
job = ActionMailer::Parameterized::DeliveryJob
# convert template and args array into an array of arrays containing args
# for ActionMailer::DeliveryJob objects
mailer_job_args = args_array.map { |args|
[job.new(
mail_class.name,
template.to_s,
"deliver_now",
{some_arg: "foo"},
*args
).serialize]
}
Sidekiq::Client.push_bulk(
"class" => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
"wrapped" => job,
"queue" => MAILER_QUEUE,
"args" => mailer_job_args
)
end
参考: Rails6 のちょい足しな新機能を試す41(MailDeliveryJob 編) - Qiita
🔗 fast_gettext: 高速i18n gem(Ruby Weeklyより)
# 同リポジトリより
FastGettext.with_locale 'gsw_CH' do
FastGettext._('Car was successfully created.')
end
# => "Z auto isch erfolgriich gspeicharat worda."
つっつきボイス:「i18n(国際化)のgemのようです」「gettextライブラリをrubyで再実装したみたい」
「fast_gettextはgettextより12倍高速でガベージが530分の1でスレッドセーフですって」「Active SupportのI18n::Simple
も比較されてる」「大量の国際化テキストを処理しないといけない場合に必要になってくるんでしょうね: 自分はあまりその必要に迫られたことはありませんが、海外のサイトだと国際化の言語数が多い分切実なのかも」
Hash FastGettext GetText ActiveSupport I18n::Simple Speed* 0.08s 0.14s 1.75s 3.75s Objects* 11K 15K 8017K 7107K Included backends db, yml, mo, po, logger, chain mo yml (db/key-value/po/chain in other I18n backends) 同リポジトリより
前編は以上です。
バックナンバー(2021年度第3四半期)
週刊Railsウォッチ:TruffleRubyでdig_fetchを実装、ruby/debug gem、AWSハンズオン教材ほか(20210901後編)
- 20210830前編 Rails 7でのimport maps導入、Steepで型を導入、KubernetesでRailsを動かすためのガイドほか
- 20210823後編 SorbetのRuby AOTコンパイラが公開、「Compiler Explorer」にRubyが追加、Ractorで非同期通信ほか
- 20210818前編 カウンタキャッシュをスレッドセーフに更新、Journey::Ast追加、GitLabをAWS Graviton2で動かすほか
- 20210810 システムテスト用headlessドライバにCupriteが追加、rails-mini-profiler、Jeremy Evansインタビューほか
- 20210804後編 Rubyの可変長アロケーションプロジェクト、サーキットブレーカーgem、EC2-Classicが終了へほか
- 20210803前編 SorbetでRailsアプリの型シグネチャを書く、activerecord-cte gemとanycable-client gem
- 20210720後編 ruby-gitでGit操作、最近のruby/debug、stdgems.org、Windows 365 Cloud PCほか
- 20210719前編 GitHubによるdisable_joins解説、MemoWise gemでメモ化、RailsのDDoS攻撃対策ほか
- 20210713後編 ruby-spacyで自然言語処理、Ruby製x86-64アセンブラ、『タイムゾーン呪いの書』ほか
- 20210712前編 AR::Relation#destroy_allがバッチ分割に変更、Active Record暗号化解説、sidekiq-unique-jobsほか
- 20210706後編 GitHub CopilotのAI補完、Pure Ruby実装のRuby JIT rhizome、PostgreSQLのPG-Strom拡張ほか
- 20210705前編 DI的な書き方が必要なとき、脆弱性学習用アプリRailsGoat、brakemanは優秀ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)