- Ruby / Rails関連
週刊Railsウォッチ: Evil MartiansのDocker+Rails記事が大幅更新、Railsガイドが電子書籍でほか(20220328前編)
こんにちは、hachi8833です。忌引のため刊行が空いてしまいました🙇
🔗Rails: 先週の改修(Rails公式ニュースより)
今回は以下のChangelogから見繕いました
🔗 ActionController::Parameters.to_h
にブロックを渡せるようになった
- PR: Receive a block to ActionController::Parameters.to_h by bobf · Pull Request #44756 · rails/rails
ActionController::Parameters.to_h
にブロックを渡せるようにして、RubyのHash#to_h
と整合するようにする。渡されるブロックはキーバーリューを受け取って要素2個の配列(キーペア)をyield
し、これをハッシュに変換できる。
同PRより
つっつきボイス:「to_h {|key, value| block }
のようにブロックを渡してキーバリューを引数にすることで新しいハッシュを生成できるようになった🎉」「これは便利そう」「一見直感的でなさそうにも思えるけど、to_h
にブロックを渡すなら|key, value|
以外のものはあまり使わなそうなので納得」
# #Lhttps://github.com/rails/rails/pull/44756/files#diff-99dab0dfb4d0cfa044997480d5aa1b100dc60347a10cedd6d8f7a0395f6a6efd#300
- def to_h
+ def to_h(&block)
if permitted?
- convert_parameters_to_hashes(@parameters, :to_h)
+ convert_parameters_to_hashes(@parameters, :to_h, &block)
else
raise UnfilteredParameters
end
end
🔗 MariaDBのデフォルト関数サポートを修正
"db/schema.rb"にデフォルトが誤った形で書き込まれて
db:schema:load
が正しく動かなくなることがある。さらに新規レコード保存時に関数名が文字列コンテンツとして追加される。
kaspernj
同Changelogより
つっつきボイス:「MariaDBはMySQLとほぼ互換のRDBMSですね」「これはバグ修正」「デフォルト関数って何だろう?」「スキーマに出力されるこのdefault:
のことのようですね↓」
# activerecord/test/cases/migration_test.rb#
def test_default_functions_on_columns
with_bulk_change_table do |t|
if current_adapter?(:PostgreSQLAdapter)
t.string :name, default: -> { "gen_random_uuid()" }
else
t.string :name, default: -> { "UUID()" }
end
end
参考: MariaDB - Wikipedia
参考: DEFAULT - MariaDB Knowledge Base
「テストでPostgreSQLについて言及されているのは、UUID()
は他のRDBMSにはあるけどPostgreSQLにはないので代わりにgen_random_uuid()
を使うということみたい」「to_a
にas: :hash
を渡せるのは初めて見た↓」「そう言えばこの書き方どこかで見て不思議に思ったことあります」「テストコードは学びありますね」
if current_adapter?(:PostgreSQLAdapter)
assert_equal "gen_random_uuid()", column(:name).default_function
Person.connection.execute("INSERT INTO delete_me DEFAULT VALUES")
person_data = Person.connection.execute("SELECT * FROM delete_me ORDER BY id DESC").to_a.first
else
assert_equal "uuid()", column(:name).default_function
Person.connection.execute("INSERT INTO delete_me () VALUES ()")
person_data = Person.connection.execute("SELECT * FROM delete_me ORDER BY id DESC").to_a(as: :hash).first
end
🔗 audio_tag
やvideo_tag
にActiveStorage::Attachment
オブジェクトを渡せるようにした
以下のように書けるようにした。
audio_tag(user.audio_file)
video_tag(user.video_file)
従来は以下のように書かないといけなかった。
audio_tag(polymorphic_path(user.audio_file))
video_tag(polymorphic_path(user.video_file))
image_tag
では既にサポート済みなので、同じ書き方ができるようにした。
同PRより
つっつきボイス:「前はpolymorphic_path
を介して添付ファイルを渡さないといけなかったのが直接渡せるようになったんですね」「polymorphic_path
は何をするんでしょうか?」「添付ファイルがポリモーフィックになっていて、そのパスを取り出すのに必要だったんでしょうね: 添付ファイルみたいなblobオブジェクトにはパス情報以外のものも含まれているので」「なるほど」「たしかに直接渡せる方が簡潔でわかりやすい👍」
参考: Rails API: polymorphic_path
-- ActionDispatch::Routing::PolymorphicRoutes
参考: Rails API: ActiveStorage::Attachment
🔗 Scaffoldで生成されるURLコメントの名前空間を修正
再現手順
rails new test_blog
cd test_blog
./bin/rails generate model Post title context:text
./bin/rails g scaffold_controller Admin::Post title context:text --model-name=Post
期待される振る舞い
生成されるURLコメントに名前空間がプレフィックスされるべき(例:# GET /admin/posts
や/admin/posts.json
)実際の振る舞い
生成されるURLコメントに名前空間がプレフィックスされない(例:# GET /posts
や/posts.json
)システム構成
Rails version: v7.0.2.2
Ruby version: v3.1.0
同PRより
つっつきボイス:「これはScaffoldジェネレータのバグ修正」「--model-name=Post
を指定してコントローラはAdmin::Posts
にしたときにコメントに名前空間が付かなかったのね」「コメントなので動作には影響はないけど」
「自分はScaffoldジェネレータをめっきり使わなくなった」「私はシンプルな用途が多いので割と使っちゃいます」「Scaffoldジェネレータは名前空間で使うと微妙に不便なところはありますね」
🔗 to_time
の古い振る舞いを非推奨化
- Ruby 2.4より前の
to_time
の振る舞いを非推奨化
Ruby 2.4以降はto_time
が変更されて、従来はローカルシステム時間へ変換していたのがレシーバーのオフセットを維持するようになっている。当時のRailsは古いRubyをサポートしていたので、移行支援のために互換性レイヤを追加していた。Rails 5.0以降の新しいアプリケーションではRuby 2.4以降の振る舞いがデフォルトになり、Rails 7.0ではRuby 2.7以降のみがサポートされているため、この互換性レイヤは安全に撤去可能。
非推奨警告は、この設定がfalse
の場合のみ表示される(互換性レイヤの削除が影響を及ぼす唯一のシナリオであり、ノイズを減らすため)。
Andrew White
同PRより
つっつきボイス:「古いRubyのto_time
の振る舞いがもう使われなくなったので警告表示を変更したんですね」
# activesupport/lib/active_support.rb#L112
def self.to_time_preserves_timezone=(value)
+ unless value
+ ActiveSupport::Deprecation.warn(
+ "Support for the pre-Ruby 2.4 behavior of to_time has been deprecated and will be removed in Rails 7.1."
+ )
+ end
+
DateAndTime::Compatibility.preserve_timezone = value
end
参考: Time#to_time
(Ruby 3.1 リファレンスマニュアル)
🔗Rails
🔗『クジラに乗ったRuby』英語版が更新(Ruby Weeklyより)
つっつきボイス:「以前翻訳したEvil Martiansの『クジラに乗ったRails』の元記事がっつり更新されて章も1つ追加されたそうです↓」「この記事はアクセスが多いですよね」「私自身これでDockerを学んだようなものでした」
日本語版もじきに更新します↑。試しにdiffを取ってみたら全文更新されていたので別記事にするかもしれません。
「お、今回の元記事更新でDocker Compose v2の記法に対応していますね」「ちょうどさっきV2について話してたのでタイムリー(明日の記事で取り上げます)」
参考: Compose V2 | Docker Documentation
「Dockerはいろいろ自由すぎて難しい面がありますよね」「ほんとに」「Docker Composeと一緒に学ぶならある程度型にはめられるけど、Docker自体は型らしいものがあまりないので難しい」
「DockerとDocker Composeは一度セットアップできれば後はだいたい使い回すかな」「自分も使いまわしまくってます↓」
# 元記事より
x-app: &app
build:
context: .
args:
RUBY_VERSION: '3.1.0'
PG_MAJOR: '14'
NODE_MAJOR: '16'
YARN_VERSION: '1.22.17'
image: example-dev:1.0.0
environment: &env
NODE_ENV: ${NODE_ENV:-development}
RAILS_ENV: ${RAILS_ENV:-development}
tmpfs:
- /tmp
- /app/tmp/pids
「上のようにdocker-compose.ymlでx-*
を使う記法は自分も使ってますね↑」「私もこれで記事を書きました↓: 定数を使い回せるのがありがたいです」「この記法にするとdocker-compose.ymlの可変要素をファイルの冒頭に集約できるもいいですよね」「そうそう」「docker-compose.ymlが長大になってくるとメンテナンスが難しくなってくるので、環境変数のような可変要素を集約できるのはいい👍」
「Evil Martiansのdip↓は特に使っても使わなくてもいいかなという感じ」「自分はdipでDocker Compose操作を覚えたのでdip使いまくりで、もうコマンドオプションを長々と指定する気になれないです」「自分はpsqlをバージョン指定して使いたいこととかもよくあるので、素のDockerコマンドも未だに使いますね」
🔗 Railsのパラレルテストの注意点(Ruby Weeklyより)
つっつきボイス:「AppSignalの記事です」「テストの個数が少ないときにパラレル化しても遅くなるだけと書かれている: データベースをdatabase-test-0とかdatabase-test-1みたいに複数立ち上げてfixtueを読み込めば当然セットアップに時間がかかる」「たしかに」「あと、データベースのテストそのものがボトルネックになっていればパラレル化しても遅くなる可能性は考えられます」
# 同記事
class ActiveSupport::TestCase
parallelize(workers: :number_of_processors)
end
「ファイルアクセスなどDB以外のリソースにアクセスするテストは、操作の重複を避けておかないとパラレル化したときにたまに失敗してしまうとも記事にある↓、たしかに」
# 同記事より
# Worker 1 executes
file = File.write('test.txt', 'created')
# Worker 2 executes
file = File.write('test.txt', 'deleted')
File.delete('test.txt')
assert_not(File.exist?('test.txt'))
# Worker 1 executes
assert_path_exists('test.txt')
🔗 RailsでRBSとSteepを試してみて
つっつきボイス:「Rails + RBS & Steep記事がだんだん出てくるようになりましたね」「ちょうど明日の銀座Rails#43↓にpockeさんが『RBSとRailsの今』というタイトルで登壇しますよ(編集部注: 同イベントは同日に終了しました)」
本日夕方19時~ 銀座Rails#43 をオンライン開催いたします。視聴には事前のConnpass登録を通じて告知されるZoomウェビナーURLが必要になりますので、未登録の方は忘れずご登録下さい。続いて本日の発表予定を紹介していきます(1/6) #ginzarails https://t.co/OIQLukCOb7
— 銀座Rails (@GinzaRails) March 25, 2022
「RBSといえば、Ruby 3.1にrbs collection
という新しいコマンドが追加されていて、gem_rbs_collectionからコレクションを取得できるようになっているんですよ↓」「お、これは知りませんでした」「それもあって銀座RailsでpockeさんにRBSの話をしてもらおうと企画しました」
「Ruby 3.0や3.1で細かな機能がいろいろ追加されていて見落としてた...」「3.1はコードの互換性にはほぼ影響しないので大きな変更はなさそうに見えるけど、ruby/debugの標準ライブラリ化やrbs collection
のように地味に機能が増えていますね」
参考: Ruby 3.1.0 リリース
「個人的にはそろそろRBSやSteepは普通に既存のRailsプロジェクトで使い始めてもいいかなと思います」「ふむふむ」「すべての機能をRBSなどでカバーするつもりはありませんが、少数のクリティカルな機能に絞って型検査を導入するならやってもいいかも👍」「たしかに」
「メソッドが想定外の使われ方をすることってよくあるじゃないですか」「想定外の使われ方は普通の方法だと見つけるだけでも大変ですよね」「そういう部分はなかなかテストが追加されないので、RBSなどでそういう部分を拾えるようになったら嬉しい」「セーフティーネット的に使えるといいですよね」
🔗 where.first
とfind_by
(Ruby Weeklyより)
つっつきボイス:「最近のActive Recordは賢いのでwhere.first
とfind_by
の違いを埋めてくれそうな気もしたけど、where.first
でやるとORDER BYが付くので全件スキャンが走って遅くなる可能性があるらしい↓」「記事のケースはいろいろ複雑で、単純なインデックスでは役に立たなかったと書いてますね」「単純なケースなら問題なくても、状況次第ではORDER BYが入るだけで遅くなる可能性があるのか」「VIEWでUNION ALLして複数テーブルを結合してるとかかな?」
# 同記事より
User.where(email: "andy@goodscary.com")
# SELECT "users".*
# FROM "users"
# WHERE "users"."email" = "andy@goodscary.com"
User.where(email: "andy@goodscary.com").first
# SELECT "users".*
# FROM "users"
# WHERE "users"."email" = "andy@goodscary.com"
# ORDER BY "users"."id" ASC
# LIMIT 1
User.find_by(email: "andy@goodscary.com")
# SELECT "users".*
# FROM "users"
# WHERE "users"."email" = "andy@goodscary.com"
# LIMIT 1
「ちなみにこの2番目のfind_by_email
みたいな動的な書き方は一応使えるけど、もう古い↓」「あ、そうだったかも」
# 同記事より
User.find_by(email: "andy@goodscary.com")
User.find_by_email("andy@goodscary.com")
後で調べると、RuboCop Railsでも警告されるそうです↓。
参考: Class: RuboCop::Cop::Rails::DynamicFindBy
— Documentation for rubocop (0.47.1)
「そういえば、find_by
はレコードが複数あっても最初の1件しか返さないけど、Railsに最近入ったfind_sole_by
は1件だけの場合にレコードを返してそれ以外はエラーにするというのがありましたね」「以前取り上げましたね(ウォッチ20210112)」
参考: find_sole_by
-- ActiveRecord::FinderMethods
🔗 Railsで最初の頃に混乱しがちな点(Ruby Weeklyより)
# 同記事より
bob = Friend.find_or_initialize_by(first_name: "Bob")
つっつきボイス:「find_or_initialize_by
だけだとsave
されないので、それに気づかずに操作して驚くという、よくある話」「永続化されてなかったんですね」「Railsを長く使っていれば気付ける話ではありますが、Railsがいろいろ便利な分、慣れないうちはRubyのオブジェクトを変更しただけなのかRDBに保存されたかはすぐに意識しにくいかもしれませんね」「たしかに」「最近のRailsはeager loadingされることも多いので、どのコードのどの部分でクエリが発生するかというのは見えづらいでしょうね: ビューを処理するまでクエリが発生しないこともあるので」
参考: find_or_initialize_by
-- ActiveRecord::Relation
その他Rails
つっつきボイス:「ViewComponentにしたらロジックの置き場所が腑に落ちて、今までホームレスだったコードにホームができて嬉しいという感じの記事でした」「ViewComponentはどうしても数が増えがちなので、置き場所が落ち着くとやりやすいというのはワカル」
#Rails考古学 シリーズとして、#Railsガイド から *Rails 2* 系のリリースノートを公開しました! 🆕⁉️
2008年の『Rails 2.2』リリースから約 1️⃣4️⃣ 年、過去のリリースノートを読んでこれまでの経緯をふりかえってみませんか...!? 😆✨
🧐 Rails考古学:2008年のRails - notehttps://t.co/BaMf3Am7kC pic.twitter.com/ouIqzaGR5B
— 安川要平/Yohei Yasukawa (@yasulab) March 24, 2022
「Railsガイドでこれまで後回しになっていたRails 2系のリリースノート2本をこの間やっと翻訳して未訳を埋められました」「Railsガイドそのものができたのも2.2からだったのね」「訳していて知らないものがいろいろ出てきて考古学な気持ちになりました」「2系は触ったことはあるけどrails new
したことがなかった」「歴史楽しい」
参考: Ruby on Rails 2.2 リリースノート - Railsガイド
参考: Ruby on Rails 2.3 リリースノート - Railsガイド
「それと前後して、Railsガイドの電子書籍を電子書籍でも買えるようになりました↓」「1000ページ超え」「紙に印刷したら立方体みたいになるかも😆」
【新着】Ruby on Rails Community(著),八田昌三,安川要平(訳)『Railsガイド(Rails 7.0対応)』(達人出版会) https://t.co/sVldOQZBE9 Ruby on Railsの大型ガイドが登場! Railsの各機能の使い方からRailsに貢献する方法まで、1000ページを越える電子書籍を一冊お手元に置いてみませんか?
— 達人出版会 (@tatsu_zine) March 22, 2022
「昨年末にRails 7がリリースされた直後からRailsガイドの差分更新翻訳をかけたんですが、過去の恥ずかしい訳や更新漏れなどの修正も含めて結果的に全文を見直しました」「すごい根性💪」「こういうことになるとムキになってしまいます」
参考: Ruby on Rails ガイド:体系的に Rails を学ぼう
前編は以上です。
バックナンバー(2022年度第1四半期)
週刊Railsウォッチ: Crystal言語作者がRubyを愛する理由、TypeScript 4.6リリースほか(20220309後編)
- 20220308前編 英国政府サイトで使われるRailsアプリ、pg-oscとPercona Toolkitほか
- 20220301後編 Ruby標準のCSVライブラリは優秀、if代入のコーディングスタイル、rambulanceほか
- 20220228前編 dartsass-railsがリリース、webpack-mergeツール、Rubyが29歳にほか
- 20220222後編: 端末文字幅とRubyのreline、SQLのプリペアドステートメント、Terraformほか
- 20220221前編 orderでコレーション指定をサポート、awesome_nested_set、GitHub Copilotほか
- 20220216後編 Bundler自身のバージョンロック機能、gem署名メカニズムの提案ほか
- 20220214前編 Rails 7.0.2の改修内容、receipts gemでレシートを作成ほか
- 20220209後編 Rubygems.orgのAPIキーに権限スコープが追加、RailsのDBパフォーマンス改善ほか
- 20220208前編 Rails 6.1を7.0にアップグレードしてみた、PostgreSQLでジョブキューほか
- 20220201後編 Rubygems Adoptionフォームが開設、JetBrains Gateway、NGINX Unitほか
- 20220131前編 Sidekiqが10歳に、BuildKiteのテストを高速化、フィーチャーフラグほか
- 20220126後編 Rubyコンパイラの歴史動画、RubyのWebAssembly対応進む、ぼっち演算子の注意点ほか
- 20220124前編 Webpackerが公式に引退宣言、『Everyday Rails』日本語版がRails 7に対応ほか
- 20220118後編 Ruby 2.5〜3.1ベンチマーク、Opal 1.4、JRubyが20歳に、2022年のCSSほか
- 20220117前編 rails-ujs->Turboアップグレードガイド、RubyとWeb Componentsほか
- 20220112 Rails 7をRuby 3.1で動かす、クックパッドのRuby 3.1解説記事、Rails 6->7更新ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)