Hey guys — this is Sajjad Umar back with another edition of Ruby on Rails Monthly.
A lot to cover this time around; so let’s jump right in!
New to Rails? This is where you want to start learning it!
Rails 8 is a major milestone, and for the first time in Rails’ history, thanks to the support of the members of the Rails Foundation, we are able to support this major release with new and updated tutorials, videos, and guides to help developers learn Rails’ new default features as quickly as possible.
If you want to learn more, here’s where you should start.
As a software engineer, I am always trying to build something. But like most software developers; I am good at building stuff but not so much at making people use it. The latter involves, being active on social media, having a good network, being good at marketing, being good at sales, or having a heavy budget to run ad campaigns (which I don’t! and I believe most of us do not have).
No matter how good my products are; unless they reach the users they are useless.
So; to fix this, I’ve launched AdEx!
AdEx is a community-driven ad-exchange platform that allows you to run ad campaigns for almost anything you want to promote throughout AdEx network for FREE!
Yes, you heard it right; It is FREE! Here is how it works:
The good news is that I’ve onboarded rubyonrails.ba, qrmaker.top, and javascript.ba as an early integration partner. I’m looking for 7 more integration partners — please reach out if you are interested in early bird perks mentioned in the video (1 year of free promotion on the growing Adex Network, along with your brand being added to the landing page of https://adex.world).
So sign up as an integration partner now!
Back to the Rails updates!
Rails World 2025: Save the date — September 4 & 5, Amsterdam
Mark your calendars: Rails World 2025 will take place September 4 and 5, 2025, again in Amsterdam. The same venue, the same city, and good news — this time we will release more tickets.
Check the official announcement here.
The new Getting Started with Rails Guide is Live!
The revamped Getting Started Guide walks you through building an e-commerce site while introducing Rails fundamentals and the latest Rails 8 defaults. Learn to use:
the built-in Authentication Generator to create an admin system for managing products.
Action Mailer, Active Job, and Solid Queue to run background jobs that send email notifications to subscribers when products are back in stock.
Solid Cache to cache product information and improve performance.
Kamal 2.0 to deploy your app to production.
This tutorial is beginner-friendly yet comprehensive, designed to make Rails accessible to everyone and welcome beginners to Rails for years to come. It’s also expandable, paving the way for future tutorials and adding more features to the app.
Follow the getting started tutorial here.
Rails 8 Unpacked with Typecraft (Video Series)
Rails 8 Unpacked with Typecraft is a 9-part video series exploring Rails 8’s default features through the lens of adding features to a task management demo app.
You’ll learn:
Authentication with the new Authentication Generator.
Asset management using Propshaft.
The Solid Trifecta for caching, messaging, and job queueing.
Deployment with Kamal 2.0.
You can build your own app or follow along with the demo app by checking out the branches of the GitHub repo found under each episode.
Follow the series here.
Rails 8.0.1 is out — a new minor release
Read the changelog here.
Rails Versions 7.0.8.7, 7.1.5.1, 7.2.2.1, and 8.0.0.1 have been released!
These are security patches addressing one new issue where using the content_security_policy helper with untrusted user input could lead to a bypass of the browser Content Security Policy.
Older versions of Rails are unsupported, and users are recommended to upgrade to at least the 7.0 series, which will be receiveing security updates until April.
Read the full announcement here.
Rails is; Fast, Flexible, and Scalable
Read the case study on how Rails powers Doximity’s Healthcare Platform.
Key takeaways from the case study are:
Rails accelerates development: Doximity’s team rapidly iterates on new products and features, helping doctors save time and focus on patient care.
Rails ensures long-term stability: By maintaining and evolving their 15-year-old monolith, Doximity avoids rewrites.
Rails scales with success: As Doximity grows, Rails helps optimize the performance of 45+ applications and handle increasing traffic with ease.
Read the official case study here.
The Rails Foundation welcomes 1Password as Core member
The Rails Foundation is growing! After 2 years, we have made great strides in fulfilling our mission, but there is still more we want to accomplish. With that in mind, at a recent board meeting, the board voted in favor of expanding the number of Core members and seats on the board.
Expanding Core membership means a stronger foundation with more long-term growth and sustainability, and most importantly: more support for the Rails community.
With that news, we are happy to announce that our first new Core member is none other than 1Password.
Read the official announcement here.
ActiveJob::ConfiguredJob#perform_later now accept a block
This PR fixes the issue #53856 — The usual perform_later
method takes a block and yields the job to the block after enqueue, to allow callers to check whether the job was enqueued successfully and if not, what prevented it from doing so.
I would expect ConfiguredJob#perform_later
to behave the same way, but that one does not call the block.
Checkout the PR here.
Fixed if_exists/if_not_exists for foreign keys addition and removal
This PR addresses a few improvements with foreign keys additions/removals and if_exists/if_not_exists
:
If there is a foreign key on a custom column and
add_foreign_key
on a default column withif_not_exists
is used, an error was raised.When adding a foreign key with
if_not_exists
,:primary_key
was not considered.When removing a foreign key with
if_exists
, custom column was not being considered.
Checkout the PR here.
Fixed: Changing column nullability does not change default function
Changing a columns nullability on MySQL drops the columns default function. This shouldn’t happen!
This happens because we have to build a
MODIFY
statement with the entire column definition (e.g.,ALTER TABLE x MODIFY x VARCHAR(255) DFEAULT 'default' NOT NULL
) and we will attempt to use the previous default value if no new one was specified. However,MySQL::Column
has separate attributes for default values and functions, and the code to build the column definition did not account for that.
This PR fixes the issue. Checout the PR here.
Migrated ActiveRecord::Normalization to Active Model
The bulk of the implementation is moved to the new ActiveModel::Normalization module. Any “persistence”-related language, methods, and test coverage has been excised.
Read all the details here.
Changed ActionText::RichText#embeds
assignment to before_validation
Prior to this commit, assignment to the embeds
association happens after validation callbacks, so it isn't possible to incorporate Rich Text-related file validation.
For example, consider rejecting text/plain
files when creating Message
records:
class Message < ApplicationRecord
has_rich_text :content
validate do
if content.embeds.any? { |attachment| attachment.blob.content_type == "text/plain" }
errors.add(:content, "Cannot attach text/plain files")
end
end
end
Without this change, the content.embeds
association is empty at the time the validate
block is evaluated, since that assignment occurs in a before_save
callback on the ActionText::Content
class.
Alongside this change, the commit corrects the documentation on the has_many_attached :embeds
line. The returned collection is of ActiveStorage::Attachment
records and not ActiveStorage::Blob
records as documented.
Checkout the PR here.
SQLite3: Now use default function as default insert value
This PR change SQLite3::DatabaseStatements#default_insert_value
to return the default function if it is present.
Check the PR here.
Reset AR::Relation in bulk insert and upsert methods
This adds a call to reset
when insert_all
or upsert_all
are used on a relation object, matching the behavior of update_all
added in #41789.
Read all the details here.
Supported loading SQLite3 extensions with config/database.yml
The SQLite3Adapter is designed to pass the database config to the SQLite3::Database constructor, so configuring extensions
with filesystem paths will work without this PR like this:
development:
adapter: sqlite3
extensions:
- .sqlpkg/nalgeon/crypto/crypto.so # a filesystem path
- <%= SQLean::UUID.to_path %> # or ruby code returning a path
What this PR does, though, is allow this usage:
development:
adapter: sqlite3
extensions:
- SQLean::UUID
Read all the details here.
Rails now use _N
as a parallel tests databases suffixes
Previously, -N was used as a suffix, which caused problems for RDBMSes which do not support dashes in database names.
Check the PR here.
AR::Middleware::ShardSelector
support for granular database connection switching
This PR adds a class_name:
configuration option to ShardSelector
to override the abstract connection class used for switching.
It takes a class name instead of a class because the shard selector must be configured in the application configuration which is likely to be run before model classes have been loaded.
Read all the details here.
Added CSP nonce to preload links
This change fixes CSP resource loading issues related to preload links (in the document via <link>
tags or in the HTTP Link
header field).
When scripts or styles require a nonce to be included, browsers will refuse to preload these resources unless the nonce
attribute is set correctly.
Check the PR here.
Don’t wrap redis in ConnectionPool
if already given one for ActiveSupport::Cache::RedisCacheStore
This PR make you avoid wrapping redis in a ConnectionPool when using ActiveSupport::Cache::RedisCacheStore if the :redis option is already a ConnectionPool.
Read all the details here.
Added CSP nonce to preload links
This change fixes CSP resource loading issues related to preload links (in the document via <link>
tags or in the HTTP Link
header field).
When scripts or styles require a nonce to be included, browsers will refuse to preload these resources unless the nonce
attribute is set correctly.
Read all the details here.
Fix asking for DB password when running kamal dbc
kamal dbc
's bin/rails dbconsole
works as is for sqlite3 because it doesn't need a password. But for database servers like Postgres, which are typically deployed with passwords in production, you are prompted to enter a password:
Password for user myapp1:
This PR use bin/rails dbconsole --include-password
to reuse the password from database.yml.
Read the PR here.
Changed the WEB_CONCURRENCY deploy comment default to auto
puma/puma#3439 introduced a new WEB_CONCURRENCY=auto
setting for puma. The config sets puma's worker count to the count of available cpus.
This PR adds the new option to the explaining comments of a new project’s default puma.rb
config file.
Read all the details here.
Raise a more specific error when the job class can’t be instantiated
This PR adds a new exception to raise that inherits from NameError
for compatibility.
Read all the details here.
Improved error highlighting of multiline methods in ERB templates
This pull request improves the error highlighting of multi-line methods in ERB templates.
Read all the details here.
Don’t enable YJIT in development and test environments
This Pull Request has been created to suggest a better default for newly created Rails apps.
Read all the details here.
Added ActiveSupport::Testing::NotificationAssertions test helper module
This Pull Request has been created because it’s currently cumbersome to test that a certain code block/action triggers an ActiveSupport::Notifications::Event
to be emitted. It would be ideal to have some helpers to assert
against such event emission.
This Pull Request adds a new test helpers module called ActiveSupport::Testing::NotificationAssertions
.
Check the PR here.
Preserve timezone and locale in ActiveJob exception handlers
This PR fixes a bug where the job locale and timezone were wrong inside the rescue_from
block.
Check the PR here.
Added ActionDispatch::Request::Session#store
method to conform Rack spec
Rack specification states that a hash-like object stored in environment with rack.session
key MUST implement store/2
method with []=
semantics.
This PR adds the store method.
Check the PR here.
Added affected_rows to sql.active_record
This commit adds the number of affected_rows
to the sql.active_record
Notification so that these kinds of queries can be identified as well.
Read all the details here.
Allowed hidden_field(_tag) to accept a custom autocomplete value
#43280 introduced an enforced autocomplete="off"
to all hidden inputs generated by Rails to fix a firefox bug.
Unfortunately, it’s also a legitimate use case to specify autocomplete
with a value such as username
and a value on a hidden input. This hints to the browser that (in this example) the username of a password reset form is what we've provided as the value and the password manager can store it as such.
This commit only sets autocomplete="off"
if another autocomplete
value isn't provided.
Read all the details here.
Allowed to reset cache counters for multiple records
There is often a need to reset counter caches for multiple records. Achieving this before will generate many extra queries.
Before
[1,2,...,10].each do |id|
Aircraft.reset_counters(id, :wheels, :engines)
end
reset_counters
creates 4 queries:
to get a record by id
to calculate a counter for
:wheels
to calculate a counter for
:engines
to update the record
So, to update 10 records, this will create 10x4=40 queries.
After
Aircraft.reset_counters([1,2,...,10], :wheels, :engines)
1 query to get :wheels
counts for all records
1 query to get :engines
counts for all records
1 query for each record to update counters
Total: 12 queries.
Read more details here.
Don’t parse response content type if value comes from Mime
Anytime ActionDispatch::HTTP::Reponse#content_type=
is called, the content_type
argument is parsed using the CONTENT_TYPE_PARSER regexp. If the content_type
argument comes from Mime
, there is no need to parse it using the regexp as we already know the result.
# Before
response.content_type = "text/html"
# After
response.content_type = :html
Read all the details here.
Made column_definitions queries retryable
This commit adds allow_retry: true
to column_definitions
to prevent needing to verify connections when executing these queries. query
can not be used (without a larger refactor) because it returns rows from the ActiveRecord::Result
instead of the Result
itself.
Read all the details here.
Parallel tests with :number_of_processors uses cgroups-aware usable processor count
When using parallel tests the default will now try to allocate a worker pool based on the total number of processors available to the system.
Read all the details here.
Redesigned ActionView::Template::Handlers::ERB.find_offset to handle edge cases
The code for finding offsets in the ERB templates outright fails in some cases, causing 500s in the error template. In other cases, it will bail out early when it’s possible to find and highlight correctly.
This PR fixes many of the issues that regularly occur and correctly highlights more often. Also adds test coverage of the translate_location method on ActionView::Template, many of which failed before this fix.
Check this PR for more info.
Fixed Mysql2Adapter support for prepared statements
Fix: #53673
Performing a query with prepared statements using the Mysql2 adapter would result in NoMethodError
.
This PR fixes the issue.
Silenced deprecate message during app:udpate
command
When users run this command, they don’t have a new_framework_defaults
yet. So they may not know about new deprecations and messages would be just noise. This PR silences those messages to avoid confusion.
Check the PR here.
Reword the error message for NoDatabaseError
This patch adds some suggestions on what to do when you run into this error.
Read the details here.
Added back the quiet assets in development
This PR adds back the quiet assets in development:
This was removed in #0f43fed. This option is supported by propshaft.
Check the PR here.
Allowed path regexp in SilenceRequest middleware
This change allows more than a single route to be silenced using a regexp pattern.
Check the PR here.
Ensured normalized attribute queries are consistent for nil
and normalized nil
This change ensures that normalized attribute queries are consistent for nil and normalized nil. Ex:
class Aircraft < ApplicationRecord
normalizes :name, with: -> name { name.presence&.titlecase }
end
# With this change, these queries are now consistent:
Aircraft.where(name: nil).to_sql === Aircraft.where(name: "").to_sql
Check the PR here.
Fixed regression when calling sum with a grouped calculation
Calling User.group(:name).sum
no longer works. It raises a TypeError: no implicit conversion of Integer into String
.
This is because we no longer typecast the field to a string before trying to match the Arel column. Sum
accepts integer (defaults to 0) as argument.
This PR fixes the issue.
Don’t add bin/thrust
if thruster
is not in the Gemfile
Running app:update
after upgrading to Rails 8.0.0 results in bin/thrust
being added even though thruster
is not in the Gemfile.
This fixes it by checking if thruster
is in the Gemfile to set the --skip-thruster
option when running the generator in the app:update
command.
Read all the details here.
That is it for this month. I will be back with more updates next month. Until than ✌🏻
Read this newsletter on:
LinkedIn: https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=6923566487630675968
Medium: https://medium.com/@sajjadumar
Substack:
Follow me on LinkedIn https://www.linkedin.com/in/sumar7