Hi, it's Emmanuel Hayford. Let's explore this week's changes in the Rails codebase.
Make add_foreign_key(if_not_exists: true) reversible
Inverting `add_foreign_key ..., if_not_exists: true` produced `remove_foreign_key ..., if_not_exists: true`:
Make add_foreign_key(if_not_exists: true) reversible
Inverting `add_foreign_key ..., if_not_exists: true` produced `remove_foreign_key ..., if_not_exists: true`:
recorder.inverse_of(:add_foreign_key, [:articles, :authors, if_not_exists: true])
# => [:remove_foreign_key, [:articles, :authors, {if_not_exists: true}], nil]`remove_foreign_key` does not understand `:if_not_exists`, so rolling back such a migration was not the idempotent inverse of the addition. The fix translates `:if_not_exists` into `:if_exists` when inverting, exactly as `invert_add_check_constraint` already does:
# => [:remove_foreign_key, [:articles, :authors, {if_exists: true}], nil]Use the float value for :wait if passed to ActiveJob.retry_on
`retry_on` now accepts a Float for `:wait`, consistent with `ActiveJob.set(wait: 1.5)`. Previously the value was converted to an Integer, so durations like `1.5.seconds` were truncated. Passing an unsupported `:wait` type will now also raise when loading the job class rather than failing silently at runtime.
Implement ActiveSupport::ProxyLogger
The proxy logger is a logger that forwards all received logs to another logger, but has its own independent severity level. This is useful when you want some library you have no control over to use the same logger as the rest of your application, but at a different severity level because it is logging too much:
SomeLibrary.logger = ActiveSupport::ProxyLogger.new(Rails.logger, :error)
Almost all of the standard Logger interface is supported.
Deprecate Mime::SET, Mime::LOOKUP and Mime::EXTENSION_LOOKUP
These constants expose the MIME type registries directly, leaking mutable internal state. Everything they're used for is already covered by the public API, so they are deprecated in favor of:
Mime::SET -> Mime.symbols / Mime[...] / Mime::Type.lookup
Mime::LOOKUP -> Mime::Type.lookup
Mime::EXTENSION_LOOKUP -> Mime.extensions / Mime::Type.lookup_by_extension / Mime[...]Enumerating every registered extension (including synonyms) was previously only possible through `Mime::EXTENSION_LOOKUP`, so `Mime.extensions` is added as the public counterpart to `Mime.symbols` to cover that case.
Include call options in Cache#exist? instrumentation payload
The `cache_exist?.active_support` notification payload omits the options passed to the call, so keys such as `:namespace`, `:version`, and `:expires_in` are missing from it.
ActiveSupport::Notifications.subscribe("cache_exist?.active_support") do |event|
event.payload[:namespace]
# Before: nil - options not passed through
# After: "foo" - consistent with read, write, and delete
end
Rails.cache.exist?("key", namespace: "foo")The `read`, `write`, `delete`, and `fetch_hit` cache operations all include options in their instrumentation payloads. `exist?` now does too.
Report PostgreSQL default timestamp/time precision as 6
Bare PostgreSQL `timestamp` and `time` columns now use their effective microsecond precision in Active Record type metadata, matching the database's persisted precision. Previously, Active Record parsed the formatted SQL type string to extract precision; it now reads the typmod value passed through the type map directly. When `fmod == -1` (no typmod), Active Record reports precision `6`, otherwise it uses the explicit typmod precision. This keeps column metadata and type casting aligned with PostgreSQL's effective behavior for `timestamp`, `timestamptz`, `time`, and `timetz` columns.
Add Product Reviews Tutorial
The Rails guides gain a new tutorial that continues the e-commerce store series by adding product reviews. Users can write reviews with a 1-5 star rating and optional image uploads. Reviews automatically update the product's average rating, can be filtered by star value, and display a bar graph showing the percentage breakdown. The tutorial also covers admin review management and uses radio buttons for the star rating input so it works intuitively with plain CSS.
You can view the whole list of changes.
We had 26 contributors to the Rails codebase this past week!
Until next time!