- Ruby / Rails関連
週刊Railsウォッチ: スライド『Rails 7.1をn倍速くした話』、Rails 7.1でMessagePackをサポートほか(20230502)
こんにちは、hachi8833です。RubyKaigi 2023のグルメ情報も出ましたね。
はてなブログに投稿しました #はてなブログ #rubykaigi
☕️Coffeehouse スポンサー & 松本グルメ情報🍴 - ESM アジャイル事業部 開発者ブログhttps://t.co/s0t8wkX6Yi— ESM, Inc. (@rubyagile) April 28, 2023
お知らせ: 次回の週刊Railsウォッチは5/23(火)の予定です。
🔗Rails: 先週の改修(Rails公式ニュースより)
- 公式更新情報: Ruby on Rails — A week dedicated to composite primary keys 😎
- 公式更新情報: Ruby on Rails — The beginning of Trilogy, ChatGPT as a contributor
🔗 複合主キー関連
🔗 複合主キーでレコードを関連付けから削除可能になった
動機/背景
複合主キーをサポートする継続的取り組みの一環。この変更によって、複合コンテキストにおける関連付けからのレコード削除が正しく実装される。
詳細
この変更が行われないと以下のようになる。
great_author = cpk_authors(:cpk_great_author) books = great_author.books # ArgumentError (Expected corresponding value for [:author_id, :number] to be an Array) great_author.books.delete(books.first)これは、
composite_query_constraints_listを用いて配列内でクエリ制約(主キー)を取得してから、渡されたレコードから対応するカラムをSELECTできるようにする。従来は、多くのケースが
where(id => [records])に解決し、最終的にArrayHandlerのロジックを用いてidがデフォルトになっていた。これは、複合主キーのシナリオをサポートするためにIDへの解決がより早い段階で行われることを除けば同じである。
注: マルチテナンシーでは、主キーだけではなくクエリ制約を用いてレコードを削除する必要が生じるシナリオが存在するため、クラスで
query_constraints_listを使うことが重要。
同PRより
つっつきボイス:「dependentオプションで指定したモデルのレコードを芋づる式に削除する機能がActive Recordに標準でありますけど、それを複合主キーでもできるようになった👍」「複合主キーを標準機能でサポートするとフレームワーク側でいろいろ改修しないといけなくなるのか、大変そう」「キーが使われる場所全部で複合主キーに対応する必要があるからそうなりますよね」
参考: Active Record の関連付け - Railsガイド
🔗 ids_writerとids_readerで複合主キーをサポート
ids_writerが複合主キー(CPK)を扱えるように修正する。
非複合主キーモデルに関連付けられている複合主キーモデルを使う場合は正しく動作していた(これ用のテストも追加しておいた)が、別の複合主キーモデルに関連付けられているCPKモデルを使う場合は正しく動作していなかった。この修正によって、正しいidの書き込みにも配慮するようになった。
ids_readerで作業していて、pluckメソッドが複合主キーで動作しないことに気づいた。理由は、これが属性の配列を渡していて、disallow_raw_sql!ではサポートされていないため。
他の場所でも同様の問題が起きていそうだったので、pluck内でflattenを呼び出すことにした。これによって複合主キーによるpluck全体が修正され、計算のテストファイルも修正できた。
同PRより
つっつきボイス:「has_manyを使うと複数形の何ちゃら_idsメソッドや何ちゃら_ids=メソッドが生えてくる機能が前からあったのか」「これ使ったことなかったかも」「その機能を複合主キーに対応したんですね」
参考: §20.3 ids -- Active Record クエリインターフェイス - Railsガイド
「ids_writerの定義はこれですね↓」
# activerecord/lib/active_record/associations/collection_association.rb#L60
def ids_writer(ids)
primary_key = reflection.association_primary_key
pk_type = klass.type_for_attribute(primary_key)
ids = Array(ids).compact_blank
ids.map! { |i| pk_type.cast(i) }
- records = klass.where(primary_key => ids).index_by do |r|
- r.public_send(primary_key)
+ records = if klass.composite_primary_key?
+ query_records = ids.map { |values_set| klass.where(primary_key.zip(values_set).to_h) }.inject(&:or)
+
+ query_records.index_by do |r|
+ primary_key.map { |pk| r.public_send(pk) }
+ end
+ else
+ klass.where(primary_key => ids).index_by do |r|
+ r.public_send(primary_key)
+ end
end.values_at(*ids).compact
if records.size != ids.size
found_ids = records.map { |record| record.public_send(primary_key) }
not_found_ids = ids - found_ids
klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
else
replace(records)
end
end
🔗 バッチ処理で複合主キーをサポート
従来のバッチ処理は、シンプルな主キーを持つテーブルでしか動作しなかった。
実装の説明
複合主キーの現在のタプル
(a, b)を昇順でイテレートする場合、以下のタプル(ここでは(x, y)とする)を取得するSQL条件は以下の数式で計算される。(x, y) > (a, b) iff x > a OR (x = a AND y > b)何らかのタプルから開始する場合は、これと似た以下の式で計算される。
(x, y) >= (a, b) iff x > a OR (x = a AND y >= b)複合主キーにカラムが3つ以上ある場合も、類似のロジックが適用される。
同PRより
つっつきボイス:「バッチ処理用のin_batchesというメソッドを複合主キーに対応するために、batch_orderの部分ですべての複合主キーにORDERが効くようにしている↓: ORDERのキーが1つだと順序が固定されないので」「こうしてみるとActive Recordに全面的に手を入れているのがわかりますね」「複合主キーが7.1に入るとしたらきっと目玉機能になりそう」
# activerecord/lib/active_record/relation/batches.rb#L230
- relation = relation.reorder(batch_order(order)).limit(batch_limit)
+ relation = relation.reorder(*batch_order(order)).limit(batch_limit)
+ relation = apply_limits(relation, start, finish, order)
+ relation.skip_query_cache! # Retaining the results in the query cache would undermine the point of batching
+ batch_relation = relation
+ empty_scope = to_sql == klass.unscoped.all.to_sql
loop do
if load
records = batch_relation.records
ids = records.map(&:id)
yielded_relation = where(primary_key => ids)
yielded_relation.load_records(records)
elsif (empty_scope && use_ranges != false) || use_ranges
- ids = batch_relation.pluck(primary_key)
+ ids = batch_relation.ids
finish = ids.last
if finish
yielded_relation = apply_finish_limit(batch_relation, finish, order)
yielded_relation = yielded_relation.except(:limit, :order)
yielded_relation.skip_query_cache!(false)
end
else
- ids = batch_relation.pluck(primary_key)
+ ids = batch_relation.ids
yielded_relation = where(primary_key => ids)
end
参考: Rails API in_batches -- ActiveRecord::Batches
🔗 Trilogyデータベースクライアント用のアダプタが導入される
TrilogyはMySQLと互換性のあるデータベースクライアント。
Railsアプリケーションでconfig/database.ymlを以下のように設定することでTrilogyを利用できる。development: adapter: trilogy database: blog_development pool: 5以下のように
DATABASE_URL環境変数でも設定できる。ENV['DATABASE_URL'] # => "trilogy://localhost/blog_development?pool=5"Adrianna Chang
同Changelogより
動機/背景
Trilogyデータベースクライアントに対応するActive Recordアダプタが昨年GitHubによってオープンソース化された。Shopifyも数週間前に自社のRailsモノリスでTrilogyの採用に成功した。2つのメジャーなRailsアプリケーションでTrilogyが正常に動作しているので、これを
Mysql2Adapterに代わるMySQL互換のアダプタとしてアップストリームのRailsにマージすることを提案したい。なお、この取り組みはShopifyの@adrianna-chang-shopifyと@eileencodes、そしてGitHubの@matthewdによる。アダプタライブラリのオリジナル作者はクレジット済み。
詳細
このプルリクは、activerecord-trilogy-adapterの全コードと全テストケースを移植する。Trilogyと既存のMysql2の間の重複コードはDRYにできるが、テストカバレッジを完全に確保したかったので、さしあたって全コピーし、次のプルリクでクリーンアップを提案する。
以下の変更も行われた。
Mysql2と同等にするため、TrilogyAdapterに#explainオプションのサポートを追加- データベースジェネレータがアプリケーションの
trilogyオプションに対応- Trilogyのマイグレーション互換性を追加
TrilogyAdapter#select_allが更新され、MULTI_RESULTクエリ後にコネクションが正常な状態に復帰するようになった- Active RecordのRakefileが更新され、Trilogyに対してMySQLの抽象テストケースを実行できるようになった
追加情報
クライアント側のgithub/trilogy#64がまだマージされていない。これがリリースされたらクライアントの新バージョンをリリースしてGemfileを更新し、バージョンを合わせるべき。
同PRより
つっつきボイス:「Trilogyは、以前も話題になったGitHubによるMySQLクライアントアダプタライブラリですね(ウォッチ20220906)」「GitHubとShopifyでTrilogy導入に成功したことを受けてRailsに正式に移植したのか」
🔗 ActiveModel::SecurePasswordのサイズバリデーションをBCrypt仕様の上限に合わせて修正
従来のバリデーションで考慮されていたのはパスワードの文字数のみであったため、
BCryptによる上限72バイトを正確に反映していない可能性があった。
この変更によって、文字数のバリデーションは変えずに、文字数とバイトサイズの両方を考慮するようになる。user = User.new(password: "a" * 73) # 73 characters user.valid? # => false user.errors[:password] # => ["is too long (maximum is 72 characters)"] user = User.new(password: "あ" * 25) # 25 characters, 75 bytes user.valid? # => false user.errors[:password] # => ["is too long (maximum is 72 bytes)"]ChatGPT、Guillermo Iguaran
同Changelogより
つっつきボイス:「bcryptに72バイトの上限があったとは知らなかった」「そういえばbcryptは固定長方式でしたね」「上限を超えた場合のバリデーションエラーはたしかに必要」「現状でパスワードが72バイトを超えている人にとってはbreaking changeになるのかな」
参考: bcryptの72文字制限をSHA-512ハッシュで回避する方式の注意点 | 徳丸浩の日記
参考: bcrypt - Wikipedia
🔗 ActiveSupport::MessagePackが追加
ActiveSupport::MessagePackシリアライザはmsgpackgemに統合され、さまざまなRubyオブジェクトをシリアライズする。ActiveSupport::MessagePackは、msgpackでサポートする基本型に加えて、TimeやRangeなどの型や、ActiveSupport::TimeWithZoneやActiveSupport::HashWithIndifferentAccessなどのActive Support 型もサポートしている。
ActiveSupport::MessagePackが提供する機能は、JSONやMarshalと比較してパフォーマンスやメッセージサイズの削減に優れている。以下はMessageVerifierで使う場合の例。# frozen_string_literal: true require "benchmark/ips" require "active_support/all" require "active_support/message_pack" marshal_verifier = ActiveSupport::MessageVerifier.new("secret", serializer: Marshal) json_verifier = ActiveSupport::MessageVerifier.new("secret", serializer: JSON) asjson_verifier = ActiveSupport::MessageVerifier.new("secret", serializer: ActiveSupport::JSON) msgpack_verifier = ActiveSupport::MessageVerifier.new("secret", serializer: ActiveSupport::MessagePack) ActiveSupport::Messages::Metadata.use_message_serializer_for_metadata = true expiry = 1.year.from_now data = { bool: true, num: 123456789, string: "x" * 50 } Benchmark.ips do |x| x.report("Marshal") do marshal_verifier.verify(marshal_verifier.generate(data, expires_at: expiry)) end x.report("JSON") do json_verifier.verify(json_verifier.generate(data, expires_at: expiry)) end x.report("AS::JSON") do asjson_verifier.verify(asjson_verifier.generate(data, expires_at: expiry)) end x.report("MessagePack") do msgpack_verifier.verify(msgpack_verifier.generate(data, expires_at: expiry)) end x.compare! end puts "Marshal size: #{marshal_verifier.generate(data, expires_at: expiry).bytesize}" puts "JSON size: #{json_verifier.generate(data, expires_at: expiry).bytesize}" puts "MessagePack size: #{msgpack_verifier.generate(data, expires_at: expiry).bytesize}"▶ベンチマーク(クリックすると表示されます)
Warming up -------------------------------------- Marshal 1.206k i/100ms JSON 1.165k i/100ms AS::JSON 790.000 i/100ms MessagePack 1.798k i/100ms Calculating ------------------------------------- Marshal 11.748k (± 1.3%) i/s - 59.094k in 5.031071s JSON 11.498k (± 1.4%) i/s - 58.250k in 5.066957s AS::JSON 7.867k (± 2.4%) i/s - 39.500k in 5.024055s MessagePack 17.865k (± 0.8%) i/s - 89.900k in 5.032592s Comparison: MessagePack: 17864.9 i/s Marshal: 11747.8 i/s - 1.52x (± 0.00) slower JSON: 11498.4 i/s - 1.55x (± 0.00) slower AS::JSON: 7866.9 i/s - 2.27x (± 0.00) slower Marshal size: 254 JSON size: 234 MessagePack size: 194さらに、
ActiveSupport::MessagePack::CacheSerializerはActiveSupport::Cacheのコーダーに適したシリアライザーである。ActiveSupport::MessagePack::CacheSerializerは、読み込み済みの関連付けを含むActiveRecord::Baseインスタンスをシリアライズ可能で、ActiveSupport::MessagePackと同様に、パフォーマンスの向上とペイロードサイズの削減を実現できる。# frozen_string_literal: true require "benchmark/ips" require "active_support/message_pack" ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Schema.define do create_table :posts, force: true do |t| t.string :body t.timestamps end create_table :comments, force: true do |t| t.integer :post_id t.string :body t.timestamps end end class Post < ActiveRecord::Base has_many :comments end class Comment < ActiveRecord::Base belongs_to :post end post = Post.create!(body: "x" * 100) 2.times { post.comments.create!(body: "x" * 100) } post.comments.load cache_entry = ActiveSupport::Cache::Entry.new(post) Rails70Coder = ActiveSupport::Cache::Coders::Rails70Coder CacheSerializer = ActiveSupport::MessagePack::CacheSerializer Benchmark.ips do |x| x.report("Rails70Coder") do Rails70Coder.load(Rails70Coder.dump(cache_entry)) end x.report("CacheSerializer") do CacheSerializer.load(CacheSerializer.dump(cache_entry)) end x.compare! end puts "Rails70Coder size: #{Rails70Coder.dump(cache_entry).bytesize}" puts "CacheSerializer size: #{CacheSerializer.dump(cache_entry).bytesize}"▶ベンチマーク(クリックすると表示されます)
Warming up -------------------------------------- Rails70Coder 329.000 i/100ms CacheSerializer 492.000 i/100ms Calculating ------------------------------------- Rails70Coder 3.285k (± 1.7%) i/s - 16.450k in 5.008447s CacheSerializer 4.895k (± 2.4%) i/s - 24.600k in 5.028803s Comparison: CacheSerializer: 4894.7 i/s Rails70Coder: 3285.4 i/s - 1.49x slower Rails70Coder size: 808 CacheSerializer size: 593/cc @byroot: あなたが
msgpackやShopify/paquitoで多くの作業を行ってきたことを認識している。このプルリクはpaquitoに強くインスパイアされているので、共著に加えておいた。このコードをpaquitoで役立てる(もしくはmsgpackにアップストリームする)ためにこちらで何かできることはあるだろうか?
同PRより
つっつきボイス:「お、ついにMessagePackがRailsにやってきた🎉」
「MessagePackって何でしょうか?」「MessagePackは古くからAPI通信のプロトコルなどでペイロードとして使われるフォーマットの1つですね: 既にRailsでもシリアライザとして使っている人は結構いるんじゃないかな」
「たしかにMessagePackサイトを見るとものすごくたくさんの言語やライブラリに対応していますね」「そうそう、特定の言語に依存しないフォーマットです: 言語がJSONに対応するとJSONパーサーなどが必要になるのでコードベースが大きくなりがちなんですが、MessagePackはそのあたりがシンプルになっていたという覚えがあります」「JSONはテキストフォーマットだけどMessagePackはバイナリフォーマットで、その分コンパクトなんですね」「MessagePackは読んだそばから解析できるのもいい点です」
🔗 PostgreSQL: UNIQUE制約でUSING INDEXをサポート
動機/背景
#46192の続き。詳細
UNIQUE制約(一意制約)の定義で既存のインデックスを利用する:using_indexオプションを追加した。:using_indexオプションを使うと、指定されたUNIQUEインデックスがUNIQUE制約に変更される。add_unique_key :users, deferrable: :immediate, using_index: 'unique_index_name'UNIQUE制約の内部ではUNIQUEインデックスを構築する。
UNIQUEインデックスが既に作成済みであれば、制約を生成するときにUNIQUEインデックスの作成が不要になるので、UNIQUE制約の構築がかなり高速化される。先延ばし不可の(non-deferrable)UNIQUEインデックスは、先延ばし可能な(deferrable)UNIQUE制約に変更可能。
同PRより
つっつきボイス:「PostgreSQLのUSING INDEXで既存のUNIQUEインデックスをUNIQUE制約に変更できるようになったそうです」「deferrableといえば、以前PostgreSQLのDEFERRABLEが使えるようになったという話がありましたね(ウォッチ20211011): 今回の改修によって、deferrableでないUNIQUEインデックスを後付けでdeferrableにできるようになったんですね、なるほど」
参考: PostgreSQL 14.5ドキュメント CREATE TABLE
DEFERRABLE
NOT DEFERRABLE
制約を遅延させることが可能かどうかを制御します。 遅延不可の制約は各コマンドの後すぐに検査されます。 遅延可能な制約の検査は、(SET CONSTRAINTSコマンドを使用して)トランザクションの終了時まで遅延させることができます。NOT DEFERRABLEがデフォルトです。 現在、UNIQUE、PRIMARY KEY、EXCLUDE、REFERENCES(外部キー)制約のみがこの句を受け付けることができます。NOT NULLおよびCHECK制約は遅延させることができません。 遅延可能な制約はON CONFLICT DO UPDATE句を含むINSERT文において、競合解決のために使うことはできないことに注意してください。
CREATE TABLEより
🔗 fixture読み込み時の外部キーエラーの理由を表示するようになった
#42674でfixture作成時Railsが外部キーを検証する機能が追加された。その後、どの外部キーで違反が発生したかも表示されたら助かるというフィードバックをもらった(これまでの修正の試みについては#44943と#47780を参照)。
このプルリクは、それらのプルリクでのアイデアのいくつかを統合可能にする。
all_foreign_keys_valid?を非推奨にし、今後はcheck_all_foreign_keys_valid!を使う。- PostgreSQLとSQLiteのアダプタでraiseするときに、外部キーエラーを詳しく表示する。
- 他のプルリクの作者を共著者にしてクレジットする。
- 新しい非推奨化APIで動作するよう、非推奨化処理を更新する。
- テストを更新する。
今後エラーメッセージは以下のように表示される。
PostgreSQL:Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations. Error from database: PG::ForeignKeyViolation: ERROR: insert or update on table "fk_pointing_to_non_existent_objects" violates foreign key constraint "fk_that_will_be_broken" DETAIL: Key (fk_object_to_point_to_id)=(980190962) is not present in table "fk_object_to_point_tos". CONTEXT: SQL statement "UPDATE pg_constraint SET convalidated=false WHERE conname = 'fk_that_will_be_broken' AND connamespace::regnamespace = 'public'::regnamespace; ALTER TABLE public.fk_pointing_to_non_existent_objects VALIDATE CONSTRAINT fk_that_will_be_broken;" PL/pgSQL function inline_code_block line 16 at EXECUTESQLite:
Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations. Error from database: Foreign key violations found: fk_pointing_to_non_existent_objectscc @s-mage、@danini-the-panini
同PRより
つっつきボイス:「これは嬉しい機能👍」「fixture読み込みのエラーを何でもActiveRecord::StatementInvalidにせずに詳しい情報を出してくれるの助かります😂」
# activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L637
# Override to check all foreign key constraints in a database.
def all_foreign_keys_valid?
+ check_all_foreign_keys_valid!
true
+ rescue ActiveRecord::StatementInvalid
+ false
+ end
+ deprecate :all_foreign_keys_valid?, deprecator: ActiveRecord.deprecator
🔗 ActiveSupport::Inflector#underscoreで正規表現のキャプチャを削減
- PR: Reduce captures in
ActiveSupport::Inflector#underscoreby nobu · Pull Request #47982 · rails/rails
動機/背景
このプルリクは小さな改善のために作成した。詳細
このプルリクでは、ActiveSupport::Inflector#underscoreのgsub!でキャプチャを行わないようにしている。
これによって、$1/$2による短い文字列の作成とアンダースコア追加をスキップし、置き換え対象文字列から除外する。
同PRより
# activesupport/lib/active_support/inflector/methods.rb#L97
def underscore(camel_cased_word)
return camel_cased_word.to_s.dup unless /[A-Z-]|::/.match?(camel_cased_word)
word = camel_cased_word.to_s.gsub("::", "/")
word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
- word.gsub!(/([A-Z])(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }
+ word.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
word.tr!("-", "_")
word.downcase!
word
end
つっつきボイス:「パッチモンスターことnobuさんによる改修です」「正規表現の()で余分なキャプチャが発生するのを減らした、なるほど」「使わないキャプチャがあるとメモリを余分に食いますよね」
参考: Rails API underscore -- ActiveSupport::Inflector
参考: Rubyist Hotlinks 【第 9 回】 中田伸悦さん
なお、Rubyの正規表現でキャプチャはしないけどやむを得ず(式)を使いたい場合は、(?:式)のように書くことでキャプチャされなくなります。なお、先読みや後読みはキャプチャされません。
🔗 ドキュメント: アセットパイプラインガイドを更新
つっつきボイス:「既に日本語版Railsガイドにも反映されています↓が、Rails公式情報でも紹介されました」「Rails 7.0に合わせてかなりがっつりリライトされている」「アセットパイプラインガイドの原文更新には自分も関わったんですが、その中でSprocketsのガイドを分けようかという構想も持ち上がっていました(#47012コメント)」「Sprocketsのガイドはある方がいいですね」
参考: アセットパイプライン - Railsガイド
参考: 📕 アセットパイプラインガイドを大幅更新|YassLab 株式会社|note
🔗Rails
🔗 スライド『Rails 7.1をn倍速くした話』
すみません、大変遅くなりましたが、鹿児島Ruby会議02の講演「Rails 7.1をn倍速くした話」の資料を公開しました。
なお、ここで話したパッチはまだupstreamに全てコミットできてないので、現時点ではn倍速くなったRailsは皆さんのお手元ではご確認いただけません…… https://t.co/8lfqnTFIQh #k_ruby https://t.co/TCbzGNGoRj— Akira Matsuda (@a_matsuda) April 27, 2023
つっつきボイス:「amatsudaさんのこのスライドをついさっきツイートで見かけたので早速取り上げました」「Railsのedgeブランチにamatsudaさんによる最適化がいろいろ入っていたのはこれまでも見かけていましたが、スライドにまとめてくれたんですね」「対象を絞り込みながら最適化を進める手法の解説めっちゃ面白い」「ヤン提督って誰かと思ったら銀英伝なんですね」
「このあたりの改修↓はamatsudaさんのコミットで見た覚えがあります(ウォッチ20221220)」「そうそう、Arrayオブジェクトの生成を避けるための改修ですね」
「スライドにもあるように、この種の最適化はライブラリやミドルウェア向けのものなので、アプリケーションコードでは、データ処理・解析系などでよほどパフォーマンス重視の実装を求められるのでない限りやるべきではないでしょうね」
「edgeにこんなにパッチを当ててくれた↓」「すごい」「今回はAction Controller編で、次回はAction View & Active Record編か」「Rails 7.1の高速化、期待できそう」
後でamatsudaさんのedgeのコミットリストを見つけました↓。
参考: Rails Contributors - #20 Akira Matsuda - Edge -- 92件
🔗Ruby
🔗 Rubyの*argsや**kwargsや...によるdelegationが遅い問題
- issue: Bug #19165: Method (with no param) delegation with *, **, and ... is slow - Ruby master - Ruby Issue Tracking System
- PR: Improve performance of
f(*a, **kw)style method dispatch by ko1 · Pull Request #6920 · ruby/ruby -- 現在ドラフト
つっつきボイス:「上のスライドで取り上げられていたissue #19165です」「パラメータなしの*argsや**kwargsでこんなことが起きていたとは」「この問題を見つけたのすごい」「ko1さんが早くも改善に取り組み始めていますね(現在ドラフト)」
今週は以上です。
バックナンバー(2023年度第2四半期)
週刊Railsウォッチ: 第1回Rails Worldが10月に開催、『研鑽Rubyプログラミング』でRuby本体も高速化ほか(20230427後編)
- 20230425前編 Rails 7.1の複合主キー対応が引き続き進む、exceptメソッドにwithoutエイリアスが追加ほか
- 20230413後編 ShopifyのRubyパーサーyarp、RJITを書いた理由ほか
- 20230412前編 複合主キーの実装が進む、Rails公式のバグ再現用テンプレートほか
- 20230406後編 Rubyオブジェクトモデルクイズの最難問ほか
- 20230405前編 Arel::Nodes::NodeにAPIドキュメントが追加、rubocop-mdほか
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

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