Rails 4.2.8 の変更点
Rails 4.2.7.1 から 4.2.8 の変更点について、CHANGELOG に変更内容が記述されているものを対象に、自分用に日本語であらためてまとめておくことにしました。
ソースコードの差分は、以下の URL から確認できます。
今回対象とする変更では、以下のライブラリの CHANGELOG にのみ変更点が記載されています。それぞれ順番に説明していきます。
- activerecord
- activesupport
- railties
activerecord
MySQL サーバとの再接続失敗時に発生する例外が改善された
MySQL サーバとの接続が閉じられたときに、これまではインスタンス変数 connection に nil が格納されていましたが、connection を再度利用するような処理が試みられた場合に NoMethodError が発生する状態でした。これが改善され、接続が閉じられたときにもインスタンス変数 connection はそのまま保持されるようになり、再接続試行時に適切な例外が発生するようになりました。
@rafaelfranca, @arthurnn please review cc @sirupsen Backport #26434 for the 4-2-stable branchgithub.com
attribute でモデルの属性の定義を更新したときに attribute_names のキャッシュが適宜更新されるようになった
モデルクラスに属性を追加したり、あるいは既存の属性の型情報を上書きしたり、またこのときに独自の変換ロジックを持つ型情報を与えたりできる、attribute というメソッドがあります。また、モデルの持つ属性一覧を返す attribute_names というメソッドがあります。ここで属性と呼んでいるものは、カラムとほぼ同義のものです。
attribute メソッドを呼び出して新しくモデルに属性を追加した場合、これまでは attribute_names のキャッシュが更新されず、追加した属性の内容が結果に反映されない状態でした。これが、attribute メソッドを呼び出したときに適切にキャッシュが更新されるように改善され、attribute_names が意図通りの結果を返すようになりました。
Summary Model.attribute_names is cached and relies on Model.columns, but does not currently have its cache busted when…github.com
属性の型情報が以前の問い合わせ時と同等のものであれば、以前のクエリキャッシュが再利用されるようになった
ActiveRecord は、各クラスの属性を表現するために、属性ごとに ActiveRecord::Type::Value というクラスのインスタンスを生成します。attribute メソッドが実行されたり、属性の情報を再読込するような処理が実行された場合、このインスタンスは再生成されます。
データベースへの問い合わせ時、ActiveRecord はクエリキャッシュを利用することを試みます。このとき、クエリキャッシュが利用できるかどうかの判断材料として、属性がそのキャッシュが作成されたときのものと同じかどうかという情報が利用されます。この実装には、Hash が利用されています。
Hash が利用されるということは、同じキーであるかどうか調べるために、eql? メソッドと hash メソッドが利用されます。これまでの ActiveRecord::Type::Value の実装では eql? や hash は特に上書きされておらず、完全に同じオブジェクトであるかどうかで判断される状態でした。これでは、前述したような理由でインスタンスが再生成されたあとでデータベースに問い合わせときに、例え以前と属性の情報が同じでも、クエリキャッシュが利用されなくなってしまいます。
今回の変更によって Hash のキーとしての同値性判定ロジックが改善され、別のインスタンスであっても、内部情報が同等のものであれば同じキーとして認識されるようになりました。この結果、上手くクエリキャッシュが利用されるようになりました。
Summary AR's query cache currently depends on the type objects being not only equivalent but also to be the same…github.com
joinsを呼び出したときに、unscoped ブロックの効果が意図通り発揮されるようになった
unscoped ブロックの中で処理を実行することで、それまでに付与されていた default_scope などのスコープが適用されないようにする機能があります。これまでの実装では、joins の呼び出し時にこの unscoped の効果が発揮されない状態でした。これが改善され、意図通りの効果が発揮されるようになりました。
composed_of で実装された属性について、この属性に Hash を割り当てようとしたときに発生する不具合が修正された
複数の値から成る1つの仮想的な属性を定義するために、 ActiveRecord では composed_of を利用できます。compose_of で実装された属性に対して値を割り当てるときに、特定の仕様を満たした Hash を利用した場合に割り当て方が変化するという挙動がそれまでに実装されていましたが、そこで Hash であるかどうかしか確認しておらず、それ以外のケースで Hash を割り当てたい場合に問題になっていました。
これが改善され、従来通りのユースケースにおいても Hash を意図通り割り当てることが可能になりました。
ed_of` So this bug is kinda funky. The code path is basically "if we weren't passed an instance of the class we…github.com
activesupport
DateTime#utc が DateTime ではなく Time を返すようになった
ActiveSupport は DateTime#utc というメソッドを定義しています。このメソッドの返り値が DateTime ではなく Time に変更されました。
DateTime#in_time_zone の実装においては、DateTime#utc の返り値が ActiveSupport::TimeWithZone のコンストラクタに渡され、これが ActiveSupport::TimeWithZone のインスタンス内で保持されます。上記の変更により、これまで ActiveSupport::TimeWithZone の内部では DateTime あるいは Time のどちらかのインスタンスが保持される状態になっていたのが、常に Time が保持されるように変更されました。
Previously these methods could return either a DateTime or a Time depending on how the ActiveSupport::TimeWithZone…github.com
この Time を返すようにするという変更自体は、Ruby 2.4 に対応するための言わば付随的な変更でした。パッチリリースで変更するには大きすぎる変更だとみなされ、後に 4.2.9 で元の状態に戻されます。
In Rails 5.0 the return type of `DateTime#utc` was changed to `Time` to be consistent with the new `DateTime#localtime`…github.com
DateTime#subsec が追加された
DateTime の秒数部分を分数として得るメソッドとして、DateTime#subsec が追加されました。
Time#subsec との対称性を考慮して追加されたのではないかと思います。内部実装としては、単に DateTime#sec_fraction を利用しているだけのようです。
Mirrors the Time#subsec method by returning the fraction of the second as a Rational. (cherry picked from commit…github.com
DateTime#getgm と DateTime#gmtime が追加された
ActiveSupport#TimeWithZone や Time との対称性を考慮し、DateTime にも DateTime#getgm と DateTime#gmtime が追加されました。これらは DateTime#utc の alias となっています。
(cherry picked from commit dbc7a7cb509ef0046afa327d4ac8330259fd8e73)github.com
DateTime#getlocal と DateTime#localtime が追加された
DateTime#getlocal と、その alias である DateTime#localtime が追加されました。システムのタイムゾーンで、レシーバと同等の日時を持つ Time のインスタンスを返します。
そもそも DateTime#getlocal が定義されたのは、Ruby 2.4 の #to_time の挙動が変更されることから、ActiveSupport の設定次第でこの挙動を変更可能にしようとしたためです。このための作戦として、ActiveSupport の設定を参照しつつ getlocal というメソッドを利用しながら適切な Time のインスタンスを返す、#to_time が定義された module が用意されました。この module をActiveSupport::TimeWithZone、DateTime、Time に prepend することで、#to_time の挙動を設定によって制御できるようになるという流れです。
Time#getlocal はそのままで利用可能でしたが、ActiveSupport::TimeWithZone#getlocal はそのままでは問題がありました。というのも、当時の ActiveSupport::TimeWithZone は Time あるいは DateTime のどちらかのインスタンスを内包するような仕組みであり、DateTime が内包されている場合、#getlocal では DateTime#to_time が利用されることになっていたからです。これでは #to_time を呼ぶために #getlocal が必要で、#getlocal を呼ぶために #to_time が必要となり、循環参照してしまう問題がありました。DateTime#utc についての変更の箇所でも前述した通り、これは常に Time を内包するように変更されたため、問題は解消されました。
DateTime#getlocal は元々定義されていなかったため、このタイミングで定義されました。少し長くなりましたが、これが DateTime#getlocal が定義された背景です。#localtime については、DateTime#getlocal の定義場所が calculations.rb に移動されたタイミングで、一貫性を考慮して追加されています。
In Ruby 2.4 the `to_time` method for both `DateTime` and `Time` will preserve the timezone of the receiver when…github.com
DateTime#getlocal` is newly added public API. It's responsible is same as `DateTime#utc`, so `calculations.rb` is a…github.com
Time#sec_fraction が追加された
DateTime#sec_fraction との対称性を考慮して、Time#sec_fraction が追加されました。
内部実装は Rational を返すように実装されていますが、これは後に Time#subsec を使うように変更されることになります。
DateTime#subsec と Time#sec_fraction が追加されたことで、DateTime と Time に対して、#subsec と #sec_fraction のどちらを呼び出しても秒数部分の分数表現が得られるようになったと言えます。
Mirrors the DateTime#sec_fraction method by returning the fraction of the second as a Rational. (cherry picked from…github.com
ActiveSupport.to_time_preserves_timezone が追加された
Ruby 2.4 から #to_time がレシーバのタイムゾーンを引き継ぐようになったことを承けて、ActiveSupport 側でその挙動を制御するための設定が追加されました。デフォルトでは引き継がないように設定されています。
In Ruby 2.4 the `to_time` method for both `DateTime` and `Time` will preserve the timezone of the receiver when…github.com
activesupport 5 で serialize された ActiveSupport::TimeWithZone のインスタンスが 4 で deserialize できない問題が修正された
ActiveSupport::TimeWithZone のインスタンスを YAML 形式に serialize しようとしたとき、これまでは UTC+0 のタイムゾーンに変換された Time のインスタンスとしてエンコードされる状態でした。変換前のタイムゾーンを持つ ActiveSupport::TimeWithZone のインスタンスとして deserialize されるよう、activesupport 5 でこれが改善されました。しかし、5 で serialize したデータを 4 で deserialize しようとしたときに問題が起こることが分かったため、これが修正されました。
Previously when converting AS::TimeWithZone to YAML it would be output as a UTC timestamp. Whilst this preserves the…github.com
It is helpful to be able to run apps concurrently written in successive versions of Rails to aid migration, e.g. run…github.com
ActiveSupport::TimeWithZone#in が夏時間に対応していない問題が修正された
ActiveSupport では、Date や DateTime や Time において、#in が #since の alias となるように実装されていました。しかし ActiveSupport::TimeWithZone#in が #since に alias されていなかったため、ActiveSupport::TimeWithZone#since で加えられている夏時間への対応が加わらず、method_missing により Time#since の実装が利用される状態でした。
この問題が修正され、ActiveSupport::TimeWithZone#in にも夏時間への対応が含まれるようになりました。
Previously calls to `in` were being sent to the non-DST aware method `Time#since` via `method_missing`. It is now…github.com
railties
config/initializers/to_time_preserves_timezone.rb が追加された
#to_time の挙動を制御する設定が追加されたことに関連して、Rails が生成するファイルに config/initializers/to_time_preserves_timezone.rb が追加されました。内容は ActiveSupport.to_time_preserves_timezone を true に設定するものです。
In Ruby 2.4 the `to_time` method for both `DateTime` and `Time` will preserve the timezone of the receiver when…github.com
ActionDispatch::IntegrationTest で同時に複数のセッションを利用したときの不具合が修正された
ActionDispatch::IntegrationTest では、それまでの変更により、新しいセッションを作成するために Object#dup が利用されるようになっていました。このとき、それまで利用しているセッションが再利用されるような実装になっていたため、複数のセッションを利用しようとすると問題が起きる状態でした。
これが修正され、複数のセッションを利用しても問題が起きないようになりました。
Added the test for issue #22742 in actionpack/test/controller/integration_test.rb Updated the CHANGELOG Credited @tawangithub.com
before_configuration フックが適切なタイミングで実行されるようになった
Rails 4.2 で加えられた変更によって、before_configuration フックの実行されるタイミングに問題が発生していました。今回この問題が修正され、適切なタイミングで before_configuration フックが実行されるようになりました。