Hey everyone—Sajjad Umar here with the February edition of Ruby on Rails Monthly!
January flew by, and the Rails community is already buzzing with new updates, fresh ideas, and exciting discussions. Whether you’re working on new projects, refining your skills, or just keeping up with the latest in Rails, I’ve got plenty to share this month. Let’s dive in!
Rails Foundation now has a new Contributing Member
Welcome Clio as the newest Contributing Member of the Rails Foundation. Since its founding in 2008, Clio has relied on Rails to build tools that empower legal professionals to manage their practices more efficiently.
Check out the announcement here.
Ruby on Rails for Agile Web Development
Added a script for using dev containers outside VSCode
This commit adds a small script that reads the devcontainer JSON file and runs docker commands based on the information in the JSON file.
Read all the details here.
Micro-Optimization in Router#find_routes
Many small optimizations were made, but they combined to reduce the overhead by half compared to the previous version.
Look at all the PRs and benchmarks here.
Speed up GTG Simulator by reducing slices/matches
This commit improves the performance of routing by 10-20% in simple cases by removing the duplication and preventing excess string allocations. The improvement is larger for routes deeper in the tree.
First, it avoids string allocations and scanning by peeking at the next byte in the StringScanner. Only if the next byte is not one of the "special characters" does the StringScanner do a scan to consume as many non "special characters" as it can. And because the two cases are now separated, the #match?
in #move
can be replaced with a boolean parameter.
Read all the details about it here.
Simplified stdparam
state to reduce retained hashes
This commit refactors the stdparam_state
to be a simple hash mapping from
to to
values:
{
13 => 24,
14 => 25,
15 => 26,
16 => 27,
17 => 28,
18 => 29,
19 => 30,
20 => 31,
...
}
In a large application, this can save thousands of retained hashes.
Read all the details here.
Allowed execution wrapper to handle all exceptions
The execution wrapper doesn't catch all errors an application can throw. If a more serious error like SystemStackError
or NoMemoryError
happens, the error reporter should be able to report these kinds of exceptions.
This Pull Request changes the execution wrapper and execution middleware to default to catching all Exception
s instead of all StandardError
s.
Essentially, before this change:
Rails.error.handle do
raise Exception
end
would not handle the exception, but now it does.
Read all the details here.
Made Value type default to mutable
This PR changes the default of Value
type's mutable?
to true
. It makes sure that all prior immutable types set mutable?
to false
via a new internal Helpers::Immutable
Read all the details here.
Pass keyword arguments to PostgreSQL Adapter Table methods
t.exclusion_constraint
and t.unique_constraint
are extensions available only for the PostgreSQL adapter. These delegate the given arguments to SchemaStatements#add_exclusion_constraint
etc., but the current implementation receives all arguments as an array, so keyword arguments cannot be passed correctly.
As a result, method calls that pass keyword arguments always result in an argument error.
This change fixes this issue by forwarding arguments using ...
.
Read all the details here.
Free growth with AdEx.world
A community-driven Ad-Exchange Platform!
Signup Now for Free Growth across the Adex Network
Made column name optional for index_exists?
Currently, when using index_exists?
the index name is derived from the column names if supplied. And if the column names aren't supplied options[:name]
is used. This means that the column argument in the index_exists?
method can be optional. Similar behavior already exists on remove_index
.
This Pull Request change allows the following invocation:
table.index_exists?(name: "index_name")
index_exists?(:table, name: "index_name")
Read all the details here.
Allowed to skip HashWithIndifferentAccess
value conversion
This PR addresses an inefficiency in ActiveModel’s
handling of attribute changes, specifically when working with HashWithIndifferentAccess
in serialized attributes.
Read all the details here.
Docker Build is now Faster
This PR removes recursive chown to speed up the Docker Build.
Check out the details and benchmarks here.
Postgresql and SQlite now Support Joins in update_all
Previously when generating update queries with one or more JOIN clauses, Active Record would use a subquery that would prevent to reference the joined tables in the SET clause, for instance:
Comment.joins(:post).update_all("title = posts.title")
This is now supported as long as the relation doesn’t also use a LIMIT, ORDER or GROUP BY clause. This was supported by the MySQL adapter for a long time.
Read all the details here.
Added Rate Limitation for password resets in auth generator
An attacker could spam the password reset form and send unlimited emails for an application.
This PR implements a matching rate_limit
to what is used for SessionController#create
.
Read all the details here.
Enabled statement-cached queries to be retryable
This commit enables queries that would be retryable without using the StatementCache to also be retryable when using it. Just like in to_sql_and_binds
, cacheable_query
now sets retryable: true
on the collector
that constructs the query and additionally copies the collector
's final retryable
value to the query object. Then, instead of accepting an allow_retry
parameter when executing the cached query, execute
now uses the query object's retryable
attribute.
Read all the details here.
Fixed routes being cleared when using reload_routes!
When using Rails.application.reload_routes! (which is public API), in certain conditions, this may clear almost all routes in the routeset.
The line of code making this happen is this one.
clear! unless @disable_clear_and_finalize
Added allow_retry
to sql.active_record
This enables identifying queries which are and are not automatically retryable on connection errors.
A few end-to-end tests were left using remote_disconnect
to ensure that reconnection is covered, but tests that are just trying to assert that certain queries will cause a reconnect can now make assertions using allow_retry
instead. (In the case of Mysql2/Trilogy, this speeds up tests by 1 second per remote_disconnect
call removed)
Read all the details here.
Fixed migrating multiple DBs with pending migration action
This Pull Request changes two things:
The pending migration action to use the same code path as the
db:migrate
task (migrating all DBs)Introduces
ActiveRecord::Tasks::DatabaseTasks.dump_all
to unify schema bumping logic for all DBs in one place so it may be called by the migrate/dump task and pending migration action
Read all the details here.
Eliminated queries loading dumped model schema on Postgres
This Pull Request has been created because we should avoid requiring connections to the database at boot.
Commit 835eb8a (#48743) adds a comment making it clear the intention is
to avoid connections being opened/used at app boot:
For resiliency, it is critical that a Rails application should be
able to boot without depending on the database (or any other service)
being responsive.
Additionally, the follow-up commit 35a614c (#48793) is even more explicit
with the title: "Never connect to the database in
define_attribute_methods initializer".
Read all the details here.
Deprecated insert_all with unpersisted associations
#45943 changes cause an unexpected regression. Using insert_all
now wipes out prior unpersisted built associations.
This PR emits a deprecation to inform users that this behavior will change in the future and make it a hard change in Rails 8.1.
Read all the details here.
JSON serialized attributes can now return symbolized keys
This Pull Request changes the options available to the serialize
attribute in active record. Similar to the yaml:
option, you can now specify json:
with the only option right now being symbolize_names
Check the PR here.
Allowed passing in a date or time to ActiveSupport::Testing::TimeHelpers#freeze_time
This PR adds a date_or_time
positional arg to ActiveSupport::Testing::TimeHelpers#freeze_time
which defaults to Time.now
i.e. the current functionality. This simply calls #travel_to
under the hood.
Read all the details here.
Added application-name metadata to application layout
This PR adds the following metatag to app/views/layouts/application.html.erb
<meta name="application-name" content="Name of Rails Application">
The application-name
is a standard metadata name, and compliments existing app-related metatags.
Read all the details here.
Extended --minimal option
The --minimal
option allows for a basic Rails app, but recent additions to Rails were still included.
This PR extends the --minimal
option by excluding features recently added:
skip_brakeman
skip_ci
skip_docker
skip_kamal
skip_rubocop
skip_solid
skip_thruster
Read all the details here.
ActionMailbox: Added reply_to_address Mail extension
When using Action Mailer you can now specify a reply_to: email address in addition to the to: recipient.
Read all the details here.
Load the routes in the console when calling the app IRB helper
When running a console, the routes are not loaded, which results in the inability to see what route helpers are available.
This PR fixes the issue.
Fixed rotate(on_on_rotation:) and #on_rotation
MessageVerifier and MessageEncryptor were intended to accept an on_rotation callback but that wasn’t the case.
Accept on_rotation argument in find_signed
The on_rotation callback can be useful for the find_signed and find_signed! methods, which are used to locate records by signed IDs. This callback is triggered whenever the signed_id_verifier uses rotated secret keys, making it valuable for monitoring and tracking purposes.
Read all the details here.
Made the devcontainer script work with podman
This PR makes the devcontainer script optionally work with podman. It tries to detect if you have podman installed, then use it, otherwise, it tries to use docker.
Read all the details here.
Made active record instrumentation thread safe
When using Active Record async feature, the instrumentation was not thread safe.
Post.count_async
ActiveSupport::Notifications.subscribed(->(*) { }, "active_record.sql") do
Post.count
end
With the right race condition, the subscription would never fire up. This is due to temporarly overriding the instrumentation instance variable on the connection.
Read all the details here.
That is all for this month’s edition of Ruby on Rails monthly. Will be back with more updates next month. Until than ✌🏻