こんにちは、hachi8833です。技術評論社のRuby 3.3記事、まだまだありました🙇。
> 新しいIRBでは行末に;をつけると出力を抑制できます。
知らなかった。便利すぎる。;を付けなくてもデフォルトで抑制できるオプションとかありそう。
というかirb & debugを極めるとめちゃくちゃ生産性上がりそう。今度リファレンスマニュアル眺めてみよう。https://t.co/1btnbrfXPL— Masafumi Koba 🇺🇦 (@ybiquitous) January 25, 2024
Ruby 3.3リリース! 新機能解説 「M:Nスレッドによる軽量な並行処理への挑戦」 公開 https://t.co/Atv0p8wFpx
— gihyo.jp (@gihyojp) January 30, 2024
Ruby 3.3リリース! 新機能解説 「徹底解説! default gemsとbundled gemsのすべて」 @hsbt 公開 https://t.co/RoovQcmb02
— gihyo.jp (@gihyojp) January 29, 2024
Ruby 3.3リリース! 新機能解説 「Ruby 3.3 YJITのメモリ管理とRJIT
〜すべてが新しくなった2つのJITを使いこなす」 公開 https://t.co/PleFqRHg0C— gihyo.jp (@gihyojp) January 22, 2024
🔗Rails: 先週の改修(Rails公式ニュースより)
今回は小さめのバグ修正が中心です。
- 公式更新情報: Ruby on Rails — A class method to introspect delegated types, schema_dump configuration via DATABASE_URL, etc
- 公式更新情報: Ruby on Rails — Customized console prompt and bugfixes
🔗 ActiveRecord::DelegatedTypeに<role>_typesクラスメソッドが追加
DelegatedTypeのイントロスペクションを可能にするため、
<role>_typesクラスメソッドをActiveRecord::DelegatedTypeに追加する。JP Rosevear
同Changelogより
動機/背景
APIリクエストのstrong parametersを処理中に、やや抽象的な方法でDelegatedTypeのポリモーフィックな型フィールドをバリデーションする場合、許可されている型にアクセスできると便利。
詳細
このプルリクは、
ActiveRecord::DelegatedTypeで定義されるメソッドを変更する。
同PRより
つっつきボイス:「DelegatedTypeに<role>_typesというクラスメソッドが生えてくるようになったそうです」「従来の<role>_classや<role>_nameとかはdefine_methodでインスタンスメソッドを生やすようになっているけど、今回の<role>_typesはdefine_singleton_methodでクラスメソッドとして生えてきて、そのクラスのtypesのリストを返してくれるんですね: DelegatedType自体はモジュールなので、これをincludeかextendするとそこで<role>_typesがクラスメソッドになるということみたい」
# activerecord/lib/active_record/delegated_type.rb#L216
private
def define_delegated_type_methods(role, types:, options:)
primary_key = options[:primary_key] || "id"
role_type = options[:foreign_type] || "#{role}_type"
role_id = options[:foreign_key] || "#{role}_id"
+ define_singleton_method "#{role}_types" do
+ types.map(&:to_s)
+ end
+
define_method "#{role}_class" do
public_send(role_type).constantize
end
define_method "#{role}_name" do
public_send("#{role}_class").model_name.singular.inquiry
end
define_method "build_#{role}" do |*params|
public_send("#{role}=", public_send("#{role}_class").new(*params))
end
...
参考: Object#define_singleton_method (Ruby 3.3 リファレンスマニュアル)
参考: Module#define_method (Ruby 3.3 リファレンスマニュアル)
「DelegatedTypeってほとんど使ってなかったけど、ドキュメント↓を見た感じではなかなかいい子のようですね: 今回の<role>_typesも含めて使ってみようかな👍」
「ところでイントロスペクション(introspection)ってリフレクションプログラミングの文脈でよく出てきますけど、今ひとつよくわからないです😅」
後で調べてみました:
コンピューターサイエンスにおけるリフレクティブプログラミング(リフレクション)とは、プロセスが自身の構造と動作について検査やイントロスペクションを行い、変更する能力のこと。
Reflective programming - Wikipediaより
コンピューティングにおける型イントロスペクションとは、実行時にオブジェクトの型やプロパティを検査するプログラムの機能。一部のプログラミング言語がこの機能を有している。
Type introspection - Wikipediaより
🔗 schema_dumpなどをDATABASE_URLでコンフィグ可能になった
以下を
DATABASE_URLでコンフィグ可能にする。
schema_dumpquery_cachereplicadatabase_tasks従来は、ブーリアン値が文字列として解釈されてできないことがあった。
改修後は、
DATABASE_URL=postgres://localhost/foo?schema_dump=falseのように書くことでスキーマキャッシュのダンプを正しく無効にできるようになった。Mike Coutermarsh、Jean Boussier
同Changelogより
つっつきボイス:「DATABASE_URLで?schema_dump=falseのようなコンフィグパラメータを追加できるようになったんですね」「to_boolean!っていうprivateメソッドが追加されてる」「ちょっとややこしいけど、文字列で入ってくる"true"や"false"を検査して、その結果でハッシュconfiguration_hash[key]を破壊的に更新しているので、メソッド名に!がついているのか、なるほど」
# activerecord/lib/active_record/database_configurations/url_config.rb#L43
@url = url
- @configuration_hash = @configuration_hash.merge(build_url_hash).freeze
+ @configuration_hash = @configuration_hash.merge(build_url_hash)
+
+ if @configuration_hash[:schema_dump] == "false"
+ @configuration_hash[:schema_dump] = false
+ end
+
+ if @configuration_hash[:query_cache] == "false"
+ @configuration_hash[:query_cache] = false
+ end
+
+ to_boolean!(@configuration_hash, :replica)
+ to_boolean!(@configuration_hash, :database_tasks)
+
+ @configuration_hash.freeze
end
private
+ def to_boolean!(configuration_hash, key)
+ if configuration_hash[key].is_a?(String)
+ configuration_hash[key] = configuration_hash[key] != "false"
+ end
+ end
+
🔗 ActiveSupport::MessagePackでIPAddrをシリアライズするとprefixが含まれていなかったのを修正
ActiveSupport::MessagePackシリアライザでIPAddrをシリアライズしたときにIPAddr#prefixが含まれるようにした。この変更には下位互換性と上位互換性があり、古いペイロードは引き続き読み取り可能になり、新しいペイロードは古いバージョンのRailsでも読み取り可能になる。Taiki Komaba
同Changelogより
動機/背景
このプルリクを作成した理由は、ダンプ時に
msgpackシリアライザでIPAddrオブジェクトのプレフィックス(ネットマスク)データが欠落していることに気付いたため。詳細
このプルリクは、
IPAddrをmsgpackシリアライザでダンプしたときにプレフィックス(ネットマスク)データが失われないよう修正する。追加情報
to_sにはプレフィックス(ネットマスク)データが含まれないIPAddr.new('192.168.0.1/24').to_s => "192.168.0.0"このため、
msgpackシリアライザでダンプ/読み込みを行ったときにto_sにはプレフィックス(ネットマスク)データが含まれない。irb(main):032> serializer = ActiveSupport::MessagePack::CacheSerializer irb(main):033> serializer.load(serializer.dump(IPAddr.new('192.168.0.1/24'))) => #<IPAddr: IPv4:192.168.0.0/255.255.255.255>
IPAddrオブジェクトのアサーション
IPAddrインスタンスの==アサーションは、同じデータのアサーションが正確ではない。このためテストではinspectを使った。IPAddr.new('192.168.0.1/24') == IPAddr.new('192.168.0.0/32') => true irb(main):027> IPAddr.new('192.168.0.1/24').inspect == IPAddr.new('192.168.0.0').inspect => false同PRより
つっつきボイス:「これはバグ修正ですね」「プレフィックスって何だろうと思ったら、IPアドレスのサブネットマスクを/24とか/25とかで表すヤツを指してそう言っているのね」「==でアサーションするとサブネットマスクが違っていてもtrueになるのでinspectでアサーションしたんですって」「これは残念」「今さら変えられなさそう...」
🔗 デフォルトの読み込みパスにファイルが含まれないよう修正
Railsのデフォルトの読み込みパスにディレクトリ以外のものが存在しないよう修正。
従来は
appディレクトリにある一部のファイルが読み込みパスを汚染していた。
このコミットは、Railsフレームワークで設定されるデフォルトの読み込みパスからファイルを削除する。これで、以下のパスにデフォルトでディレクトリだけが含まれるようになった。
autoload_pathsautoload_once_pathseager_load_pathsload_pathsTakumasa Ochi
同Changelogより
つっつきボイス:「これもバグ修正です」「読み込みパスにディレクトリの他にファイルが混じることがあったとは」「existentとexistent_directoriesは前から同じPathsモジュールにあるんですね」
# railties/lib/rails/paths.rb#L105
private
def filter_by(&block)
all_paths.find_all(&block).flat_map { |path|
- paths = path.existent
- paths - path.children.flat_map { |p| yield(p) ? [] : p.existent }
+ paths = path.existent_directories
+ paths - path.children.flat_map { |p| yield(p) ? [] : p.existent_directories }
+ }.uniq
end
# railties/lib/rails/paths.rb#L208
def existent
expanded.select do |f|
does_exist = File.exist?(f)
if !does_exist && File.symlink?(f)
raise "File #{f.inspect} is a symlink that does not point to a valid file"
end
does_exist
end
end
def existent_directories
expanded.select { |d| File.directory?(d) }
end
🔗 Active StorageでHDR動画のrotationを取得できない場合があったのを修正
動機/背景
修正: #50853
詳細
動画アナライザの
rotationメタデータの取得は、side_dataの位置参照に依存していた。しかしside_dataの位置がすべての動画で同じであることは保証されていない。たとえば、iOSでポートレートモードで撮影したHDR動画では、"DOVI configuration record"side_dataが最初の位置にあり、それに続いてrotation値を含む"Display matrix"side_dataが置かれる。この修正によって位置参照が削除され、"Display matrix"
side_dataを明示的に探索してrotation値を取得できるようになる。
同PRより
つっつきボイス:「位置参照って言っているのはside_data[0]のことみたい」「位置をゼロで決め打ちしていると値を取り出せない場合があったので、Display Matrixという名前で取ってから"rotation"で取るようにしたのね」「これもバグ修正ですね」
# activestorage/lib/active_storage/analyzer/video_analyzer.rb#L55
def angle
if tags["rotate"]
Integer(tags["rotate"])
- elsif side_data && side_data[0] && side_data[0]["rotation"]
- Integer(side_data[0]["rotation"])
+ elsif display_matrix && display_matrix["rotation"]
+ Integer(display_matrix["rotation"])
end
end
+ def display_matrix
+ side_data.detect { |data| data["side_data_type"] == "Display Matrix" }
+ end
🔗 NestedAttributesで、渡した引数の種類によってエラーメッセージが異なっていたのを修正
ネステッド属性関連付けライター(writer)に無効な引数を渡すと、常に
ArgumentErrorが発生するよう修正。従来、このエラーはコレクション(複数形)の関連付けでしか発生せず、単数形の関連付けでは一般的なエラーを発生していた。
修正後は、コレクションと単数形の両方の関連付けで
ArgumentErrorが発生するようになった。Joshua Young
同Changelogより
つっつきボイス:「Active RecordのNestedAttributesで、渡した引数が複数形か単数形でエラーの種類が違ってしまっていたので、どちらも同じエラーになるように修正したんですね」「options =の行を下に移動してから、引数がHashかどうかのチェックを追加している↓」「メソッド名が長いけど、これは1対1関連付け用なのね」
# activerecord/lib/active_record/nested_attributes.rb#L423
def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
- options = nested_attributes_options[association_name]
if attributes.respond_to?(:permitted?)
attributes = attributes.to_h
end
+ unless attributes.is_a?(Hash)
+ raise ArgumentError, "Hash expected for `#{association_name}` attributes, got #{attributes.class.name}"
+ end
+
+ options = nested_attributes_options[association_name]
attributes = attributes.with_indifferent_access
existing_record = send(association_name)
「こっちのコレクション用メソッドはメッセージを複数形用に修正している↓」
# activerecord/lib/active_record/nested_attributes.rb#L487
def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
options = nested_attributes_options[association_name]
if attributes_collection.respond_to?(:permitted?)
attributes_collection = attributes_collection.to_h
end
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
- raise ArgumentError, "Hash or Array expected for attribute `#{association_name}`, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
+ raise ArgumentError, "Hash or Array expected for `#{association_name}` attributes, got #{attributes_collection.class.name}"
end
🔗Rails
🔗 Rails Foundationによるドキュメント改良プロジェクト(Rails公式ニュースより)
- 元記事: Ruby on Rails — Documentation update: work has begun
rails foundationタグ付きissue: Issues · rails/rails
つっつきボイス:「Rails Foundationが、新しい取り組みとしてRailsのドキュメントをがっつり改良するプロジェクトを始めたそうです」「お〜、rails foundationタグがついているタグは、監査->執筆&チームレビュー->プルリク&コミュニティレビューという流れでドキュメントを改善していくんですね🎉」「見た感じではRailsガイドが改良の対象みたい↓」「今のところ3つか」
参考: [RF DOCS] Add documentation for perform_all_later to Active Job Basics guide [ci-skip] by bhumi1102 · Pull Request #51004 · rails/rails
参考: [RF DOCS] Action Text Documention [ci-skip] by Ridhwana · Pull Request #50977 · rails/rails -- マージ
参考: [RF DOCS] Action Mailbox Documention [ci-skip] by bhumi1102 · Pull Request #50973 · rails/rails -- マージ
🔗 Turbo 8リリース
- リリースタグ: Release v8.0.0 · hotwired/turbo
参考: Turbo: The speed of a single-page web application without having to write any JavaScript.
つっつきボイス:「つっつきの日(2/8)の前日に、jnchitoさんのXポストでTurbo 8リリースを知りました」「おぉ、HotwireやTurbo周りはどうなっていくのかな?」「以下のEvil Martiansの記事でも取り上げているモーフィングによるページ更新などが使えるようになるそうです」
https://t.co/MOrJRvGSdm meetup Vol.28を公開しました。次回はリリースされたばかりのTurbo 8をみんなでつっつきます!
スタートは2/15(木) 18:30から。ラジオ参加もwelcomeです!【聞き専OK】https://t.co/MOrJRvGSdm meetup Vol.28 / Turbo 8.0と戯れる会 https://t.co/Fh1laC11JD #hotwirelove
— Junichi Ito (伊藤淳一) (@jnchito) February 8, 2024
- モーフィングによるページ更新(#1019)
- ビュートランジションAPIによるナビゲーション(#935)
InstantClickの追加(#1101)- TypeScriptの削除(#971) -- 👍より👎が圧倒的に多いですね
- ナビゲーション中に使われていなかったスタイルシートの削除(#1128)
data-turbo-track="dynamic"の導入(#1140)- 送信中のフォーム要素に
aria-busyを設定(#1110)html[data-turbo-visit-direction]によるvisit方向取得・操作(#1007)turbo:{before-,}morph-{element,attribute}の導入(#1097)- ナビゲーション中に
html[lang]を設定(#1035)- プリロード中の
turbo:before-fetch-{request,response}ディスパッチ(#1034)
Release v8.0.0 · hotwired/turboより
🔗Ruby
🔗 Rubyの位置引数(Ruby Weeklyより)
つっつきボイス:「Rubyのいわゆるpositional arguments(位置引数)を中心とした解説記事ですが、3.0のときの改訂↓の話に加えて、ナンパラなどの最新の書き方もカバーしています」
# 同記事より
def passthrough(*)
illegal = * # can't do this
somehow_legal = [*] # somehow, this works
a, b, *c = [*] # as does this
other_method(*)
end
[1, 2, 3].map { _1 * _1 }
[1, 2, 3].map { it * it } // Ruby 3.4で導入予定
参考: Ruby 3.0における位置引数とキーワード引数の分離について
今回は以上です。
バックナンバー(2024年度第1四半期)
- 20240206前編 Pumaのデフォルトスレッド数変更、Rails 1.0をRuby 3.3で動かすほ
- 20240125後編 RailsコントローラのparamsはHashではない、ruby-enumほか
- 20240123前編 Railsの必須Rubyバージョンが3.1.0以上に変更ほか
- 20240119後編 Ruby 3.3でYJITを有効にすべき理由、Turbo 8の注意点8つほか
- 20240117前編 Rails 8マイルストーン、2023年のRails振り返り、Solid Queueほか
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。


週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)