Ruby on Rails - January 2024
First edition of 2024 - The only Ruby on Rails newsletter you will ever need!
Greetings,
Happy New Year! Welcome to the first edition of the "Ruby on Rails, Monthly" newsletter for 2024! I hope your January is off to a fantastic start, filled with renewed energy and enthusiasm for all the possibilities this year holds. I'm Sajjad Umar, your DesiDeveloper, and I'm thrilled to embark on another year of keeping you informed about the latest developments in the dynamic world of Ruby on Rails.
Let's dive right in.
Rails World
Mark your calendars for the upcoming Rails World event scheduled for September 26th and 27th, 2024, in Toronto, Canada.
Rails World has announced the venue, the sponsorship options, information about the CFP, and the ticket costs for the next Rails World conference.
Read all the details here.
Ruby 3.3.0 Released
Ruby 3.3.0 has been released. Ruby 3.3 adds a new parser named Prism, uses Lrama as a parser generator, adds a new pure-Ruby JIT compiler named RJIT, and many performance improvements especially YJIT.
Read all the details here.
Whats Ahead? Rails 8 Milestones
DHH created a milestone for Rails 8 and in my opinion, some of the goals are:
Setup Kamal by default for new applications
Kamal has given us the power to easily deploy Rails applications anywhere without any commercial entanglements. This should be the default path we recommend for new people getting started with Rails, so it should be configured by default. This basically just means adding kamal to the gemfile by default and running kamal init during setup. You should be able to turn this off using --skip-kamal.
Solid Queue should be the default Active Job backend for Rails 8
Solid Queue should be the default Active Job backend for Rails 8 since it supports all our major databases and enables someone to run jobs in production without either additional dependencies (like Redis), using database-specific backends (like Good Job), or using commercially-restrained backends (like Sidekiq).
The necessary tables should be setup by default as part of rails new. The user should not have to do any configuration whatsoever to be able to deploy their application in production with Solid Queue as the job runner. We also need to figure out a standardized setup for running the worker jobs, especially as part of the default Dockerfile setup. You should be able to avoid this using
--skip-solid-queue
or just--skip-solid
.Solid Cache should be the default caching backend for Rails 8
Like with Solid Queue, Solid Cache gives us a database-agnostic backend for
Rails.cache
that works well as an out-of-the-box default in production – without any configuration needed or dependencies (like Redis) required.The tables should be setup out of the box with "
rails new
", but you should be able to avoid this using--skip-solid-cache
or just--skip-solid
.Make Propshaft the default asset pipeline in Rails 8
It's time to have Propshaft take over from our venerable Sprockets in Rails 8. Our primary approach with
#nobuild
Hotwire is a better fit for Propshaft, and it also works great with outjsbundling/cssbundling
secondary approach.We will need to thoroughly test, document, and work with popular gems/plugins to ensure compatibility. Propshaft contains a small subset of features from Sprockets, and undoubtedly some will depend on these features. If incompatibilities can't be corrected in all cases, we should make sure that
error/guard
messages are good.Add basic authentication generator
Rails now include all the key building blocks needed to do basic authentication, but many new developers are still uncertain of how to put them together, so they end up leaning on all-in-one gems that hide the mechanics. While these gems are great, and many people enjoy using them, they should not be seen as a necessity. We can teach Rails developers how to use the basic blocks by adding a basic authentication generator that essentially works as a scaffold, but for authentication.
Add a built-in benchmark tool
We should have a simple, built-in benchmark mechanism for people to evaluate different computing platforms. Whether they be cloud VMs, Raspberry Pis, or 96-core state-of-the-art owned server hardware. Think Geekbench or Speedometer. Run a big test suite, on a single machine, produce one comparable number. "Oh, that new box we got? It does 3,456 on the Rails Speedrun!!". Easily comparable numbers make for better comparisons make for better buying decisions.
Add structured logging next to developer logging
When you want to aggregate the data from your Rails application into a data analysis platform, it's a real pain trying to parse the developer-focused log lines that Rails spits out by default. In addition to these log lines, which are great in development and for manual inspection, we should add structured
json
logging on a per-request basis that can easily be digested by analytics.Both Shopify and 37signals have been using in-house structured logging for a long time, so extracting something from those battle-tested scenarios is the path we intend to go.
Add a default Language Server Protocol (LSP) for better editor/IDE support in Rails 8
@tenderlove made a very compelling argument for including an official Rails LSP in the framework at Rails World. Let's make it so.Shopify is doing work in https://github.com/Shopify/ruby-lsp-rails.
Extract Action Notifier framework for push notifications
One of the most exciting developments in web standards this year was the full adoption of Web Push by Apple. This means we now have a reliable push notification standard that works in Safari, Chrome, and Firefox, which reduces the need for native applications.
Add Airlock* to Docker setup to get HTTP/2, X-Sendfile, Caching by default in Rails 8
Puma does not support HTTP/2 out of the box, and there does not seem to be a short-term path to that changing. That means most people have to either stick nginx or a CDN in front of their app, which means more configuration and more moving parts.
As part of developing ONCE #1, we built a tiny, no-config Go-based proxy that sits in front of Puma to provide HTTP/2, public caching, and X-Sendfile setup. You run it by starting it with airlock puma, and that's it, you gain those speed upgrades without any hassle. This makes it a great fit for people who just want to run their Rails app off the default Dockerfile and with a minimum of fuss.
(The Airlock name is contested, so we'll pick another before release).
Add DB-backed Action Cable adapter as new default in Rails 8
Out of the box, Action Cable will only work with a single process setup using the in-memory adapter. If you run multiple processes, let alone multiple app servers, you have to add Redis to the mix or be running PostgreSQL. Redis is great, but it's another moving part, and PostgreSQL is also lovely, but it's only one out of our 3 supported databases. Action Cable should work with multiple processes out of the box regardless of which of the supported databases you use.
For ONCE #1, we built a tiny Active Record-based cable adapter, which works for both MySQL and SQLite. Plan is to extract this and add it in as the new default.
Read all the details about these goals and more here.
Now let’s have a look at what has changed in the rails codebase in last 30 days:
Added webp
and avif
as allowed formats for active storage to serve inline
ActiveStorage now supports webp
and avif
formats for active_storage.content_types_allowed_inline.
Read all the details here.
Fixed inclusion of url_helpers
module in concern
The dynamically generated url_helpers
module is an ActiveSupport::Concern
. Therefore, when it is included directly in another ActiveSupport::Concern
, its included block is deferred until the latter concern is itself included elsewhere. Thus, in that case, the call to base._routes
in def self.included(base)
will raise NoMethodError
because the included block will not yet have defined the _routes
method.
This commit prevents the error by first checking if base responds to _routes
.
Read all the details here.
Added ActionView
bug report template
This PR Introduces Action View bug report templates for contributors to reproduce issues with failing ActionView::TestCase instances
.
In addition to rendering ERB
with the inline:
keyword, the sample tests also include a Helpers module to demonstrate how to incorporate view helpers into the reproduction script.
Read all the details here.
Do not generate pidfile in production environments
This Pull Request has been created to not create pidfiles in production.
This makes the pidfile
instruction from the default puma.tt
template conditional. The puma files in the respective dummy / test apps have been updated for consistency. The default path for the pidfile in the server command has also been made conditional.
Read all the details here.
Consolidated bug_report_templates
and removed the gem versions
Before this modification, the Rails repository maintained two sets of bug report templates, differing only in the Rails version they utilized. With this adjustment, a single line change has consolidated them into a unified template moving forward.
Read all the details here.
Expose assert_queries and assert_no_queries assertions
To assert the expected number of queries are made, Rails internally uses assert_queries and assert_no_queries. These assertions can now be used in applications as well.
Read all the details here.
Exposed assert_queries_match
and assert_no_queries_match
assertions
The initially introduced helpers have certain limitations. For instance:
It is necessary to specify the exact number of expected queries.
Only queries related to the application are considered, while schema/transaction-related queries are consistently excluded.
Only one matcher can be specified.
While these limitations generally suffice for regular users, they can be restrictive when attempting to test more intricate behaviors, such as within a gem's tests. A notable example is the inability to test the creation of an index and a foreign key during the execution of a specific operation.
Now, this can be achieved with the following code:
assert_queries_match(/create index/i, include_schema: true) do
do_something
end
Read all the details here.
Added instrumentation for ActionController::Live#send_stream
This PR adds instrumentation for ActionController::Live#send_stream
. The event payload contains the filename, disposition, and type.
Read all the details here.
Allowed serving compressed SVG images
This PR adds image/svg+xml
to the compressible content types of ActionDispatch::Static.
Read all the details here.
Added default pwa manifest and service worker file
Having the PWA manifest and service-worker.js files under app/views/pwa means we can work on them dynamically with ERB and other techniques. They're mounted via a built-in Rails controller and explicitly revealed in the routes.rb file to ensure there are no surprises. This PR adds it.
Read all the details here.
TrilogyAdapter: ignore host if socket is set
Contrary to mysql2
, trilogy
will ignore the socket config if host is set.
This makes it impossible to configure a UNIX
domain socket connection via DATABASE_URL
, as the URL must include a host. This PR allows to configure a connection on a UNIX socket via DATABASE_URL
.
Read all the details here.
Added support for generated columns in SQLite3
adapter
SQLite
is a feature-rich database engine, and production usage is only growing. We need Rails' support to offer developers the range and scope of its features.
Both the MySQL
and PostgreSQL
adapters support virtual columns, and SQLite
itself has support generated columns since 2020.
PR #41856 introduced virtual columns to the PostgreSQLAdapter
. This is effectively a clone of that feature, with some necessary tweaks.
Read all the details here.
Arel nulls first/last implementation Mysql
This Pull Request changes the implementation of .nulls_first()
and .nulls_last()
for MySQL
databases. These methods now work as designed.
Read all the details here.
Added runner script option to disable Executor wrap
This Pull Request allows passing --skip-executor
to the Rails script runner to conditionally not add an Executor wrap to the script.
Read all the details here.
AR Affixes
Take AR affixes into account for Action Mailbox and Action Text database models
This removes the redundant hard coded table names to make the models compatible with ActiveRecord::Base.{prefix|suffix}
.
This also adds prefix/suffix in the dummy app to force the entire test suite to be agnostic to hard-coded table names.
Read all the details here and here.
Rails UJS has been deprecated since Rails 7, time to die
We will still have the official package at @rails/ujs
and also leave the final compiled targets for the asset pipeline. But everything else ought to go.
Read all the details here.
Added rate limiting to ActionController
via the Kredis limiter type
Added rate limiting API using Redis and the Kredis limiter type.
class SessionsController < ApplicationController
rate_limit to: 10, within: 3.minutes, only: :create
end
class SignupsController < ApplicationController
rate_limit to: 1000, within: 10.seconds,
by: -> { request.domain }, with: -> { redirect_to busy_controller_url, alert: "Too many signups!" }, only: :new
end
Read all the details here.
Added rubocop-rails-omakase
to new Rails applications
This setups a basic rubocop config for new rails apps using the rubocop-rails-omakase gem.
It can be skipped with the --skip-rubocop
flag.
Read all the details here.
Bumped the required Ruby version to 3.1.0
As Ruby 3.0 will reach EOL in march, this PR bumps the required Ruby version to 3.1.0.
Read all the details here.
Added allow_browser
to set minimum versions for your application
This PR makes it easy to specify which browsers you’ve supported to allow access to your application.
Read all the details here.
Added Brakeman by default to new apps
This PR makes changes to add Brakeman by default to all new apps
It can be skipped with the --skip-brakeman
flag.
Read all the details here.
Rails now create GitHub CI files by Default
Now that we have rubocop and brakeman by default, it makes sense to add a default GitHub CI workflow file for new applications. This will get especially newcomers off to a good start with automated scanning, linting, and testing. That's a natural continuation for the modern age of what we've done since the start with unit tests.
So this PR makes it to add .github/workflows/ci.yml
and .github/dependabot.yml
with good defaults. This can be avoided with --skip-ci.
Read all the details here.
Updated the default Puma configuration
Currently the default Puma thread count to 5. That's very high. This PR changes it to 3.
Read all the details here.
Yield instance to Object#with block
The introduction of the block argument means that Object#with can now accept a Symbol#to_proc as the block argument:
client.with(timeout: 5_000, &:ping)
This PR makes it possible.
Read all the details here.
Prevents unnecessary application reloads in development
This PR fixes this issue by ensuring the application reloads correctly according to Rails.autoloaders.main.dirs
, thereby preventing unnecessary reloads.
Read all the details here.
Added oven-sh/setup-bun
to GitHub CI when generating an app with bun
When generating an app with bun, GitHub CI test fails with Command install failed, ensure bun is installed.
This Pull Request adds oven-sh/setup-bun to ci.yml when generating an app with bun.
Read all the details here.
Added explain
support for methods like last, pluck and count
Currently explain doesn't work for queries using last
, pluck
or count
, as these return the actual result instead of a relation. This makes it difficult to optimize these queries.
This PR adds explain support for these methods.
Read all the details here.
Added support for :on
option in #set_callback
This PR Introduces ActiveRecord::Transactions::ClassMethods#set_callback
which behaves like ActiveSupport::Callbacks::ClassMethods#set_callback
with support for the :on
option available on #after_commit
and #after_rollback
callbacks.
Read all the details here.
Added default:
support for ActiveSupport::CurrentAttributes.attribute
This PR extends the .attribute
class method to accept a :default
option for its list of attributes:
class Current < ActiveSupport::CurrentAttributes
attribute :counter, default: 0
end
Read all the details here.
Desi Developer
Yours truly is building a community-powered cross-promotion platform called Adex, and I’m glad that quite a few people are showing interest in the idea.
If you are interested to know more about the project consider subscribing to the waiting list at https://adex.world.
And that is it for this month’s Ruby on Rails Monthly, will be back with more updates next month ✌️
Keep reading with a 7-day free trial
Subscribe to Ruby on Rails - Monthly to keep reading this post and get 7 days of free access to the full post archives.