rails

7 Lesser-Known Features and Changes in Rails 7.1

Rails 7.1 lacks big show-stopping additions, but there are still many improvements to the developer experience that are worth checking out.

Rails 7.1 is finally here, nearly 2 years after 7.0 was released. My initial reaction was that – with a couple notable exceptions (strict locals in ERB views and an auto-loaded lib directory, hooray!) – most of the major features in the release notes were a bit underwhelming.

However, as I dug deeper into the 2,000+ pull requests merged since Rails 7.0, I realized that there are several interesting features and changes, many of which haven’t been widely promoted. Here’s what caught my attention:

  1. Finally, we can write proper integration tests for errors
  2. SSL is now on by default in production
  3. A new picture_tag helper supports responsive images
  4. Development and test log files no longer grow to infinity
  5. The webdrivers gem has been removed
  6. Active Record enums can now validate like other attributes
  7. Deployments to Linux work out of the box

1. Finally, we can write proper integration tests for errors

Before Rails 7.1, if you wanted to write an integration test for an error scenario that returned a 404 status, this would not work:

test "attempting to load non-existent user ID results in 404 error" do
  get "/users/does-not-exist"
  assert_equal(404, response.status)
end
This would seem to correctly describe the behavior of the application in production. However, this test fails and the assert_equal line is never reached.

Surprisingly, the get method raises ActiveRecord::RecordNotFound. You may be tempted to set config.action_dispatch.show_exceptions = true to get this test to pass, but this disables stack traces for legitimate errors.

In Rails 7.1, there is now a clean solution:

config.action_dispatch.show_exceptions = :rescuable
config/environments/test.rb
This is a new default for Rails 7.1 apps. For apps being upgraded to Rails 7.1, you may need to add this setting to test.rb.

Now integration tests will correctly render a 404 response instead of halting with an exception. Other errors that can’t be rescued (i.e. legitimate bugs) will still raise with a stack trace.

For more details, check out this PR: Make the test environment show rescuable exceptions in responses #45867.

2. SSL is now on by default in production

It probably goes without saying that we should all be using secure cookies and HTTPS in production. Prior to Rails 7.1, you needed to remember to explicitly set config.force_ssl = true for this to take effect. Without it, your requests might technically still be traveling over HTTPS (depending on your hosting or CDN setup), but without the full security of HSTS and https-only cookies.

In Rails 7.1, force_ssl is now on by default in production. If you are deploying to a private staging or hobby environment where a full HTTPS stack is not possible, you can opt-out by setting config.force_ssl = false.

PR: Enable force_ssl=true in production by default #47852

3. A new picture_tag helper supports responsive images

When developing responsive web apps, sometimes you’ll need to deliver different images to different browsers. Here are a few scenarios:

  • For efficiency, you want to deliver a WebP version of an image, but you also need to provide a PNG version as a fallback for older browsers.
  • You want to swap between different images based on screen size or orientation.
  • You have low-res and hi-res versions of images that are appropriate for different devices based on their screen density.

The HTML <picture> tag is designed to solve for these use cases. Rails 7.1 adds nice syntactic sugar for generating them with the new picture_tag helper:

<%= picture_tag(
      "picture.webp",
      "picture.png",
      class: "mt-2",
      image: { alt: "Image", class: "responsive-img" }
    ) %>
This is an example of specifying a WebP image with PNG fallback. The picture_tag helper knows where the alt and class attributes should be placed in the generated markup.

The resulting HTML:

<picture class="mt-2">
  <source srcset="/images/picture.webp">
  <source srcset="/images/picture.png">
  <img alt="Image" class="responsive-img" src="/images/picture.png">
</picture>
The image paths are automatically resolved via the asset pipeline. Notice how a fallback <img> tag is automatically constructed based on the last image passed to the helper.

PR: Add a picture_tag helper #48100

4. Development and test log files no longer grow to infinity

Rails helpfully writes very detailed logs to log/development.log and log/test.log. But over the course of months and months of fast paced test-driven development, these files could grow very large. I’ve seen some apps with log files over 1 gigabyte. Before Rails 7.1, there was technically no upper limit.

Rails 7.1 now caps development and test log files at 100 MB. Whew!

config.log_file_size = 100 * 1_024 * 1_024 # 100 MB
This is the default setting for development and test. Production has no limit by default.

PR: Rotate Default Logs on Each 100MB #44888

5. The webdrivers gem has been removed

The webdrivers gem has long been included in the default Rails Gemfile to facilitate browser testing. If your tests use chromedriver, webdrivers would automatically detect the necessary chromedriver version and download it, so that you didn’t need to install it manually (e.g. via homebrew).

Fast-forward to 2023, and the selenium-webdriver gem has taken over this responsibility. The webdrivers gem is now deprecated.

In short, as long as you are using Ruby 3.0+, the latest version of selenium-webdriver has you covered. You can remove webdrivers from your Gemfile. In Rails 7.1, rails new no longer includes it.

PR: Omit webdrivers gem from Gemfile template #48847.

6. Active Record enums can now validate like other attributes

Prior to Rails 7.1, enum attributes always raised an exception when assigned invalid values. For programmatically-maintained fields, like an internal state machine, this made sense. However it was not a good fit for user-provided data.

class Conversation < ApplicationRecord
  enum :status, [:active, :archived]
end

conversation.status = "inactive"
# => ArgumentError, "'inactive' is not a valid status"
Assigning an invalid value raises ArgumentError by default.

In Rails 7.1, this behavior is now configurable to provide user-friendly validation errors.

class Conversation < ApplicationRecord
  enum :status, [:active, :archived], validate: true
end

conversation.status = "inactive" # no longer raises
conversation.valid? # => false
conversation.errors.full_messages
# => ["Status is not included in the list"]
Defining an enum with validate: true causes it to behave similarly to a regular attribute validated by validates_inclusion_of.

PR: Make enums validatable without raising error #49100

7. Deployments to Linux work out of the box

To make dependency resolution more predictable, recent versions of Bundler include platform information in the Gemfile.lock. This means that, for example, a Gemfile.lock created on macOS will not work on Linux until bundle install is run in a Linux environment to update the lock file and resolve any Linux-specific dependencies.

That may sound reasonable, but in Rails, it can make for a frustrating experience.

  • Many Rails developers are using macOS.
  • As a result, the initial Gemfile.lock generated when they run bundle install will include macOS platform information, but not Linux.
  • Nearly all cloud deployment and CI platforms use Linux.
  • Inevitably, the first time a deploy is attempted to Linux, it fails with a Bundler error.

Specifically, Bundler’s error message will instruct you to run this command:

bundle lock --add-platform=x86_64-linux

This is such a common obstacle that Rails 7.1 now includes its own workaround. When you generate an app using rails new, Rails 7.1 will automatically run the lock command to make sure the resulting Gemfile.lock includes Linux support. This ensures the first deploy to a Linux environment will “just work”, even if you develop on Mac.

PR: rails new: add x86_64-linux and aarch64-linux platforms by default #47492

Share this? Copy link

Feedback? Email me!

Hi! 👋 I’m Matt Brictson, a software engineer in San Francisco. This site is my excuse to practice UI design, fuss over CSS, and share my interest in open source. I blog about Rails, design patterns, and other development topics.

Recent articles

RSS
View all posts →

Open source projects

mattbrictson/nextgen

Generate your next Rails app interactively!

11
Updated 1 day ago

mattbrictson/tomo

A friendly CLI for deploying Rails apps ✨

360
Updated 1 day ago

More on GitHub →