こんにちは、hachi8833です。間が空いてしまって申し訳ありません。🙇🙏
🔗Rails: 最近の改修(Rails公式ニュースより)
間が空いてしまったので、昨年12月頃からの主な改修をピックアップしました。🙏
- 公式更新情報: Ruby on Rails — Schema dumper versions formatter and more
- 公式更新情報: Ruby on Rails — Revert Active Model's Normalization and Cache Store gets session ID uniqueness flag
- 公式更新情報: Ruby on Rails — New Rails Tutorial and Unpacked Videos, Releases, ActiveModel::Attributes::Normalization and more!
🔗 ActiveRecord::Normalization
がActive Modelに移行(その後取り消し)
動機/背景
- クローズ: rails/rails#53793
- フォローアップ: rails/rails#43945
この振る舞いをサポートするために、実装の大部分を新しい
ActiveModel::Normalization
モジュールに移動した。「永続性」関連の言語、メソッド、テスト範囲はすべて削除された。追加情報
実装における唯一の変更点は、属性の読み書きに関連している。
def normalize_attribute(name) # 値を新規の非normalize値として扱う - self[name] = self[name] + send(:"#{name}=", send(name)) end
このプルリクは、rails/rails#53886のような変更が行われた場合は取り消す可能性がある。
同PRより
つっつきボイス:「ActiveRecord::Normalization
を切り出して、Active Modelで使えるようにしたんですね」「移動できるものは移動しようという感じかな」
参考: Rails API ActiveRecord::Normalization
「これには続きがあって、上のプルリク↑がマージされたのは昨年だったんですが、その後以下でマージが取り消されていました↓」「あら」「APIドキュメントを準備するまで保留か」「遠からず実際に移動しそうではありますね👍」
#53887を取り消す。
これはマージしてよい状態ではなかった。ドキュメントもActive Recordから移動すべき。
振る舞いをドキュメント化するためだけに空のモジュールを必要とすべきではない。
同PRより
🔗 ActiveRecord::Persistence#increment!
を新規レコードで呼んだ場合はraiseするよう修正
修正: #26420
Rails 4では新規レコードに対して
#increment!
を呼び出すと保存される。この振る舞いはよくないと思うが、もう何年も修正されていない。
#update_columns
と同様にraiseする方がよい。
同PRより
つっつきボイス:「保存していない新規レコードやdestroy済みのレコードを更新できたらおかしいので、この修正はわかる👍」「!
付きの#increment!
って使ったことなかった」「UPDATE
文でSET hoge=hoge+1
みたいなのを楽に書きたいときに使うのかなと思ったけど、その属性だけを即値でインクリメントするみたいですね」
# activerecord/lib/active_record/persistence.rb#L649
def increment!(attribute, by = 1, touch: nil)
+ raise ActiveRecordError, "cannot update a new record" if new_record?
+ raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
+
increment(attribute, by)
change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
self.class.update_counters(id, attribute => change, touch: touch)
public_send(:"clear_#{attribute}_change")
self
end
参考: Rails edge API increment!
-- ActiveRecord::Persistence
データベースに更新を書き込む
increment
のラッパー。attribute
のみが更新され、レコード自体は保存されない。つまり、変更された他の属性はダーティなままとなる。バリデーションとコールバックはスキップされる。
increment!
--ActiveRecord::Persistence
より
🔗 シリアライズされる属性にcomparable
オプションを指定可能になった
修正: #28416
シリアライズされた属性を扱うときに割とよくある問題は、オブジェクトのシリアライズ表現が時間の経過とともに変化する可能性があること。変化する理由としては、あるシリアライザから別のシリアライザに移行したとき (YAMLからJSONやmsgpackへの移行など)や、使っているシリアライザの出力が微妙に変更されたときがある。
1つの例として
libyaml
がある。以前は末尾に余分な空白があったが、最近yaml/libyaml#186で修正された。このようなことが起こると、実際には変更されていないのに変更されたと報告されるレコードが多数発生し、最もましな場合でもデータベースへの書き込みが大幅に増加し、最悪の場合は厄介なバグにつながる。
したがって、シリアライズ表現を比較するのではなくデシリアライズされたオブジェクトを比較するのが理想的ではあるが、デシリアライズされた型に、オブジェクトを深いところまで適切に比較できる
==
メソッドが存在することを想定するわけにはいかない。すなわち、現状の最善の策としては、型が比較可能であると宣言されている場合にのみ比較することである。
同PRより
つっつきボイス:「このプルリクを見ていて、Rubyのバージョンが変わると以前のバージョンでシリアライズしたオブジェクトをデシリアライズできなくなるみたいな話を思い出しました」「あぁ、それはMarshall.dump
したものをMarshall.load
できるかどうかはそのときのRubyのバージョンに依存することがあるという話ですね」
参考: Marshal.#dump
(Ruby 3.4 リファレンスマニュアル)
参考: Marshal.#load
(Ruby 3.4 リファレンスマニュアル)
「JSONやmsgpackをシリアライザとして使っていれば、それ自体に変更が入らない限り通常は問題ないはず」「プルリクではシリアライザを変更した場合に問題が起きることがあると書かれていますけど、シリアライザを変更することってそうそうなさそうですよね」「たまに変更が必要になることがなくもないけど、あんまりやりたくない作業」「"デシリアライズされた型にオブジェクトを深いところまで適切に比較できる==
メソッドが存在することを想定するわけにはいかない"、これはその通り」
「このプルリクではActiveRecord::AttributeMethods::Serialization
のserialize
にcomparable
オプションを追加して、オプションがtrueのときだけ比較可能にしたんですね」「なるほど」
# activerecord/lib/active_record/attribute_methods/serialization.rb#L183
- def serialize(attr_name, coder: nil, type: Object, yaml: {}, **options)
+ def serialize(attr_name, coder: nil, type: Object, comparable: false, yaml: {}, **options)
coder ||= default_column_serializer
unless coder
raise ArgumentError, <<~MSG.squish
missing keyword: :coder
If no default coder is configured, a coder must be provided to `serialize`.
MSG
end
column_serializer = build_column_serializer(attr_name, coder, type, yaml)
attribute(attr_name, **options)
decorate_attributes([attr_name]) do |attr_name, cast_type|
if type_incompatible_with_serialize?(cast_type, coder, type)
raise ColumnNotSerializableError.new(attr_name, cast_type)
end
cast_type = cast_type.subtype if Type::Serialized === cast_type
- Type::Serialized.new(cast_type, column_serializer)
+ Type::Serialized.new(cast_type, column_serializer, comparable: comparable)
end
end
参考: Rails edge API serialize
-- ActiveRecord::AttributeMethods::Serialization::ClassMethods
- Ruby on Rails API
🔗 Range#each
とRange#step
のモンキーパッチを削除した
#11474を取り消す。
動機/背景
Range#each
rangeの最初の要素が
#succ
を実装していない場合、Range#each
はTypeErrorをraiseする。現状のモンキーパッチはRubyと同じ振る舞いを実装しているため、当てる価値がない。
Range#step
Ruby 3.4で
Range#step
の振る舞いが変更され(#7444)、連続する要素の生成で#+
メソッドが使われるようになったことで、一部の非数値型に対して適切な結果が得られるようになった。この変更により、次のようなコードが可能になる (Ruby3.4.0-preview2
でテスト済み)。(Time.current..).step(30.minutes).first(3) # => # [Thu, 12 Dec 2024 23:55:41.568749000 CET +01:00, # Fri, 13 Dec 2024 00:25:41.568749000 CET +01:00, # Fri, 13 Dec 2024 00:55:41.568749000 CET +01:00]
ActiveSupport::TimeWithZone
では#+
を独自に実装している。しかし、Rubyの
Range#step
がオーバーライドされており、特にActiveSupport::TimeWithZone
の境界が妨げられるため、この独自実装は現在機能しない。(Time.current..).step(30.minutes).first(3) # can't iterate from ActiveSupport::TimeWithZone (TypeError)
#11474でこのオーバーライドが導入された当時は、Rubyの振る舞いが異なっていた。
Ruby 3.4より前の
Range#step
は、Range#each
と同様にTypeErrorをraiseするので、これらのバージョンにモンキーパッチを当てる価値はない。詳細
このプルリクは、
Range#each
とRange#step
のモンキーパッチを削除する。
同PRより
つっつきボイス:「このあたりは年末にたまたま追いかけていてWebチーム内発表でも取り上げたんですが、要するにRuby 3.4.0の変更↓によってこれまでActive Supportで当てられていたモンキーパッチが不要になったというか、モンキーパッチがあると動かなくなったので削除したという流れでした」「こんなふうにRubyの内部実装の変更でRails側の対応が変わることはたまにありますね」
参考: Range#step
の意味が少し変わった -- プロと読み解くRuby 3.4 NEWS - STORES Product Blog
参考: Make Range#step to consistently use + for iteration by zverok · Pull Request #7444 · ruby/ruby
なお、Ruby 3.4では、非数値のrangeでstep(0)
やstep(0.0)
を呼び出してもエラーにならなくなっています↓。
$ ruby -v
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24]
$ irb
irb(main):001> ('a'..'c').step(0)
=> #<Enumerator: ...>
irb(main):002> ('a'..'c').step(0.0)
=> #<Enumerator: ...>
🔗 ActionDispatch::Session::CacheStore
にcheck_collisions
オプションが追加された
新しく生成されるセッションIDは、128ビットのランダムネスを利用する。これは衝突が発生しないことを保証するには十分すぎるほどだが、セッションをさらに強化する必要がある場合は、このオプションを有効にして、セッションストアでIDが実際に空いているかどうかを保証できる。ただし、これによりセッション作成時に追加の書き込みが発生する。
Shia
同Changelogより
背景
ActiveSupport::Cache::Store
をセッションストレージとして利用するSession::CacheStore
は、セッションIDの一意性を保証するためにSecureRandom.hex(16)
に依存している(ref)。
これにより 2^128個の値が提供され、ほとんどの場合に実質的に十分だが(参考: 誕生日攻撃 - Wikipedia)、追加のオーバーヘッドを犠牲にしても一意性を保証する追加対策が好ましいシステムもある(例:RedisCacheStore
の単一のset
コマンド)。提案
生成されたセッションIDが既に利用されているかどうかをチェックすることで、セッションIDの一意性を保証するオプションを
Session::CacheStore
に追加することを提案したい。このオプションは、ensure_sid_uniqueness
のような名前(またはもっと適切な名前)になるだろう。実装の参照
- Dalli gemの
Session::MemCacheStore
には、生成されたIDが既にに存在する場合にセッションIDを再生成するロジックが含まれている。redis-store
のセッションストア実装でも同様のアプローチが採用されている。
つっつきボイス:「セッション用キャッシュストアにcheck_collisions
というオプションが追加されたそうです」「これを有効にするとパフォーマンスが落ちる代わりに、新しいセッションIDを生成するときに既存のセッションIDと絶対かぶらないようチェックされるんですね: 多少の速度を犠牲にしてでも絶対にセッションIDが重複しないことをシステム上保証したいケースは確かにありそう👍」
# actionpack/lib/action_dispatch/middleware/session/cache_store.rb#L26
class CacheStore < AbstractSecureStore
def initialize(app, options = {})
@cache = options[:cache] || Rails.cache
options[:expire_after] ||= @cache.options[:expires_in]
+ @check_collisions = options[:check_collisions] || false
super
end
...
end
+ def generate_sid
+ if @check_collisions
+ loop do
+ sid = super
+ key = cache_key(sid.private_id)
+ break sid if @cache.write(key, {}, unless_exist: true, expires_in: default_options[:expire_after])
+ end
+ else
+ super
+ end
+ end
参考: Rails edge API ActionDispatch::Session::CacheStore
- Ruby on Rails API
🔗 ActiveStorage::Attachment <-> Blob
リレーションにinverse_of
を追加したときの挙動を修正
動機/背景
このコミットは、#50800で導入された振る舞い変更を修正するため。この振る舞い変更によって、#53451や#53452で報告された問題が発生する。
詳細
コードパスやコールバックが多数発生していて問題が微妙であるため、説明が少し難しいが、簡単に言うと、
ActiveStorage::Attachment <-> Blob
リレーションにinverse_of
を追加すると、添付ファイルの保存方法に振る舞いの違いが追加される。問題
#50800以後、
ActiveStorage::Attachment <-> Blob
にinverse_of
を追加すると、関連付けられた AttachmentがBlobで認識されるようになり、関連付けの保存コールバックの順序に問題が発生するようになった。class User < ApplicationRecord has_one_attached :avatar blob = create_active_storage_blob("avatar.png") User.create!(avatar: blob) end
#50800がマージされる前は、以下のステップをたどってレコードが永続化された。
- Blobが作成される
- Userが作成される
- BlobがUser<->Blob autosave関連付けを介して更新される
Active storageは、添付ファイルが作成されるときにBlobのメタデータの一部を変更する(rails/activestorage/lib/active_storage/attached/changes/create_one.rb at ad9bc2a51d5980d1c0990db97e552015410778f0 · rails/rails)- AttachmentがUser<->Attachment autosave関連付けを介して作成される
しかし#50800以後は以下のように変わった。
1-3. (上と同じ)
4. Attachmentが(Userではなく)Blob<->Attachment autosave関連付けを介して作成される
これが問題になる理由は、#53451と#53452でそれぞれ異なるため、個別に説明する。
#53451での問題
Blob#touch_attachments
のafter_update
コールバックより前のタイミングに実行されるafter_update
コールバックで、Blob関連付けを介して添付ファイルが作成されるようになったため、Blobが関連付けられた添付ファイルをtouch
するようになり、その結果、レコード(上の場合はUserのレコード)が読み込まれてtouch
される。
user.touch_later
を呼び出すと、ユーザー(メモリ上のユーザーではなく、データベースから読み込まれたユーザー)がトランザクションに追加され、最終的にメモリ上のユーザーが置き換えられてcommitコールバックが実行される(これとこれ。
以下に簡単な例を示す。class Car < ApplicationRecord attr_accessor :engine_sound after_save :touch_car after_commit :rev_engine def touch_car Car.find(1).touch end def rev_engine puts engine_sound # => nil end end Car.new(id: 1, engine_sound: "vroom")
#53452での問題
従来は、Userの
Attachment#previously_new_record?
はtrueになっていた。これは、リレーションのautosave関連付けにより添付ファイルが作成されていたため。AttachmentはBlobのautosave関連付けを介して保存されるようになったため、User<->Attachmentのautosave関連付けが呼び出されると、添付ファイルはすでに保存済みなので更新と見なされ、コードパスが変わり、結果として
@previously_new_record
フラグがリセットされる(これとこれ)。ソリューション
Blob<->Attachmentリレーションを変更してautosaveをfalseに設定する方法よりも良い方法が見当たらない。
この方法では、Blobは添付ファイルを保存する責務を持たないが、レコード(User)には保存する責務がある。
また、Blobに添付ファイルを保存する責任を持たせることにそもそも意味がないと思われる。BlobとAttachmentをインスタンス化し、Blobを保存してAttachmentを保存しないままにしておくと、問題が発生する理由を考えようとしたが、何も思いつかなかった。理由は、Attachmentが有効であるためにはレコード(Userなど)が必要であり、その同じレコードがAttachmentを保存するため。
重要
アプリケーションが何らかの理由で既存のBlobのメタデータを変更して保存した場合、既存のAttachmentとダーティ属性はこのコミットでは保存されない。
よくわからないのだが、これは普通によくあるシナリオなのだろうか?
追加情報
このコミットの代わりに、#50800を取り消して、Blob<->Attachment の
inverse_of
を明示的にnil
に設定する方法もありだろう。
同PRより
つっつきボイス:「説明は長いですが、#50800がマージされて以降のAttachmentモデルのautomergeのリレーションが、inverse_of
を追加したときにUser<->AttachmentからBlob<->Attachmentに変わってしまったということだそうです」「割とレアなケースみたいだけど、こういう微妙な変更を知らずに踏むとつらい」「修正は以下の1行だけでした↓」
# activestorage/app/models/active_storage/blob.rb#L19
class ActiveStorage::Blob < ActiveStorage::Record
MINIMUM_TOKEN_LENGTH = 28
has_secure_token :key, length: MINIMUM_TOKEN_LENGTH
store :metadata, accessors: [ :analyzed, :identified, :composed ], coder: ActiveRecord::Coders::JSON
class_attribute :services, default: {}
class_attribute :service, instance_accessor: false
- has_many :attachments
+ has_many :attachments, autosave: false
添付ファイルがあるレコードのダーティ属性がリセットされ、そのレコードの
after_commit
コールバックが期待どおりに動作しなくなる問題が修正された。この変更は内部的なものであり、アプリケーションの変更を必要としない。Active Storageの添付ファイルは引き続き (別のリレーションを介して)自動保存される。
Edouard-chin
同Changelogより
🔗 structure.sql
ファイル内のバージョン表示を工夫してマージの競合を削減した
- PR: Introduce versions formatter for the schema dumper by fatkodima · Pull Request #53797 · rails/rails
- スキーマダンパー用のバージョンフォーマッタを導入
スキーマダンパーが
structure.sql
ファイル内のバージョン情報をフォーマットする方法をオーバーライド可能になった。
改修前は、このファイルのスキーマのバージョンは降順で単純にソートされていたが、大規模なチームでは、これによりリストの上部付近でマージの競合が多くの発生する可能性がある。カスタムフォーマッタにカスタムソートロジック(バージョンのハッシュ値によるソートなど)を提供できるようにしたことで、競合の数を大幅に削減できるようになった。
fatkodima
同Changelogより
#44363の続き(cc @ghiculescu)。
従来は、(スキーマの)バージョンが降順でソートされ、新しいバージョンがリストの先頭に追加されていた。チームの人数が十分多い場合、このリストの先頭付近で多くのマージ競合が発生する可能性がある。私の会社では多くの開発者が多くのスキーマ変更をプッシュしており、誰かがプルリクをマージする時間の半分で、既存の多数のプルリクがmasterブランチと競合するようになった。
この修正によって、バージョンは逆順にした表現(
20241201183002
->20038110214202
)によって降順でソートされるようになる(昇順でもかまわないが、以前の実装との一貫性を保つため降順にした)。これにより、新しいバージョンが既存のバージョンリストのランダムな場所に挿入されるようになり、マージ競合の可能性が大幅に減少する。簡単な計算をしたところ、従来は上位5つの要素のリスト内で競合が発生する可能性が20%あったが、変更後は600個以上の項目のリスト全体で競合が発生する可能性が0.1%に下がった。また、ダンプされた
structure.sql
ファイル内の各バージョンにも、逆バージョン表現のコメントを追加するようにした。これにより、マージの競合を解決しやすくなる。ただし、これを削除して、アルゴリズムを説明するコメントINSERT
の先頭に追加することも可能。既存のアプリの場合、次回
structure.sql
が生成されたときに大きな差異が発生することになる。
同PRより
つっつきボイス:「大勢の開発者がスキーマをそんなにしょっちゅう更新することってあるんでしょうか?」「普通にありますね: 競合をなるべく防ぐためにも、最初にスキーマ変更だけを行う小さなプルリクを投げて速やかにマージすること、というルールを設定したりすることもあります」「なるほど、スキーマ変更前にSlackとかで声掛けしたりしますか?」「それはさすがに繁雑になりすぎて開発速度が落ちるでしょう」「それもそうですね😅」
「バージョンを逆順にするって最初何だろう?と思ったけど、20241201183002
->20038110214202
のように日時の表示が逆順になればバージョンの挿入場所が適度にランダムになるので、バージョンリストの冒頭に更新が集中しにくくなってコンフリクトが減る、ということのようですね」「あ、そういうことか!」「この改修は、schema.rbじゃなくてstructure.sqlを使っている場合にコンフリクトを減らすということなんですね」「最初にスキーマ変更だけをマージするような運用にしていれば普段そこまで困らないと思うけど、スキーマ変更の競合が減ったのはよさそう👍」
🔗 path_params
がクエリパラメータ経由で渡された場合にクラッシュしないよう修正
Railsアプリのエンドユーザーによって
path_params
がurl_for
に渡される可能性がある。Kaminariは現在これをトリガーする。また、これが起こらないようにするためのテストが書きにくい。機能テストで
path_params: "string"
を渡すと、テストランナーがクラッシュする(クラッシュするRailsバグレポートテンプレートのこの例を参照)。文字列値は Rails内部で500エラーを引き起こす。
?path_params[inject]=string
ハッシュを使うと、URL生成にpath_param
を挿入することが可能になる。ただし、path_params
は実際のパラメータよりも優先度が低く、一致するものがない場合には無視されるため、脆弱性はないと思われる。
また、非常によく似た#39616が警告しなかったことを考えると、これを脆弱性だとは思わない。それでも、このキーを削るのはよいことだと思う。なお、似たような問題に対する脆弱性がかつて存在していた: GHSA-r5jw-62xg-j433動機/背景
自分は、RubyGems.orgのセキュリティスキャンによって発生した500エラーを修正しようとしていた。こちらが知らない研究者が、多数のページに膨大なパラメータリストを送信していた。以下はその抜粋。
これによってrubygems.orgで500エラーが少なくとも2件発生する。対応の手間は小さいものの、オンコールで誰かを叩き起こすには十分。
実際の問題はkaminariにまでさかのぼり、そのためにkaminari/kaminari#1123をオープンした。
これはkaminari側で修正する必要があると思うが、このキーが適切に処理されていることを確認するためのテストを書くのがやや難しいため、Rails側でも修正する必要があると思う(上部にリンクしたバグレポートテンプレートを参照)。
このプルリクによって、
path_params:
キーを含まないURL生成ごと1個のハッシュアロケーションと1個のマージも節約される(ほとんどの場合)。ただし、このメリットはoption = option.dup
によってほとんど帳消しになる可能性がある(重複を保存するように再構成することはおそらく可能だろう)。詳細
このプルリクは、
ActionDispatch::Journey::Formatter#generate
を変更して、ハッシュの場合にのみpath_params
を抽出してマージするようにする。このプルリクでは、
path_params
のフィルタリングについては特に何も対処していない(kaminariがこれを実行すべきだと思う)。ただし、これにより、path_params
がハッシュではない場合のクラッシュに対処できるようになり、ほとんどのURL生成の効率がわずかに高まる。追加情報
これは、自分の同僚@simiがRailsに送信した別の修正#39616と非常によく似ている。この修正も、現在のRailsコードにまだ適用できるのであれば、同じ理由で注目に値すると思う。
それまでは、不要な500エラーが発生しないように、rubygems.orgでもこの修正をパッチとして適用している。
同PRより
つっつきボイス:「このプルリクはrubygems.orgのメンテナーだそうです」「rubygems.orgに誰かさんがpath_params
入りのリクエストを投げて500エラーが頻発していたのを修正するためなのね」「脆弱性ではないにしても、path_params
を投げるだけで500エラーになるのは嬉しくないヤツ」「path_params
はクエリパラメータの形式だと無理やり送信できてしまうんですね」
🔗Rails
🔗 Rails公式サイトにドキュメントへのランディングページが追加(Rails公式ニュースより)
つっつきボイス:「最近のRails公式サイトのリニューアルの一環としてドキュメントのランディングページが追加されました↓🎉」「なるほど、Rails GuidesだけではなくAPIドキュメントやDiscussionへの動線もポータル的にここに置いたんですね👍」
🔗 libyear: 依存関係の更新がどのぐらい遅れているかを数値化するツール
つっつきボイス:「これは今翻訳中のEvil Martiansの記事↓で知りました」「なるほど、依存関係の更新がどのぐらい遅れているかを数値化するというアイデアなのね: CIに入れてみてもいいかも👍」「libyearはRuby以外にもJavaScriptやPythonやRustやGoなどいろんな言語に対応していますね」「大規模プロジェクトならlibyearが数世紀に達することもあるみたいです😆」
(上の記事はめちゃめちゃ長いので翻訳公開までしばらくお待ちください🙏)
🔗 RailsガイドがRails 8.0.1に対応
2024年12月にリリースされた Rails 8.0.1 に合わせて、#Railsガイド も Rails 8.0.1 に対応しました🆙🎉
Web版と電子書籍版の両方をリリースしていますので、お好みに合わせてぜひご活用ください📕✨アップデートの詳細は note 記事で詳しく紹介していますhttps://t.co/2S2Jm41ltD pic.twitter.com/Q3CsEl4TX9
— Railsガイド 📕 (@RailsGuidesJP) January 8, 2025
つっつきボイス:「昨年12月にRails 8.0.1がリリースされて、Guidesの更新が多かったのでRailsガイドに一通り反映しました」「お〜🎉」「詳しくは上のNote記事で解説していますが、特にRailsをはじめようガイドが大幅に更新されてRails 8の新機能の概要をキャッチアップしやすくなっています」
参考: Ruby on Rails — Rails Version 8.0.1 has been released!
🔗Ruby
🔗 rail_inspector
のSyntaxTreeがPrismに置き換えられた(Rails公式ニュースより)
- PR: Replace SyntaxTree with Prism in
rail_inspector
by skipkayhil · Pull Request #54050 · rails/rails
つっつきボイス:「Railsの更新情報ですが、lintツールであるrail_inspector
のコンフィグでSyntaxTreeがPrismパーサーに置き換えられたそうです」「お〜、着々とPrismが浸透していますね🎉」
# tools/rail_inspector/lib/rail_inspector/configuring.rb#L14
def call(path)
- @cache[path] ||= SyntaxTree.parse(SyntaxTree.read(path))
+ @cache[path] ||= Prism.parse_file(path.to_s).value
end
🔗 syntax_finder: RubyスクリプトをPrismで解析
[ko1/syntax_finder: Analyze your Ruby scripts with prism](https://t.co/wjqMO4UYCG)
— _ko1 (@_ko1) January 15, 2025
つっつきボイス:「これもPrismがらみで、_ko1さんがついこの間作ったツールです」「こんな感じにSyntaxFinder
を継承してlook(node)
メソッドを定義するとPrismベースで検索できるようになるんですね↓: Prismのおかげでこういうのが作りやすくなった👍」「サンプルコードもいろいろ揃っていますね」
# 同リポジトリより
require 'syntax_finder'
# Count up all `if` statements (if/elsif/?:) and `then` keywords.
class IfThenFinder < SyntaxFinder
def look node
if node.type == :if_node
if node.if_keyword_loc # if or elsif
inc node.if_keyword_loc.slice
if node.then_keyword_loc
inc 'then'
# inc path: @file
end
else
# a ? b : c
inc '?:'
end
end
end
end
🔗 その他Ruby
私hachi8833はこの度、地域rbのひとつとして三浦半島.rbの立ち上げに参加しました。近辺のRubyistの皆さま、ご興味がおありでしたらどうぞ。
三浦半島.rb第0回のキックオフイベントのconpassページを公開しました!!
横須賀市の施設で2/22(土)14時から開催します!
ご興味のある方はぜひご参加ください〜!三浦半島.rb #0 キックオフ&ミートアップ https://t.co/z0hx3tMZED #miurahantorb #tokyorubykaigi #地域rb
— 桐生あんず (@anzu_mmm) January 21, 2025
今回は以上です。
バックナンバー(2024年度第4四半期)
週刊Railsウォッチ: Rails公式のdevcontainerでKamalをサポート、RailsBump.orgほか(20241108)
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)