Hello!

This post about news and some changes that have been done in Rails 6.0.

Table of Contents

Rails 6.0 requires Ruby 2.5.0 or newer

Pull Requests:

Parallel testing

Pull Requests:

You know that class ActiveSupport::TestCase uses minitest under the hood (Minitest::Test is the superclass of the class). To ActiveSupport::TestCase was added parallelize method. It allows you to parallelize your test suite with forked processes. Running tests in parallel reduces the time it takes your entire test suite to run.

Running test in threads is supported as well, but note that it doesn’t make your test run faster (read “Minitest Parallelization and You” since it uses Minitest::Parallel::Executor).

The default parallelization method is to fork processes using Ruby’s DRb system.

To enable parallelization with forked processes add the following to your test/test_helper.rb:

class ActiveSupport::TestCase
  parallelize(workers: 3, with: :processes)
end

The :workers option controls how many times the the process is forked or how many threads are used. The default number of workers is the actual physical core count on the machine you are on:

class ActiveSupport::TestCase
  parallelize(workers: :number_of_processors)
end

If you would like to parallelize your test suite with threads, you should set :with option to :threads:

class ActiveSupport::TestCase
  parallelize(workers: 3, with: :threads)
end

You can also use environment variable PARALLEL_WORKERS to easily change the number of workers a test run should use. This is useful for CI environments or other environments where you may need more or less workers than you do for local testing:

PARALLEL_WORKERS=6 bin/rails test

When parallelizing test with processes, Active Record automatically handles creating a database and loading the schema into the database for each process. The databases will be suffixed with the number corresponding to the worker. For example, if you have 2 workers the tests will create test_database-0 and test_database-1 databases respectively. If the number of workers passed is 1 or fewer the processes will not be forked and the tests will not be parallelized and the tests will use the original test_database database. Also, two hooks are provided, one runs when the process is forked but before the tests run, and one runs before the forked process is closed. These can be useful if your app uses multiple databases or perform other tasks that depend on the number of workers. The parallelize_setup method is called right after the process is forked. The parallelize_teardown method is called right before the process is closed:

class ActiveSupport::TestCase
  parallelize_setup do |worker|
    # code
  end

  parallelize_teardown do |worker|
    # code
  end

  parallelize(workers: 2)
end

Note that these methods are not available with the threaded parallelization.

ActiveRecord::Relation#pick as shorthand for single-value plucks

Pull Requests:

I am sure you know about the method #pluck that has been in Rails since version 3.2.0. Let’s refresh our knowledge. #pluck is a shortcut to select one or more attributes without loading a bunch of records just to grab the attributes you want:

User.pluck(:name)
# SELECT "users"."name" FROM "users"
# => ["Bogdan", "David"]

User.where(id: 1).pluck(:name, :email)
# SELECT "users"."name", "users"."email" FROM "users" WHERE "users"."id" = ?  [["id", 1]]
# => [["Bogdan", "bogdanvlviv@gmail.com"]]

User.where("1=0").pluck(:name)
# SELECT "users"."name" FROM "users" WHERE (1=0)
# => []

But sometimes you just need single value(s) from the result. To get this you can do something like:

User.limit(1).pluck(:name).first
# SELECT "users"."name" FROM "users" LIMIT ?  [["LIMIT", 1]]
# => "Bogdan"

User.where(id: 1).limit(1).pluck(:name, :email).first
# SELECT "users"."name", "users"."email" FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
# => ["Bogdan", "bogdanvlviv@gmail.com"]

Since Rails 6.0 you can use the method #pick, it is shorthand for limit(1).pluck(*column_names).first:

User.pick(:name)
# SELECT "users"."name" FROM "users" LIMIT ?  [["LIMIT", 1]]
# => "Bogdan"

User.where(id: 1).pick(:name, :email)
# SELECT "users"."name", "users"."email" FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
# => ["Bogdan", "bogdanvlviv@gmail.com"]

User.pick(Arel.sql("UPPER(name)"))
# SELECT UPPER(name) FROM "users" LIMIT ?  [["LIMIT", 1]]
# => "BOGDAN"

User.where("1=0").pick(:name)
# SELECT "users"."name" FROM "users" WHERE (1=0) LIMIT ?  [["LIMIT", 1]]
# => nil

You probably noticed that the method #pluck does not guarantee the order of the returned value(s) by any column. So note that #pick does not guarantee to return the value(s) of the first row ordered by any column either. For instance, if you want to get value(s) of the first row ordered by id column, you should apply order(:id) before pick:

