[FULL] Ruby on Rails - November 2023
Sajjad Umar
Senior Backend Engineer | Ruby on Rails | Author of Ruby on Rails for Agile Web Development | Manager RubyConfPk | Desi Developer | Building adex.world
Greetings everyone! Welcome to the November 2023 edition of Ruby on Rails Monthly, we have a lot to discuss this month but before I jump into that I want to introduce you to something I’m working on solo.
I call it “Adex” a community-driven cross-promotion platform. I am building this project in public, if you are interested in following the journey consider following me on X (fka Twitter) where I share about all my ongoing projects.
If you are interested in learning what Adex is? you can find all the details here: https://adex.world
Now let’s jump straight into the updates…
Rails 7.1.2 has been released!
Rails 7.1.2 has been released, you can view the entire CHANGELOG here.
Read all the details here.
“Ruby on Rails the Documentary” is out
Ruby on Rails has one of the most faithful communities online, it also has one of the most controversial, rabble-rousing creators out there, Danish programmer, David Heinemeier Hansson. Widely known as DHH, David tells us how Rails went from a crazy idea to one of the most talked-about full-stack frameworks over the course of 20 years.
View the entire documentary here.
Rails World Recap by Amanda Perino
Amanda Perino wrote a comprehensive recap of the RailsWorld.
Read it here.
YJIT is not enabled by default on Ruby 3.3+ apps
This change was made because there were many public reports of 15-25% latency improvements for Rails apps that did enable Ruby 3.2 YJIT, and in 3.3 it's even better.
Read all the details here.
Fixed behavior of proc_for_binds in Arel::Nodes::HomogenousIn
The commit resolves unintended attribute casting issues introduced in PR #41068 by replacing the type with ActiveModel::Type.default_value, ensuring proper query functionality and eliminating the need for additional fixes. This correction also clarifies the testing process for proc_for_binds, preventing false positives and improving code integrity.
Read all the details here.
Fixed: Don't mark Float::INFINITY as changed if it didn't
When saving a record with a float infinite value, it shouldn't be marked as changed. This can be annoying when using with papertrail or any other auditing system, since it creates a new version each time you save the record without real changes.
record = MyRecord.create!(float_field: Float::INFINITY)
record.reload
record.float_field = Float::INFINITY
record.changed? # returns true when it should be false
This PR fixes the issue.
Read all the details here.
The output of ActiveRecord::Core#inspect is now configurable
ActiveRecord::Core#inspect was taking >9s to run for some large objects in production. This is because the parameter filter is executed for every attribute.
This Pull Request introduces the configuration for ActiveRecord::Core#inspect. By default, inspect just returns the object with its id (eg #<Post id: 1>. The attributes to include can be configured with Post.attributes_to_inspect=. If you want to full output with all the attributes you can call Post.full_inspect.
Read all the details here.
Ported type_for_attribute to Active Model
This moves type_for_attribute from ActiveRecord::ModelSchema::ClassMethods to ActiveModel::AttributeRegistration::ClassMethods, where attribute_types is also defined.
Read all the details here.
Ported BeforeTypeCast to Active Model
This commit ports ActiveRecord::AttributeMethods::BeforeTypeCast to ActiveModel::BeforeTypeCast and includes it in ActiveModel::Attributes. Thus, classes that include ActiveModel::Attributes will now automatically define methods such as *_before_type_cast, just as Active Record models do.
The ActiveRecord::AttributeMethods::BeforeTypeCast module is kept for backward compatibility, but it now merely includes ActiveModel::BeforeTypeCast.
Read all the details here.
Preloaded Selenium driver_path before parallelizing system tests
When the webdrivers gem is not present (which is the default scenario in Rails 7.1+), the Selenium driver_path starts out as nil. This means the driver is located lazily, and deferred until a system test is run.
If parallel testing is used, this leads to a race condition, where each worker process tries to resolve the driver simultaneously. The result is an error as described in #49906.
This commit fixes the race condition by changing the implementation of Browser#preload.
Read all the details here.
Fixed using actiontext.js in sprocket
actiontext.js is compiled as an ESM bundle instead of a UMD bundle. This leads to issues when trying to use ActionText with sprockets because the ESM bundle declares variables like they are scoped to the file but sprockets will see them as scoped globally. This is a problem, in particular, if you want to mix actiontext with turbo-rails.
This PR retains app/assets/javascripts/actiontext.js as a UMD package for backward compatibility with bundling in the asset pipeline, but also adds app/assets/javascripts/actiontext.esm.js for use with ESM via importmap in the browser.
Read all the details here.
Fixed using trix in sprockets
When Trix was updated from 1.3.1 to 2.0.4, the ESM bundle of 2.0.4 was used instead of the UMD bundle (the vendored 1.3.1 file used the UMD bundle). This leads to issues when trying to use Trix with sprockets because the ESM bundle declares variables like they are scoped to the file but sprockets will see them as scoped globally.
This commit fixes the issue by replacing the Trix ESM bundle with the UMD bundle (and upgrading it from 2.0.4 to 2.0.7). Additionally, a Rake task has been added similar to one previously added to the guides for automatic vendoring using Importmap::Packager.
Read all the details here.
Fixes in ActionMailer
This PR fixes two issues with the mailer preview:
Read all the details here.
ActionMailer Email Preview now shows the date header when present
ActionMailer Email Preview - show date header when present or fallback to Time.current.rfc2822
Read all the details here.
Only show SMTP the envelope recipient when relevant
The SMTP envelope recipient usually reflects the To, Cc, and Bcc email headers. The mailer preview only shows SMTP the envelope recipient (SMTP-To) if it differs from To, but it does not take Cc and Bcc into account.
This Pull Request extends the check for SMTP-To to cover also Cc and Bcc, so it is only displayed in the rare event that the envelope recipient has been manually overridden.
Also, show the list of email addresses without array brackets and quotes.
Read all the details here.
Allowed accepting service as a proc in has_one_attached and has_many_attached
This Pull Request changes has_one_attached and has_many_attached logic where:
Read all the details here.
Do not memoize auto/eager load paths in engines
This pull request addresses a 13-year-old issue related to manipulating auto/eager load paths in a Rails engine. Previously, when adjusting the paths as demonstrated below:
config.eager_load_paths << "#{Rails.root}/extras"
config.paths["app/helpers"] << "#{Rails.root}/custom/helpers"
The "custom/helpers" directory wouldn't be included in the autoload and eager load paths. With the changes in Rails 7.1, where the lib directory is added to config.eager_load_paths for new applications, using this in conjunction with modifications to config.paths could expose a latent bug. This scenario, although possible, may not have been encountered frequently in practice until now.
Read all the details here.
Moved quote_string from trilogy and mysql2 adapters to abstract mysql adapter
The method implementations were identical for both the trilogy and mysql2 adapters, prompting a logical decision to consolidate them within the inherited parent class. However, it's important to note that if you anticipated finding this method defined specifically on the Mysql2Adapter, it has been relocated without any accompanying deprecation warning or mention in the changelog at the time of writing this.
Read all the details here.
Fixed capture view helper for HAML and Slim
This pull request addresses an issue where capturing a blank string in HAML or Slim (and potentially other template engines) results in the entire buffer being returned. The proposed changes aim to rectify this behavior.
Read all the details here.
Raise on foreign_key: being passed as an array in associations
Associations have never allowed nor supported foreign_key option being passed as an Array. This still holds true for Rails 7.1 However with Rails 7.1 supporting composite primary keys it may become more common for applications to mistakenly pass an array to foreign_key:. This commit adds an exception to raise when foreign_key: is passed as an array.
Read all the details here.
Optimized ActiveSupport::Callbacks to use less memory
Previously, callbacks that were shared with subclasses would not share between them the procs generated in Filters::Before and Filters::After. This was also lazily generated so would cause memory growth after an application is booted.
This PR fixes the issues by changing before and after callbacks (but not around!) to be shared between all subclasses of where it was defined.
Read all the details here.
Fixed rails-ujs auto start() in bundled environments
An issue was recently raised that users were seeing an error when upgrading @rails/ujs to 7.1.0+:
Uncaught Error: rails-ujs has already been loaded!
This PR fixes the issue.
Read all the details here.
ActiveRecord::Persistence.delete method now ignores invalid PRIMARY KEY values
When some invalid primary key values are passed to ActiveRecord::Persistence.delete method, ActiveRecord still performs a DELETE query even if it is not needed. For example:
irb(main):001> User.delete([])
User Delete All (0.6ms) DELETE FROM "users" WHERE 1=0
=> 0
This PR fixes the issue by ignoring the invalid primary key values in the delete method.
Read all the details here.
Re-Added Support for non-column-backed attributes for enum
This re-adds support for using enum with non-column-backed attributes while still guarding against typos in enum attribute names. When using enum with a non-column-backed attribute, the attribute must be previously declared with an explicit type. For example:
class Post < ActiveRecord::Base
attribute :topic, :string
enum topic: %i[science tech engineering math]
end
Read all the details here.
Fixed StrongParameters#extract_value to include blank values
Fixes the following issue:
Generated URLs should be handled correctly even if the composite primary key is partially empty. It might be a design choice to not support empty keys but the URL helpers should at least warn about the invalid URLs.
Read all the details here.
NumberToHumanSizeConverter can now handle negative numbers
# Old behaviour
helper.number_to_human_size(-1234567)
# => "-1234567 Bytes"
# New behaviour
helper.number_to_human_size(-1234567)
# => "-1.18 MB"
Read all the details here.
Fixed casting PG money with comma as radix point
This PR Fixes casting PG money with a comma as a radix point for example, when casting "3,50" works as expected instead of raising an error.
Read all the details here.
Added set_constraints helper for PostgreSQL
The docs already talk about how to set up deferrable constraints, but then rely on the user crafting custom SQL to use the feature. The helpers make it easier to handle juggling multiple specific constraints and quoting issues.
Read all the details here.
Dump schema only for a specific DB for rollback/up/down tasks for multiple DBs
Currently, rails db:migrate:primary (and rails db:up:primary, rails db:down:primary) dumps schema files for all the databases, even though this is not necessary.
Read all the details here.
Improved error messages of assert_changes and assert_no_changes
The error messages for assert_changes and assert_no_changes now display objects utilizing .inspect, simplifying the distinction between nil and empty strings, strings versus symbols, and so on.
Read all the details here.
Added BroadcastLogger#deep_dup
This Pull Request adds a ActiveSupport::BroadcastLogger#deep_dup. Calling deep_dup dups the broadcast logger, and then iterates through each of its broadcasts and duplicates them.
Read all the details here.
Added Support for RETURNING clause for MariaDB
This pull request introduces support for the INSERT ... RETURNING expression in the MariaDB adapter. For further details about this feature, refer to the official MariaDB documentation.
Read all the details here.
Simplified attr_internal_define
The ‘@’ prefix is always stripped, so this PR makes it optional
For backward compatibility reasons this PR still handles the prefix for now, but now it gets eagerly stripped and emits a deprecation.
Read all the details here.
The SQLite3 adapter now implements the supports_deferrable_constraints? contract
Implementing the full supports_deferrable_constraints? contract allows foreign keys to be deferred by adding the :deferrable key to the foreign_key options in the add_reference and add_foreign_key methods.
add_reference :person, :alias, foreign_key: { deferrable: :deferred }
add_reference :alias, :person, foreign_key: { deferrable: :deferred }
This PR adds full support for the SQLite3Adapter by implementing:
ActiveRecord::ConnectionAdapters::SQLite3::SchemaCreation#visit_AddForeignKey
ActiveRecord::ConnectionAdapters::SQLite3::SchemaCreation#visit_ForeignKeyDefinition
ActiveRecord::ConnectionAdapters::SQLite3Adapter#supports_deferrable_constraints?
ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements#assert_valid_deferrable
and altering:
ActiveRecord::ConnectionAdapters::SQLite3::SchemaStatements#add_foreign_key
ActiveRecord::ConnectionAdapters::SQLite3Adapter#foreign_keys
Read all the details here.
Non-zero exit status on migration file creation errors
Now, when there's an error during the execution of bin/rails generate migration, it will result in a non-zero exit status being returned.
Read all the details here.
Dockerfile templates are now compatible with Kubernetes rootless pods
Dockerfile generated by rails new sets the default user and group by name instead of UID:GID.
When run in a Kubernetes pod with runAsNonRoot: true security context:
apiVersion: v1
kind: Pod
metadata:
name: rails-rootless
labels:
name: rails-rootless
namespace: default
spec:
containers:
- name: rails-rootless
image: rails-rootless
securityContext:
runAsNonRoot: true
This Pull Request fixes the issue by using numeric UID:GID, optionally customizable at build time with RAILS_UID and RAILS_GID build args. Default values, 1000:1000, are set to keep current behavior intact.
Read all the details here.
That is it for this month's edition of Ruby on Rails Monthly, share this newsletter with your friends to get referral rewards.
Until we meet again ??