FullStack Labs

Please Upgrade Your Browser.

Unfortunately, Internet Explorer is an outdated browser and we do not currently support it. To have the best browsing experience, please upgrade to Microsoft Edge, Google Chrome or Safari.
Upgrade

Evaluation of New Ruby on Rails Features

Written by 
Pablo Cordero
,
Software Architect
Evaluation of New Ruby on Rails Features
blog post background
Recent Posts
Google AMP vs. Other Frameworks - When to Use Each
How to Create a Simple File-Transfer WebRTC React Web Application
Using Split Flags with React to Control Feature Deployments

The release of Rails 7 took place on April 12th, 2021. Rafael França, Rails Core Team member, had confirmed the release was being targeted for the previous Railsconf.

Table of contents

By far the major feature included in Rails 7 is making Hotwire the default approach for building modern and dynamic web applications without having to write much JavaScript.

Hotwire is a collection of components and techniques that use HTML instead of JSON over the wire, as it is usually done in traditional single-page applications.

There are also important additions to ActiveRecord, continuing the valuable work that Eileen M. Uchitelle and the Github team have extracted into the Rails core. Those include, for example, the ability to configure the thread pool for async queries and improvements to the way ActiveRecord connects to multiple database applications that use a custom primary abstract class.

Top 6 noteworthy Rails 7 features for our day-to-day work

1. Add benchmark method that can be called from anywhere

One convenient method I usually use for debugging purposes is the `benchmark` method that is available in views and controllers through ActiveSupport. It simply measures and logs the speed of the code provided in a block.

This small PR allows for using the benchmark method anywhere in your code, such as on your models or services. Anywhere in our code, we can now call:

	
Rails.benchmark("Importing CSV file") do
  lengthy_import_process  
end
	

Which will print the time taken to run lengthy_import_process.

	
Importing CSV file (12300.3ms)
	

2.  Type cast enum values by the original attribute type.

Before Rails 7 and on MySQL, if you had an enum on your model, like this:

	
class Post < ActiveRecord::Base
  enum :status, { pending: 0, published: 1, deleted: 2 }
end
	

If you were to query by status but used an unknown label or had a typo in the value, the find method would wrongly match 0:

	
Post.find_by(status: :publishd)
# => #
	

Moreover, the behavior was not consistent between adapters: with MySQL, for instance, it would match 0, while for PostgreSQL, it would raise an exception, and for Sqlite3, it would return nil.

After Rails 7, all adapters are returning nil in all such cases.

3. Additions to `strict_loading`

Rails 6.1 added the strict_loading mode to prevent lazy loading of associations and requires associated records to be eager loaded. It’s a powerful tool to find places where additional queries could be avoided by preloading an association. It’s also a great complement or even replacement for tools such as the Bullet gem, which has been my go-to gem to assist in identifying N+1 queries.
Rails 7 introduces some improvements and convenient settings to configure the strict loading mode.
First, it introduces a mode argument that can be used to enable strict loading for a single record. Until now, enabling strict loading for a single record would raise an error on any lazily loaded association. We can now use n_plus_one_only mode to allow lazily loading associations that are fetched through a single query:

	
User = User.first
user.strict_loading!(mode: :n_plus_one_only)

user.posts # does not raise an error since posts are fetched through a single query
user.posts.last.body # raises StrictLoadingViolationError since posts has not been eager loaded
	

Second, Rails 7 allows us to opt-out of strict-loading mode on a per-record basis when strict loading has been enabled application-wide or on a model level:

	
Class User < ApplicationRecord
  has_many :posts
end

User = User.first
user.posts # => ActiveRecord::StrictLoadingViolationError if strict loading has been set application wide

user.strict_loading!(false) # opting out of strict loading mode
User.posts # => #
	

4. Support for file and content streaming

Rails 7 adds methods to support file streaming from controllers, and also adds convenience methods to facilitate streaming from ActiveStorage.
Before, you could stream files and on-the-fly generate data through different methods such as manipulating the response headers or through the render method with the :text option, but this commit extracts the existing functionality into a convenient method to be used on any controller:

	
send_stream(filename: "posts.csv") do |stream|
  stream.writeln "title, author, published_at"

  @posts.find_each do |post|
    stream.writeln [ post.tile, post.author.name, post.published_at ].join(",")
  end
end
	

At the same time ActiveStorage::Streaming has been extracted and leverages the commit above to allow for streaming a blob from cloud storage:

	
class PostsAttachmentsController < ApplicationController
  include ActiveStorage::SetBlob, ActiveStorage::Streaming
  
  def show
    send_blob_stream @post.header_image, disposition: params[:disposition]
  end