User.order(:id).pick(:name)
# SELECT "users"."name" FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
# => "Bogdan"

ActiveRecord::Relation#create_or_find_by/! to lean on unique constraints

Pull Requests:

#create_or_find_by attempts to create a record with the given attributes in a table that has a unique constraint on one or several of its columns. If a row already exists with one or several of these unique constraints, the exception such an insertion would normally raise is caught and the existing record with those attributes is found using #find_by!.

This is similar to #find_or_create_by, but avoids the problem of stale reads between the SELECT and the INSERT, as that method needs to first query the table, then attempt to insert a row if none is found.

There are several drawbacks to #create_or_find_by, though:

  • The underlying table must have the relevant columns defined with unique constraints.
  • A unique constraint violation may be triggered by only one, or at least less than all, of the given attributes. This means that the subsequent #find_by! may fail to find a matching record, which will then raise an ActiveRecord::RecordNotFound exception, rather than a record with the given attributes.
  • While we avoid the race condition between SELECT -> INSERT from #find_or_create_by, we actually have another race condition between INSERT -> SELECT, which can be triggered if a DELETE between those two statements is run by another client. But for most applications, that’s a significantly less likely condition to hit.
  • It relies on exception handling to handle control flow, which may be marginally slower.

This method will return a record if all given attributes are covered by unique constraints (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted and failed due to validation errors it won’t be persisted, you get what #create returns in such situation.

#create_or_find_by! is like #create_or_find_by, but calls create! so an exception is raised if the created record is invalid.

Implementation of these methods look like:

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

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

Examples using of these methods:

User.create_or_find_by(email: "bogdanvlviv@gmail.com") do |user|
  user.name = "Bogdan"
end

User.create_or_find_by!(email: "bogdanvlviv@gmail.com") do |user|
  user.name = "Богдан"
end

Action Mailbox

Pull Requests:

Action Mailbox allows you to route incoming emails to controller-like mailboxes. You can read more about Action Mailbox in the Action Mailbox Basics guide.

Action Text

Pull Requests:

Action Text brings rich text content and editing to Rails. It includes the Trix editor that handles everything from formatting to links to quotes to lists to embedded images and galleries. The rich text content generated by the Trix editor is saved in its own RichText model that’s associated with any existing Active Record model in the application. Any embedded images (or other attachments) are automatically stored using Active Storage and associated with the included RichText model.

You can read more about Action Text in the Action Text Overview guide.

Action Cable testing

Pull Requests:

Start Rails 6.0 development!!!

Introduce custom serializers to ActiveJob arguments

Deprecate update_attributes and update_attributes!

String#strip_heredoc preserves frozenness`

String#truncate_bytes

Add support for timezones to Active Job

Add :private option to ActiveSupport’s Module#delegate

Don’t enforce UTF-8 by default

Support i18n key at translation of value in submit tag

Add “rails routes –expanded” mode

Enable select tag helper to mark prompt option as selected and/or disabled for required field

Introduce explicit rails server handler option

Add before? and after? methods to date and time classes

Disable ActionView::Template finalizers in test environment

Add AR::Base.base_class? predicate

Deprecate controller level force_ssl

Add #dig to ActionDispatch::Request::Session

Deprecate support for using HOST environment to specify server IP

Define callbacks on descendants

Introduce ActionDispatch::DebugExceptions interceptors

Add touch_all method to ActiveRecord::Relation

Part 1 Easy Multi db in Rails: Add basic rake tasks for multi db setup Fix two-level database configurations with URLs Fix structure:dump for multiple databases Allow schema/structure load for multiple databases Add multidb application test Don’t create namespaced tasks if single db application Db schema cache dump and clear multi db Part 5: Multi db improvements, Fix query cache for multiple connections Part 7: Multi db improvements, Add ability to block writes to a database Part 8: Multi db improvements, Adds basic automatic database switching to Rails Add ability to change the names of the default handlers Add ActiveRecord::Base.connected_to?

Use ImageProcessing gem for ActiveStorage variants

Merge Arel

Update ‘rails_welcome.png’ to reflect a more diverse population

Changes Rails Welcome image on localhost:3000

Loaded associations should not run a new query when size is called

has_(one/many)_attached presence validation

Allow usage of strings as locals for partial renderer

Add Enumerable#index_with

Allow to override the full_message error format

Add year_format option to date_select tag.

Allow call assert_enqueued_with and assert_enqueued_email_with with no block

Allow queue option to assert_no_enqueued_jobs

Allow configurable attribute name on #has_secure_password

Adds Rails::Command::NotesCommand and makes rake notes use it under the hood

Pass along arguments to underlying get method in follow_redirect!

Store newly-uploaded files on save rather than assignment

rails server: Allow to explicitly specify whether to output Rails’s log to stdout

Add implicit to path conversion to uploaded file

Add a foreign-key constraint to the active_storage_attachments table for blobs

Show nested exceptions on the debug view

Add cpu time, idle time, and allocations features to log subscriber events Add event object subscriptions to AS::Notifications

Bundler binstubs

Add Purpose Metadata to Cookies

33515 invert remove foreign key support “to_table”

Fix AM::Serializers::JSON#as_json method for timestamps

Add Array#extract!

Move dev:cache rake task to use Rails::Command

Allow perform_enqueued_jobs to be called without a block.

Improve Active Job test helpers

Deprecate calling private model methods from view helpers.

Add database configuration to disable advisory locks

Omit BEGIN/COMMIT statements for empty transactions

Fail more gracefully from ActiveStorage missing file exceptions

Part 2: Multi-db improvements, Refactor Active Record configurations

Part 3: Multi-db Improvements, identifying replica configurations

Emit warning for unknown inflection rule when generating model.

Make null_store the default cache store in test environment config

Add hooks to ActiveJob around retries and discards

Deprecate most methods which were never used in DatabaseLimits

Maintain html_safe? on sliced HTML safe strings

https://github.com/rails/rails/pull/33798

trace autoloads, and document hints for troubleshooting

update I18n fallbacks configuration to be compatible with i18n 1.1.0

Configuration item config.filter_parameters could also filter out sensitive values of database columns when call #inspect. Implement AR#inspect using ParameterFilter

Add #unfreeze_time to ActiveSupport::Testing::TimeHelpers

Make sure there are no duplicated nested records with create_with

Use utf8mb4 character set by default for MySQL database Bump minimum MySQL version to 5.5.8.

Skip delivery notification when perform_deliveries is false.

TaggedLogging to return a new logger instance

Include test helpers in ActionDispatch::IntegrationTest

Use faster globs for template resolving

Allow subclasses to redefine autosave callbacks for associated records.

SQLite3 adapter supports expression indexes. SQLite3: Support multiple args function for expression indexes

Configure Active Storage route prefix

PostgreSQL: prepare for pg-1.1

Don’t update counter cache unless the record is actually saved

Deprecate ActiveRecord::Result#to_hash in favor of #to_a

Add support for multi environment credentials. Restructure credentials after environment overrides.

Error when using “recyclable” cache keys with a store that does not support it

index option added for change_table migrations

Encode Content-Disposition filenames on send_data and send_file

Fix transaction reverting for migrations

Allow assert_enqueued_with/assert_performed_with methods to accept a proc for the args argument. This is useful to check if only a subset of arguments matches your expectations.

Add ActionController::Parameters#each_value

Add migrations_paths option to migration generator Add migrations_paths option to model generator Refactor migrations_path command option to database

Treat #delete_prefix, #delete_suffix and #unicode_normalize results as non-html_safe. Ensure safety of arguments for #insert, #[]= and #replace calls on html_safe Strings.

Add deprecation warning when String#first and String#last receive negative integers

Make Webpacker the default JavaScript compiler for Rails 6

Deprecate the use of LoggerSilence in favor of ActiveSupport::LoggerSilence

Rename Module#parent, Module#parents, and Module#parent_name to module_parent, module_parents, and module_parent_name

Parameterized mailers can configure delivery job Add MailDeliveryJob for unified mail delivery Move MailDeliveryJob default to 6.0 defaults

Deprecate ActionDispatch::Http::ParameterFilter in favor of ActiveSupport::ParameterFilter

Add multi-db support to rails db:migrate:status

Fix inconsistent behavior by clearing QueryCache when reloading associations

Enum raises on invalid definition values

Add allocations to template renderer subscription

Part 4: Multi db improvements, Basic API for connection switching Add support for hash and url configs to be used in connected_to

Deprecate ActiveSupport::Multibyte::Unicode#downcase/upcase/swapcase in favor of String#downcase/upcase/swapcase Deprecate ActiveSupport::Multibyte::Unicode#normalize and ActiveSuppport::Multibyte::Chars#normalize in favor of String#unicode_normalize

Include deserialized arguments in job instances returned from assert_enqueued_with and assert_performed_with

Fix duration being rounded to a full second

Use Ids instead of memory addresses when displaying references in scaffold views

Deprecate ActiveSupport::Multibyte::Chars.consumes?

Deprecate ActiveSupport::Multibyte::Unicode#pack_graphemes(array) and ActiveSuppport::Multibyte::Unicode#unpack_graphemes(string) in favor of array.flatten.pack("U*") and string.scan(/\X/).map(&:codepoints), respectively

Support default expression and expression indexes for MySQL

ActiveStorage: Don’t include an undefined X-CSRF-Token header when creating a blob record

Make ActiveStorage::Blob keys lowercase

Permit generating variants of TIFF images

Permit sending Active Storage purge and analysis jobs to separate queues Send Active Storage analysis and purge jobs to dedicated queues by default

Fix the need of #protect_against_forgery? method defined in ActionView::Base subclasses

Add ActiveModel::Errors#of_kind?

Convert ActionCable javascript to ES2015 modules with a modern build environment

Keep executions for each specific exception Support in-flight jobs stored before individual execution counters for retry_on Run exception tests for all ActiveJob adapters and fix issue with individual counters and Resque

Move all npm packages to @rails scope

Add support for endless ranges introduces in Ruby 2.6

Refactor calculating beginning_of_quarter and end_of_quarter

Allow permitted instance of ActionController::Parameters as argument of ActiveRecord::Relation#exists?

Guard Enums against definitions with blank label names

Add an :if_not_exists option to create_table

Remove asset paths from autoload_paths

Add support for UNLOGGED Postgresql tables

Arel: Implemented DB-aware NULL-safe comparison

Prevent TextHelper#word_wrap from stripping white space on the left side of long lines; Fixes #34487

ActionMailer: support overriding template name in multipart

Refs #28025 nullify *_type column on polymorphic associations on :nulify polymorphic *_type column on dependent: :nullify strategy

Add rails db:system:change command

Seed database with inline ActiveJob job adapter

Make t.timestamps with precision by default

Remove all code deprecated in Rails 5.2

All of queries should return correct result even if including large number

Fix deeply nested namespace command printing

Make t.timestamps with precision by default

MySQL: Support :size option to change text and blob size

Action Cable: move channel_name to Channel.broadcasting_for

ActionDispatch::SystemTestCase.driven_by can now be called with a block to define specific browser capabilities

[Support before_reset callback in CurrentAttributes](https://github.com/rails/rails/pull/35063

Improve performance of blank? and present? in an ActiveRecord::Base instance

Move compiled ERB to an AV::Base subclass

Fix elapsed time calculations

Add ‘Hash#deep_transform_values’, and ‘Hash#deep_transform_values!’

Add slice! method to ActiveModel::Errors

Allow all assertion helpers that have a only and except keyword to accept Procs

Bump the minimum version of PostgreSQL to 9.3

Make it possible to override the implicit order column

option to disable all scopes that ActiveRecord.enum generates

Allow using parsed_body in ActionController::TestCase

Introduce guard against DNS rebinding attacks

Add option to set parallel test worker count to the physical core count of the machine

SQLite3: Implement add_foreign_key and remove_foreign_key

Zeitwerk integration

Adding enque time tracking and logging to a job

Add delete_by/destroy_by as relation methods

Ensure update_all series cares about optimistic locking

Add negative scopes for all enum values

Add reselect method

Deprecate mismatched collation comparison for uniqueness validator

Allow truncate for SQLite3 adapter and add rails db:seed:replant, rails db:truncate_all

Add insert_all/insert_all!/upsert_all methods to ActiveRecord::Persistence, allowing bulk inserts akin to the bulk updates provided by update_all and bulk deletes by delete_all

Added Array#including, Array#excluding, Enumerable#including, Enumerable#excluding

Add locale option to #parameterize

Fall back to parent locale before falling back to the :errors namespace

Support Optimizer Hints

Add -e/--environment option to rails initializers

Fix Time#advance to work with dates before 1001-03-07

Add ActiveSupport::Notifications instrumentation of the processing of each middleware in the stack

Add Relation#annotate for SQL commenting

Update `ActiveSupport::Notifications::Instrumenter#instrument to make passing a block optional

Add config.disable_sandbox option to Rails console

Type cast falsy boolean symbols on boolean attribute as false

Add ActiveRecord::Relation#extract_associated for extracting associated record

Only clear ActionView caches in dev when files change

Add after_save_commit callback shortcut

Add rails db:prepare to migrate or setup a database

Use execute_batch2 rather than execute_batch to fix performance regression for fixture loading