What’s coming to Rails 6.0?

Guy Maliar
Feb 14, 2019 · 7 min read

Rails 6.0 will be released soon and it’s packed with many features that both smaller and bigger applications will benefit from, as it feels like many improvements around speed and scalability were introduced in Rails 6.

I’ve read through the CHANGELOGs of all Rails parts (ActiveRecord, ActionPack, ActiveSupport, etc.) and picked some of the features that I found the most interesting.

Two new additions to the Rails family are Action Mailbox and Action Text that come straight out of Basecamp.

Active Record

Add basic API for connection switching to support multiple databases

Eileen M. Uchitelle has extracted out of Github’s codebase and into Rails’ this amazing feature, a native solution to switch database connections for specific models and/or specific queries.

The code is available as part of the pull request, https://github.com/rails/rails/pull/34052, it’s documented amazingly and very easy to follow through.

class AnimalsModel < ApplicationRecord
self.abstract_class = true

connects_to database: { writing: :animals_primary, reading: :animals_replica }
end

class Dog < AnimalsModel
# connected to both the animals_primary db for writing and the animals_replica for reading
end

Slow queries in jobs can be read from a replica using a simple API

ActiveRecord::Base.connected_to(database: :slow_replica) do
SlowReplicaModel.first
end

And the database.yml file is simply a little bit longer

development:
primary:
database: my_primary_db
user: root
primary_replica:
database: my_primary_db
user: ro_user
replica: true
animals:
database: my_animals_db
user: root
animals_replica
database: my_animals_db
user: ro_user
replica: true

Gannon McGibbon later added some more amazing code supporting hash and url configs in database hash of ActiveRecord::Base.connected_to which open up these possibilities

User.connected_to(database: { writing: "postgres://foo" }) do
User.create!(name: "Gannon")
end

config = { "adapter" => "sqlite3", "database" => "db/readonly.sqlite3" }
User.connected_to(database: { reading: config }) do
User.count
end

Add Relation#pick as short-hand for single-value plucks

Person.where(id: 1).pick(:name, :email_address)
# SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
# => ['David', 'david@loudthinking.com']

Add ActiveRecord::Base.create_or_find_by/! to deal with the SELECT/INSERT race condition

def create_or_find_by(attributes, &block)
transaction(requires_new: true) { create(attributes, &block) }
rescue ActiveRecord::RecordNotUnique
find_by!(attributes)
end

There are several drawbacks to this solution which are stated and I suggest reading them before using this solution.

Make the implicit order column configurable

class User < ActiveRecord::Base
self.implicit_order_column = "created_at"
end

Action Mailbox

The basics are covered at Action Mailbox Basics Rails Guide but a few cool ideas would be conversations that happen automatically in both a platform and email and are interchangeable, think about Github comments from emails, help desk emails that turn into tickets or even GDPR delete requests.

Example mailbox for adding comments to a Post by email

The documentation to set up Action Mailbox is available at https://edgeguides.rubyonrails.org/action_mailbox_basics.html, it covers the required configuration of Action Mailbox available providers.

Pay attention that Action Mailbox requires Active Job and Active Storage as part of it’s and a database table. Most of the class documentation is also available at ActionMailbox::Base class documentation, ActionMailbox::InboundEmail class documentation and working with a parsed email is done by using the Mail gem.

Action Text

The documentation is available at the Rails Guides and it’s pretty self-explanatory.

Action Pack

Introduce ActionDispatch::HostAuthorization

By default it’s set for all Rails 6 applications and allows in development the following hosts IPAddr.new(“0.0.0.0/0”), IPAddr.new(“::/0”), “localhost”] it supports arrays of RegExp, Proc, IPAddr and String or a single String in the configuration. What this means is that with Rails 6, we will need to explicitly set our domains in the environments configuration files.

More information is available at the HostAuthoriation code and HostAuthorization tests.

Purpose metadata for signed/encrypted cookies

It does so by stashing the cookie-name in the purpose field which is then signed/encrypted along with the cookie value. Then, on a server-side read, we verify the cookie-names and discard any attacked cookies.

Enable action_dispatch.use_cookies_with_metadata to use this feature, which writes cookies with the new purpose and expiry metadata embedded.

Pass along arguments to underlying GET method in #follow_redirect!

def create
# do stuff
follow_redirect!(params: { user: params[:user_id] })
end

Action View

Add allocations to template rendering instrumentation

  Rendered posts/_form.html.erb (Duration: 7.1ms | Allocations: 6004)
Rendered posts/new.html.erb within layouts/application (Duration: 8.3ms | Allocations: 6654)
Completed 200 OK in 858ms (Views: 848.4ms | ActiveRecord: 0.4ms | Allocations: 1539564)

Active Job

Added enqueue_retry.active_job, retry_stopped.active_job, and discard.active_job instrumentation hooks

It allows subscribing on specific events that happen throughout the lifecycle of requests, SQL queries and jobs and report them for example to a Prometheus instance.

With the addition of enqueue_retry.active_job , retry_stopped.active_job and discard.active_job it is easier to instrument based on jobs’ status.

Allow passing multiple exceptions to #retry_on and #discard_on

retry_on Errno::ECONNREFUSED, SocketError, Timeout::Error, attempts: 5

Active Model

Allows configurable attribute name for #has_secure_password

class User < ActiveRecord::Base
has_secure_password :recovery_password, validations: false
end
user = User.new()
user.recovery_password = "42password"
user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uX..."
user.authenticate_recovery_password('42password') # => user

Active Storage

Uploaded files assigned to a record are persisted to storage when the record is saved instead of immediately

@user.avatar = params[:avatar]

Rather when @user.save was called, the behavior now is as expected

Use the ImageProcessing gem for Active Storage variants, and deprecate the MiniMagick backend

The change is also easily configurable using the usual Rails configuration

Rails.application.config.active_storage.variant_processor = :vips

Action Cable

Merge action-cable-testing into Rails

Taken from action-cable-testing gem on Github

The ActionCable javascript package has been converted from CoffeeScript to ES2015, and we now publish the source code in the npm distribution

Action Mailer

Add MailDeliveryJob for delivering both regular and parameterized mail, deprecate using DeliveryJob and Parameterized::DeliveryJob

Action Support

Adds parallel testing to Rails

class ActiveSupport::TestCase
parallelize(workers: 2, with: :processes) # or :threads
end

or through environment variable PARALLEL_WORKERS and it’ll create the database with a numbered suffix.

More information is available in Bogdan’s What Is New In Rails 6.0

Add support for tracing constant autoloads

Create an initializer called autoload_trace.rb and add the following code

if Rails.env.development?
ActiveSupport::Dependencies.logger = Rails.logger
ActiveSupport::Dependencies.verbose = true
end

Add cpu_time, idle_time, and allocations to ActiveSupport::Notifications::Event

ActiveSupport::Notifications.subscribe('wait') do |event|
@event = event
end

ActiveSupport::Notifications.instrument('wait') do
sleep 1
end

p @event.allocations # => 7
p @event.cpu_time # => 0.256
p @event.idle_time # => 1003.2399

Zeitwerk

Given a conventional file structure, Zeitwerk loads your project’s classes and modules on demand meaning you don’t need to write require calls for your own files.

To enable it in Rails 6, simply set

config.autoloader = :zeitwerk

in your environment files.

More info here: https://github.com/fxn/zeitwerk and here: https://github.com/rails/rails/pull/35235

Thanks to Sean Handley for pointing it out and writing this content about Zeitwerk!

General

Rails 6 requires Ruby 2.5.0 or newer

Ruby Inside

Ruby articles and posts

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store