end
	

5. Encryption of ActiveRecord attributes

One of the most exciting pieces of news for Rails 7 is the support for attribute encryption. They are regular ActiveRecord attributes that Rails transparently encrypts upon saving and decrypts upon retrieving their values.
You first need to generate a key set to use for encryption and update your Rails credentials by running bin/rails db:encryption:init.
You can then declare any ActiveRecord attributes backed by a column with the same name to be encrypted, as long as they are serializable as strings.

	
Class User < ApplicationRecord
  encrypts :email_address, deterministic: true, downcase: true
end
	

There are many options and features supported by the new encrypted attributes, such as deterministic vs. non-deterministic encryption, advanced key management strategies such as custom key providers or key rotation, and an easy-to-use API to gain more control over this feature.

6. New ActiveRecord methods

Finally, ActiveRecord has been extended with several convenience methods that add new functionality and improve the existing API.
The new ActiveRecord::Relation#load_async method, for example, allows for scheduling a query to be performed asynchronously from a thread pool. You can, say, run independent queries in a controller asynchronously:

	
Class DashboardsController < ApplicationController
  def show
    @users = User.active.load_async
    @recent_posts = Post.recent.load_async
    @pending_posts = Post.pending.load_async
  end
end
	

ActiveRecord::Relation#excluding has been extracted as a convenience method for excluding the specified record or collection of records from the resulting relation. What you would have done before as Post.recent.where.not(id: current_post,id) can be now expressed as Post.recent.excluding(current_post).

Two new methods have been added to FinderMethods : FinderMethods#sole and FinderMethods#find_sole_by. These serve two purposes, as they are to be used when you expect a single row, but also want to assert that only that row matches the condition. If the condition is not met, or it is met by multiple rows, it will throw an error:

	
Post.promoted.sole
Post.find_sole_by(promoted: true)

# => ActiveRecord::RecordNotFound      (if no Post with promoted == true)
# => #                       (if only one Post with promoted == true)
# => ActiveRecord::SoleRecordExceeded  (if more than one Post with promoted == true)
	

A few methods have also been added to conveniently work with associations: build_association and create_association have been added as new constructors on has_one :through associations.

	
class Employee < ApplicationRecord
 has_one :contract
 has_one :project, through: :contract
end

@employee.build_project
	

You can also check for the presence or lack of an association with the new where.associated and where.missing methods:

	
Employee.where.associated(:user)  # Employees with a user entry

Employee.where.missing(:user) # Employees without a user entry
	

Wrapping it up

The greatest feature to be released as part of Rails 7 is without a doubt Hotwire since its release as a standalone package has drawn attention not only in the Rails community but also on the ecosystems of other languages.
In this blog post, I highlighted a few interesting features that developers can use frequently in their workflow. Some of these might seem like small additions to the framework, but they illustrate how Rails and the Rails community are always focused on providing the most convenient APIs and build upon the joy that is Ruby as a language.
I suggest you check out the Rails 7 Release notes and Changelogs as these are only a few of the many new features to be released. Also, check this article about Ruby on Rails developers’ salaries. If you’re a developer interested in new challenges, I invite you to visit our careers page.

Pablo Cordero
Written by
Pablo Cordero
Pablo Cordero

Ever since I remember I was drawn to technology and computers. I have always found building new things, finding solutions to complex problems, learning new things and challenging myself in the process to be both very powerful and rewarding. Over the years programming has allowed me to work on many different domains, and allowed me to contribute to building things that are useful to many other people, which is for me the most gratifying aspect of software development.

My favorite development framework is Ruby on Rails. I discovered it in it's very early versions and my passion for it has only increased since. Not only do I find Ruby to be a beautiful language, but along with Rails I find it the most convenient for developing for the Web, as it assists in maintaining high quality code and enables easy collaboration within teams.

At FullStack Labs I have been very fortunate to not only be involved in a challenging project used by thousands of people around the world, but also to be part of a welcoming and supportive team that always goes above and beyond their work.

People having a meeting on a glass room.
Join Our Team
We are looking for developers committed to writing the best code and deploying flawless apps in a small team setting.
view careers
Desktop screens shown as slices from a top angle.
Case Studies
It's not only about results, it's also about how we helped our clients get there and achieve their goals.
view case studies
Phone with an app screen on it.
Our Playbook
Our step-by-step process for designing, developing, and maintaining exceptional custom software solutions.
VIEW OUR playbook
FullStack Labs Icon

Let's Talk!

We’d love to learn more about your project.
Engagements start at $75,000.

company name
name
email
phone
Type of project
How did you hear about us?
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.