Rails

Rails/ActionControllerFlashBeforeRender

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.16

-

Using flash assignment before render in Rails controllers will persist the message for too long. Check https://guides.rubyonrails.org/action_controller_overview.html#flash-now

Safety

This cop’s autocorrection is unsafe because it replaces flash by flash.now. Even though it is usually a mistake, it might be used intentionally.

Examples

# bad
class HomeController < ApplicationController
  def create
    flash[:alert] = "msg"
    render :index
  end
end

# good
class HomeController < ApplicationController
  def create
    flash.now[:alert] = "msg"
    render :index
  end
end

Rails/ActionControllerTestCase

Required Rails version: 5.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.14

-

Using ActionController::TestCase is discouraged and should be replaced by ActionDispatch::IntegrationTest. Controller tests are too close to the internals of a controller whereas integration tests mimic the browser/user.

Safety

This cop’s autocorrection is unsafe because the API of each test case class is different. Make sure to update each test of your controller test cases after changing the superclass.

Examples

# bad
class MyControllerTest < ActionController::TestCase
end

# good
class MyControllerTest < ActionDispatch::IntegrationTest
end

Configurable attributes

Name Default value Configurable values

Include

**/test/**/*.rb

Array

Rails/ActionFilter

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

Always

0.19

2.22

Enforces the consistent use of action filter methods.

The cop is configurable and can enforce the use of the older something_filter methods or the newer something_action methods.

This cop is deprecated. Because the *_filter methods were removed in Rails 4.2, and that Rails version is no longer supported by RuboCop Rails. This cop will be removed in RuboCop Rails 3.0.

Examples

EnforcedStyle: action (default)

# bad
after_filter :do_stuff
append_around_filter :do_stuff
skip_after_filter :do_stuff

# good
after_action :do_stuff
append_around_action :do_stuff
skip_after_action :do_stuff

EnforcedStyle: filter

# bad
after_action :do_stuff
append_around_action :do_stuff
skip_after_action :do_stuff

# good
after_filter :do_stuff
append_around_filter :do_stuff
skip_after_filter :do_stuff

Configurable attributes

Name Default value Configurable values

EnforcedStyle

action

action, filter

Include

app/controllers/**/*.rb, app/mailers/**/*.rb

Array

Rails/ActionOrder

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.17

-

Enforces consistent ordering of the standard Rails RESTful controller actions.

The cop is configurable and can enforce any ordering of the standard actions. All other methods are ignored. So, the actions specified in ExpectedOrder should be defined before actions not specified.

 Rails/ActionOrder:
   ExpectedOrder:
     - index
     - show
     - new
     - edit
     - create
     - update
     - destroy

Examples

# bad
def index; end
def destroy; end
def show; end

# good
def index; end
def show; end
def destroy; end

Configurable attributes

Name Default value Configurable values

ExpectedOrder

index, show, new, edit, create, update, destroy

Array

Include

app/controllers/**/*.rb

Array

Rails/ActiveRecordAliases

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

0.53

-

Checks that ActiveRecord aliases are not used. The direct method names are more clear and easier to read.

Safety

This cop is unsafe because custom update_attributes method call was changed to update but the method name remained same in the method definition.

Examples

# bad
book.update_attributes!(author: 'Alice')

# good
book.update!(author: 'Alice')

Rails/ActiveRecordCallbacksOrder

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.7

-

Checks that Active Record callbacks are declared in the order in which they will be executed.

Examples

# bad
class Person < ApplicationRecord
  after_commit :after_commit_callback
  before_validation :before_validation_callback
end

# good
class Person < ApplicationRecord
  before_validation :before_validation_callback
  after_commit :after_commit_callback
end

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/ActiveRecordOverride

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.67

2.18

Checks for overriding built-in Active Record methods instead of using callbacks.

Examples

# bad
class Book < ApplicationRecord
  def save
    self.title = title.upcase!
    super
  end
end

# good
class Book < ApplicationRecord
  before_save :upcase_title

  def upcase_title
    self.title = title.upcase!
  end
end

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Include

app/models/**/*.rb

Array

Rails/ActiveSupportAliases

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.48

-

Checks that ActiveSupport aliases to core ruby methods are not used.

Examples

# good
'some_string'.start_with?('prefix')
'some_string'.end_with?('suffix')
[1, 2, 'a'] << 'b'
[1, 2, 'a'].unshift('b')

# bad
'some_string'.starts_with?('prefix')
'some_string'.ends_with?('suffix')
[1, 2, 'a'].append('b')
[1, 2, 'a'].prepend('b')

Rails/ActiveSupportOnLoad

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.16

2.24

Checks for Rails framework classes that are patched directly instead of using Active Support load hooks. Direct patching forcibly loads the framework referenced, using hooks defers loading until it’s actually needed.

Safety

While using lazy load hooks is recommended, it changes the order in which is code is loaded and may reveal load order dependency bugs.

Examples

# bad
ActiveRecord::Base.include(MyClass)

# good
ActiveSupport.on_load(:active_record) { include MyClass }

Rails/AddColumnIndex

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.11

2.20

Checks for migrations using add_column that have an index key. add_column does not accept index, but also does not raise an error for extra keys, so it is possible to mistakenly add the key without realizing it will not actually add an index.

Examples

# bad (will not add an index)
add_column :table, :column, :integer, index: true

# good
add_column :table, :column, :integer
add_index :table, :column

Configurable attributes

Name Default value Configurable values

Include

db/**/*.rb

Array

Rails/AfterCommitOverride

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.8

-

Enforces that there is only one call to after_commit (and its aliases - after_create_commit, after_update_commit, and after_destroy_commit) with the same callback name per model.

Examples

# bad
# This won't be triggered.
after_create_commit :log_action

# This will override the callback added by
# after_create_commit.
after_update_commit :log_action

# bad
# This won't be triggered.
after_commit :log_action, on: :create
# This won't be triggered.
after_update_commit :log_action
# This will override both previous callbacks.
after_commit :log_action, on: :destroy

# good
after_save_commit :log_action

# good
after_create_commit :log_create_action
after_update_commit :log_update_action

Rails/ApplicationController

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

2.4

2.5

Checks that controllers subclass ApplicationController.

Safety

This cop’s autocorrection is unsafe because it may let the logic from ApplicationController sneak into a controller that is not purposed to inherit logic common among other controllers.

Examples

# good
class MyController < ApplicationController
  # ...
end

# bad
class MyController < ActionController::Base
  # ...
end

Rails/ApplicationJob

Required Rails version: 5.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

0.49

2.5

Checks that jobs subclass ApplicationJob with Rails 5.0.

Safety

This cop’s autocorrection is unsafe because it may let the logic from ApplicationJob sneak into a job that is not purposed to inherit logic common among other jobs.

Examples

# good
class Rails5Job < ApplicationJob
  # ...
end

# bad
class Rails4Job < ActiveJob::Base
  # ...
end

Rails/ApplicationMailer

Required Rails version: 5.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

2.4

2.5

Checks that mailers subclass ApplicationMailer with Rails 5.0.

Safety

This cop’s autocorrection is unsafe because it may let the logic from ApplicationMailer sneak into a mailer that is not purposed to inherit logic common among other mailers.

Examples

# good
class MyMailer < ApplicationMailer
  # ...
end

# bad
class MyMailer < ActionMailer::Base
  # ...
end

Rails/ApplicationRecord

Required Rails version: 5.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

0.49

2.26

Checks that models subclass ApplicationRecord with Rails 5.0.

It is a common practice to define models inside migrations in order to retain forward compatibility by avoiding loading any application code. And so migration files are excluded by default for this cop.

Safety

This cop’s autocorrection is unsafe because it may let the logic from ApplicationRecord sneak into an Active Record model that is not purposed to inherit logic common among other Active Record models.

Examples

# good
class Rails5Model < ApplicationRecord
  # ...
end

# bad
class Rails4Model < ActiveRecord::Base
  # ...
end

Configurable attributes

Name Default value Configurable values

Exclude

db/**/*.rb

Array

Rails/ArelStar

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

2.9

-

Prevents usage of "*" on an Arel::Table column reference.

Using arel_table[""] causes the outputted string to be a literal quoted asterisk (e.g. <tt>`my_model`.</tt>). This causes the database to look for a column named <tt>`</tt> (or `"") as opposed to expanding the column list as one would likely expect.

Safety

This cop’s autocorrection is unsafe because it turns a quoted into an SQL , unquoted. is a valid column name in certain databases supported by Rails, and even though it is usually a mistake, it might denote legitimate access to a column named .

Examples

# bad
MyTable.arel_table["*"]

# good
MyTable.arel_table[Arel.star]

Rails/AssertNot

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.56

-

Use assert_not instead of assert !.

Examples

# bad
assert !x

# good
assert_not x

Configurable attributes

Name Default value Configurable values

Include

**/test/**/*

Array

Rails/AttributeDefaultBlockValue

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.9

-

Looks for attribute class methods that specify a :default option which value is an array, string literal or method call without a block. It will accept all other values, such as string, symbol, integer and float literals as well as constants.

Examples

# bad
class User < ApplicationRecord
  attribute :confirmed_at, :datetime, default: Time.zone.now
end

# good
class User < ApplicationRecord
  attribute :confirmed_at, :datetime, default: -> { Time.zone.now }
end

# bad
class User < ApplicationRecord
  attribute :roles, :string, array: true, default: []
end

# good
class User < ApplicationRecord
  attribute :roles, :string, array: true, default: -> { [] }
end

# bad
class User < ApplicationRecord
  attribute :configuration, default: {}
end

# good
class User < ApplicationRecord
  attribute :configuration, default: -> { {} }
end

# good
class User < ApplicationRecord
  attribute :role, :string, default: :customer
end

# good
class User < ApplicationRecord
  attribute :activated, :boolean, default: false
end

# good
class User < ApplicationRecord
  attribute :login_count, :integer, default: 0
end

# good
class User < ApplicationRecord
  FOO = 123
  attribute :custom_attribute, :integer, default: FOO
end

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*

Array

Rails/BelongsTo

Required Rails version: 5.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.62

-

Looks for belongs_to associations where we control whether the association is required via the deprecated required option instead.

Since Rails 5, belongs_to associations are required by default and this can be controlled through the use of optional: true.

From the release notes:

belongs_to will now trigger a validation error by default if the
association is not present. You can turn this off on a
per-association basis with optional: true. Also deprecate required
option in favor of optional for belongs_to. (Pull Request)

In the case that the developer is doing required: false, we definitely want to autocorrect to optional: true.

However, without knowing whether they’ve set overridden the default value of config.active_record.belongs_to_required_by_default, we can’t say whether it’s safe to remove required: true or whether we should replace it with optional: false (or, similarly, remove a superfluous optional: false). Therefore, in the cases we’re using required: true, we’ll simply invert it to optional: false and the user can remove depending on their defaults.

Examples

# bad
class Post < ApplicationRecord
  belongs_to :blog, required: false
end

# good
class Post < ApplicationRecord
  belongs_to :blog, optional: true
end

# bad
class Post < ApplicationRecord
  belongs_to :blog, required: true
end

# good
class Post < ApplicationRecord
  belongs_to :blog, optional: false
end

Rails/Blank

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

0.48

2.10

Checks for code that can be written with simpler conditionals using Object#blank? defined by Active Support.

Interaction with Style/UnlessElse: The configuration of NotPresent will not produce an offense in the context of unless else if Style/UnlessElse is enabled. This is to prevent interference between the autocorrection of the two cops.

Safety

This cop is unsafe autocorrection, because ' '.empty? returns false, but ' '.blank? returns true. Therefore, autocorrection is not compatible if the receiver is a non-empty blank string, tab, or newline meta characters.

Examples

NilOrEmpty: true (default)

# Converts usages of `nil? || empty?` to `blank?`

# bad
foo.nil? || foo.empty?
foo == nil || foo.empty?

# good
foo.blank?

NotPresent: true (default)

# Converts usages of `!present?` to `blank?`

# bad
!foo.present?

# good
foo.blank?

UnlessPresent: true (default)

# Converts usages of `unless present?` to `if blank?`

# bad
something unless foo.present?

# good
something if foo.blank?

# bad
unless foo.present?
  something
end

# good
if foo.blank?
  something
end

# good
def blank?
  !present?
end

Configurable attributes

Name Default value Configurable values

NilOrEmpty

true

Boolean

NotPresent

true

Boolean

UnlessPresent

true

Boolean

Rails/BulkChangeTable

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.57

2.20

Checks whether alter queries are combinable. If combinable queries are detected, it suggests to you to use change_table with bulk: true instead. This option causes the migration to generate a single ALTER TABLE statement combining multiple column alterations.

The bulk option is only supported on the MySQL and the PostgreSQL (5.2 later) adapter; thus it will automatically detect an adapter from development environment in config/database.yml or the environment variable DATABASE_URL when the Database option is not set. If the adapter is not mysql2, trilogy, postgresql, or postgis, this Cop ignores offenses.

Examples

# bad
def change
  add_column :users, :name, :string, null: false
  add_column :users, :nickname, :string

  # ALTER TABLE `users` ADD `name` varchar(255) NOT NULL
  # ALTER TABLE `users` ADD `nickname` varchar(255)
end

# good
def change
  change_table :users, bulk: true do |t|
    t.string :name, null: false
    t.string :nickname
  end

  # ALTER TABLE `users` ADD `name` varchar(255) NOT NULL,
  #                     ADD `nickname` varchar(255)
end
# bad
def change
  change_table :users do |t|
    t.string :name, null: false
    t.string :nickname
  end
end

# good
def change
  change_table :users, bulk: true do |t|
    t.string :name, null: false
    t.string :nickname
  end
end

# good
# When you don't want to combine alter queries.
def change
  change_table :users, bulk: false do |t|
    t.string :name, null: false
    t.string :nickname
  end
end

Configurable attributes

Name Default value Configurable values

Database

<none>

mysql, postgresql

Include

db/**/*.rb

Array

Rails/CompactBlank

Required Rails version: 6.1
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Always (Unsafe)

2.13

-

Checks if collection can be blank-compacted with compact_blank.

Safety

It is unsafe by default because false positives may occur in the blank check of block arguments to the receiver object.

For example, [[1, 2], [3, nil]].reject { |first, second| second.blank? } and [[1, 2], [3, nil]].compact_blank are not compatible. The same is true for blank?. This will work fine when the receiver is a hash object.

And compact_blank! has different implementations for Array, Hash, and ActionController::Parameters. Array#compact_blank!, Hash#compact_blank! are equivalent to delete_if(&:blank?). If the cop makes a mistake, autocorrected code may get unexpected behavior.

Examples

# bad
collection.reject(&:blank?)
collection.reject { |_k, v| v.blank? }
collection.select(&:present?)
collection.select { |_k, v| v.present? }
collection.filter(&:present?)
collection.filter { |_k, v| v.present? }

# good
collection.compact_blank

# bad
collection.delete_if(&:blank?)            # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
collection.delete_if { |_k, v| v.blank? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
collection.keep_if(&:present?)            # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`
collection.keep_if { |_k, v| v.present? } # Same behavior as `Array#compact_blank!` and `Hash#compact_blank!`

# good
collection.compact_blank!

Rails/ContentTag

Required Rails version: 5.1
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

2.6

2.12

Checks legacy syntax usage of tag

Allow tag when the first argument is a variable because tag(name) is simpler rather than tag.public_send(name). And this cop will be renamed to something like LegacyTag in the future. (e.g. RuboCop Rails 3.0)

Examples

# bad
tag(:p)
tag(:br, class: 'classname')

# good
tag.p
tag.br(class: 'classname')
tag(name, class: 'classname')

Configurable attributes

Name Default value Configurable values

Exclude

app/models/**/*.rb, config/**/*.rb

Array

Rails/CreateTableWithTimestamps

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.52

2.20

Checks the migration for which timestamps are not included when creating a new table. In many cases, timestamps are useful information and should be added.

Allow timestamps not written when id: false because this emphasizes respecting user’s editing intentions.

Examples

# bad
create_table :users

# bad
create_table :users do |t|
  t.string :name
  t.string :email
end

# good
create_table :users do |t|
  t.string :name
  t.string :email

  t.timestamps
end

# good
create_table :users do |t|
  t.string :name
  t.string :email

  t.datetime :created_at, default: -> { 'CURRENT_TIMESTAMP' }
end

# good
create_table :users do |t|
  t.string :name
  t.string :email

  t.datetime :updated_at, default: -> { 'CURRENT_TIMESTAMP' }
end

# good
create_table :users, articles, id: false do |t|
  t.integer :user_id
  t.integer :article_id
end

Configurable attributes

Name Default value Configurable values

Include

db/**/*.rb

Array

Exclude

db/**/*_create_active_storage_tables.active_storage.rb, db/**/*_create_active_storage_variant_records.active_storage.rb

Array

Rails/DangerousColumnNames

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.21

-

Avoid dangerous column names.

Some column names are considered dangerous because they would overwrite methods already defined.

Examples

# bad
add_column :users, :save

# good
add_column :users, :saved

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Include

db/**/*.rb

Array

Rails/Date

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

0.30

2.11

Checks for the correct use of Date methods, such as Date.today, Date.current etc.

Using Date.today is dangerous, because it doesn’t know anything about Rails time zone. You must use Time.zone.today instead.

The cop also reports warnings when you are using to_time method, because it doesn’t know about Rails time zone either.

Two styles are supported for this cop. When EnforcedStyle is strict then the Date methods today, current, yesterday, and tomorrow are prohibited and the usage of both to_time and to_time_in_current_zone are reported as warning.

When EnforcedStyle is flexible then only Date.today is prohibited.

And you can set a warning for to_time with AllowToTime: false. AllowToTime is true by default to prevent false positive on DateTime object.

Safety

This cop’s autocorrection is unsafe because it may change handling time.

Examples

EnforcedStyle: flexible (default)

# bad
Date.today

# good
Time.zone.today
Time.zone.today - 1.day
Date.current
Date.yesterday
date.in_time_zone

EnforcedStyle: strict

# bad
Date.current
Date.yesterday
Date.today

# good
Time.zone.today
Time.zone.today - 1.day

AllowToTime: true (default)

# good
date.to_time

AllowToTime: false

# bad
date.to_time

Configurable attributes

Name Default value Configurable values

EnforcedStyle

flexible

strict, flexible

AllowToTime

true

Boolean

Rails/DefaultScope

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

2.7

-

Looks for uses of default_scope.

Examples

# bad
default_scope -> { where(hidden: false) }

# good
scope :published, -> { where(hidden: false) }

# bad
def self.default_scope
  where(hidden: false)
end

# good
def self.published
  where(hidden: false)
end

Rails/Delegate

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.21

0.50

Looks for delegations that could have been created automatically with the delegate method.

Safe navigation &. is ignored because Rails' allow_nil option checks not just for nil but also delegates if nil responds to the delegated method.

The EnforceForPrefixed option (defaulted to true) means that using the target object as a prefix of the method name without using the delegate method will be a violation. When set to false, this case is legal.

Examples

# bad
def bar
  foo.bar
end

# good
delegate :bar, to: :foo

# bad
def bar
  self.bar
end

# good
delegate :bar, to: :self

# good
def bar
  foo&.bar
end

# good
private
def bar
  foo.bar
end

EnforceForPrefixed: true (default)

# bad
def foo_bar
  foo.bar
end

# good
delegate :bar, to: :foo, prefix: true

EnforceForPrefixed: false

# good
def foo_bar
  foo.bar
end

# good
delegate :bar, to: :foo, prefix: true

Configurable attributes

Name Default value Configurable values

EnforceForPrefixed

true

Boolean

Rails/DelegateAllowBlank

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.44

-

Looks for delegations that pass :allow_blank as an option instead of :allow_nil. :allow_blank is not a valid option to pass to ActiveSupport#delegate.

Examples

# bad
delegate :foo, to: :bar, allow_blank: true

# good
delegate :foo, to: :bar, allow_nil: true

Rails/DeprecatedActiveModelErrorsMethods

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Always (Unsafe)

2.14

2.18

Checks direct manipulation of ActiveModel#errors as hash. These operations are deprecated in Rails 6.1 and will not work in Rails 7.

Safety

This cop is unsafe because it can report errors manipulation on non-ActiveModel, which is obviously valid. The cop has no way of knowing whether a variable is an ActiveModel or not.

Examples

# bad
user.errors[:name] << 'msg'
user.errors.messages[:name] << 'msg'

# good
user.errors.add(:name, 'msg')

# bad
user.errors[:name].clear
user.errors.messages[:name].clear

# good
user.errors.delete(:name)

# bad
user.errors.keys.include?(:attr)

# good
user.errors.attribute_names.include?(:attr)

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Rails/DotSeparatedKeys

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.15

-

Enforces the use of dot-separated locale keys instead of specifying the :scope option with an array or a single symbol in I18n translation methods. Dot-separated notation is easier to read and trace the hierarchy.

Examples

# bad
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
I18n.t :title, scope: :invitation

# good
I18n.t 'activerecord.errors.messages.record_invalid'
I18n.t :record_invalid, scope: 'activerecord.errors.messages'

Rails/DuplicateAssociation

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.14

2.18

Looks for associations that have been defined multiple times in the same file.

When an association is defined multiple times on a model, Active Record overrides the previously defined association with the new one. Because of this, this cop’s autocorrection simply keeps the last of any duplicates and discards the rest.

Examples

# bad
belongs_to :foo
belongs_to :bar
has_one :foo

# good
belongs_to :bar
has_one :foo

# bad
has_many :foo, class_name: 'Foo'
has_many :bar, class_name: 'Foo'
has_one :baz

# good
has_many :bar, class_name: 'Foo'
has_one :foo

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Rails/DuplicateScope

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.14

2.18

Checks for multiple scopes in a model that have the same where clause. This often means you copy/pasted a scope, updated the name, and forgot to change the condition.

Examples

# bad
scope :visible, -> { where(visible: true) }
scope :hidden, -> { where(visible: true) }

# good
scope :visible, -> { where(visible: true) }
scope :hidden, -> { where(visible: false) }

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Rails/DurationArithmetic

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.13

-

Checks if a duration is added to or subtracted from Time.current.

Examples

# bad
Time.current - 1.minute
Time.current + 2.days

# good - using relative would make it harder to express and read
Date.yesterday + 3.days
created_at - 1.minute
3.days - 1.hour

# good
1.minute.ago
2.days.from_now

Rails/DynamicFindBy

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Always (Unsafe)

0.44

2.10

Checks dynamic find_by_* methods. Use find_by instead of dynamic method. See. https://rails.rubystyle.guide#find_by

Safety

It is certainly unsafe when not configured properly, i.e. user-defined find_by_xxx method is not added to cop’s AllowedMethods.

Examples

# bad
User.find_by_name(name)
User.find_by_name_and_email(name)
User.find_by_email!(name)

# good
User.find_by(name: name)
User.find_by(name: name, email: email)
User.find_by!(email: email)

AllowedMethods: ['find_by_sql', 'find_by_token_for'] (default)

# bad
User.find_by_query(users_query)
User.find_by_token_for(:password_reset, token)

# good
User.find_by_sql(users_sql)
User.find_by_token_for(:password_reset, token)

AllowedReceivers: ['Gem::Specification', 'page'] (default)

# bad
Specification.find_by_name('backend').gem_dir
page.find_by_id('a_dom_id').click

# good
Gem::Specification.find_by_name('backend').gem_dir
page.find_by_id('a_dom_id').click

Configurable attributes

Name Default value Configurable values

Whitelist

find_by_sql, find_by_token_for

Array

AllowedMethods

find_by_sql, find_by_token_for

Array

AllowedReceivers

Gem::Specification, page

Array

Rails/EagerEvaluationLogMessage

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.11

-

Checks that blocks are used for interpolated strings passed to Rails.logger.debug.

By default, Rails production environments use the :info log level. At the :info log level, Rails.logger.debug statements do not result in log output. However, Ruby must eagerly evaluate interpolated string arguments passed as method arguments. Passing a block to Rails.logger.debug prevents costly evaluation of interpolated strings when no output would be produced anyway.

Examples

# bad
Rails.logger.debug "The time is #{Time.zone.now}."

# good
Rails.logger.debug { "The time is #{Time.zone.now}." }

Rails/EnumHash

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

2.3

-

Looks for enums written with array syntax.

When using array syntax, adding an element in a position other than the last causes all previous definitions to shift. Explicitly specifying the value for each key prevents this from happening.

Examples

# bad
enum :status, [:active, :archived]

# good
enum :status, { active: 0, archived: 1 }

# bad
enum status: [:active, :archived]

# good
enum status: { active: 0, archived: 1 }

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/EnumSyntax

Requires Ruby version 3.0
Required Rails version: 7.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.26

-

Looks for enums written with keyword arguments syntax.

Defining enums with keyword arguments syntax is deprecated and will be removed in Rails 8.0. Positional arguments should be used instead:

Examples

# bad
enum status: { active: 0, archived: 1 }, _prefix: true

# good
enum :status, { active: 0, archived: 1 }, prefix: true

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Include

app/models/**/*.rb

Array

Rails/EnumUniqueness

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.46

-

Looks for duplicate values in enum declarations.

Examples

# bad
enum :status, { active: 0, archived: 0 }

# good
enum :status, { active: 0, archived: 1 }

# bad
enum :status, [:active, :archived, :active]

# good
enum :status, [:active, :archived]

# bad
enum status: { active: 0, archived: 0 }

# good
enum status: { active: 0, archived: 1 }

# bad
enum status: [:active, :archived, :active]

# good
enum status: [:active, :archived]

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/EnvLocal

Required Rails version: 7.1
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.22

-

Checks for usage of Rails.env.development? || Rails.env.test? which can be replaced with Rails.env.local?, introduced in Rails 7.1.

Examples

# bad
Rails.env.development? || Rails.env.test?

# good
Rails.env.local?

Rails/EnvironmentComparison

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.52

-

Checks that Rails.env is compared using .production?-like methods instead of equality against a string or symbol.

Examples

# bad
Rails.env == 'production'

# bad, always returns false
Rails.env == :test

# good
Rails.env.production?

Rails/EnvironmentVariableAccess

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

2.10

2.24

Looks for direct access to environment variables through the ENV variable within the application code. This can lead to runtime errors due to misconfiguration that could have been discovered at boot time if the environment variables were loaded as part of initialization and copied into the application’s configuration or secrets. The cop can be configured to allow either reads or writes if required.

Examples

# good
Rails.application.config.foo
Rails.application.config.x.foo.bar
Rails.application.secrets.foo
Rails.application.config.foo = "bar"

AllowReads: false (default)

# bad
ENV["FOO"]
ENV.fetch("FOO")

AllowReads: true

# good
ENV["FOO"]
ENV.fetch("FOO")

AllowWrites: false (default)

# bad
ENV["FOO"] = "bar"

AllowWrites: true

# good
ENV["FOO"] = "bar"

Configurable attributes

Name Default value Configurable values

Include

app/**/*.rb, config/initializers/**/*.rb, lib/**/*.rb

Array

Exclude

lib/**/*.rake

Array

AllowReads

false

Boolean

AllowWrites

false

Boolean

Rails/Exit

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.41

-

Enforces that exit calls are not used within a rails app. Valid options are instead to raise an error, break, return, or some other form of stopping execution of current request.

There are two obvious cases where exit is particularly harmful:

  • Usage in library code for your application. Even though Rails will rescue from a SystemExit and continue on, unit testing that library code will result in specs exiting (potentially silently if exit(0) is used.)

  • Usage in application code outside of the web process could result in the program exiting, which could result in the code failing to run and do its job.

Examples

# bad
exit(0)

# good
raise 'a bad error has happened'

Configurable attributes

Name Default value Configurable values

Include

app/**/*.rb, config/**/*.rb, lib/**/*.rb

Array

Exclude

lib/**/*.rake

Array

Rails/ExpandedDateRange

Required Rails version: 5.1
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.11

-

Checks for expanded date range. It only compatible .. range is targeted. Incompatible …​ range is ignored.

Examples

# bad
date.beginning_of_day..date.end_of_day
date.beginning_of_week..date.end_of_week
date.beginning_of_month..date.end_of_month
date.beginning_of_quarter..date.end_of_quarter
date.beginning_of_year..date.end_of_year

# good
date.all_day
date.all_week
date.all_month
date.all_quarter
date.all_year

Rails/FilePath

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.47

2.4

Identifies usages of file path joining process to use Rails.root.join clause. It is used to add uniformity when joining paths.

Examples

EnforcedStyle: slashes (default)

# bad
Rails.root.join('app', 'models', 'goober')

# good
Rails.root.join('app/models/goober')

# bad
File.join(Rails.root, 'app/models/goober')
"#{Rails.root}/app/models/goober"

# good
Rails.root.join('app/models/goober').to_s

EnforcedStyle: arguments

# bad
Rails.root.join('app/models/goober')

# good
Rails.root.join('app', 'models', 'goober')

# bad
File.join(Rails.root, 'app/models/goober')
"#{Rails.root}/app/models/goober"

# good
Rails.root.join('app', 'models', 'goober').to_s

Configurable attributes

Name Default value Configurable values

EnforcedStyle

slashes

slashes, arguments

Rails/FindBy

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.30

2.21

Identifies usages of where.take and change them to use find_by instead.

And where(…​).first can return different results from find_by. (They order records differently, so the "first" record can be different.)

If you also want to detect where.first, you can set IgnoreWhereFirst to false.

Examples

# bad
User.where(name: 'Bruce').take

# good
User.find_by(name: 'Bruce')

IgnoreWhereFirst: true (default)

# good
User.where(name: 'Bruce').first

IgnoreWhereFirst: false

# bad
User.where(name: 'Bruce').first

Configurable attributes

Name Default value Configurable values

IgnoreWhereFirst

true

Boolean

Rails/FindById

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.7

-

Enforces that ActiveRecord#find is used instead of where.take!, find_by!, and find_by_id! to retrieve a single record by primary key when you expect it to be found.

Examples

# bad
User.where(id: id).take!
User.find_by_id!(id)
User.find_by!(id: id)

# good
User.find(id)

Rails/FindEach

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Always (Unsafe)

0.30

2.21

Identifies usages of all.each and change them to use all.find_each instead.

Safety

This cop is unsafe if the receiver object is not an Active Record object. Also, all.each returns an Array instance and all.find_each returns nil, so the return values are different.

Examples

# bad
User.all.each

# good
User.all.find_each

AllowedMethods: ['order']

# good
User.order(:foo).each

AllowedPattern: ['order']

# good
User.order(:foo).each

Configurable attributes

Name Default value Configurable values

AllowedMethods

order, limit, select, lock

Array

AllowedPatterns

[]

Array

Rails/FreezeTime

Required Rails version: 5.2
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.16

-

Identifies usages of travel_to with an argument of the current time and change them to use freeze_time instead.

Safety

This cop’s autocorrection is unsafe because freeze_time just delegates to travel_to with a default Time.now, it is not strictly equivalent to Time.now if the argument of travel_to is the current time considering time zone.

Examples

# bad
travel_to(Time.now)
travel_to(Time.new)
travel_to(DateTime.now)
travel_to(Time.current)
travel_to(Time.zone.now)
travel_to(Time.now.in_time_zone)
travel_to(Time.current.to_time)

# good
freeze_time

Rails/HasAndBelongsToMany

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.12

-

Checks for the use of the has_and_belongs_to_many macro.

Examples

# bad
# has_and_belongs_to_many :ingredients

# good
# has_many :ingredients, through: :recipe_ingredients

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/HasManyOrHasOneDependent

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.50

-

Looks for has_many or has_one associations that don’t specify a :dependent option.

It doesn’t register an offense if :through or dependent: nil is specified, or if the model is read-only.

Examples

# bad
class User < ActiveRecord::Base
  has_many :comments
  has_one :avatar
end

# good
class User < ActiveRecord::Base
  has_many :comments, dependent: :restrict_with_exception
  has_one :avatar, dependent: :destroy
  has_many :articles, dependent: nil
  has_many :patients, through: :appointments
end

class User < ActiveRecord::Base
  has_many :comments
  has_one :avatar

  def readonly?
    true
  end
end

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/HelperInstanceVariable

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

2.0

-

Checks for use of the helper methods which reference instance variables.

Relying on instance variables makes it difficult to reuse helper methods.

If it seems awkward to explicitly pass in each dependent variable, consider moving the behavior elsewhere, for example to a model, decorator or presenter.

Provided that a class inherits ActionView::Helpers::FormBuilder, an offense will not be registered.

Examples

# bad
def welcome_message
  "Hello #{@user.name}"
end

# good
def welcome_message(user)
  "Hello #{user.name}"
end

# good
class MyFormBuilder < ActionView::Helpers::FormBuilder
  @template.do_something
end

Configurable attributes

Name Default value Configurable values

Include

app/helpers/**/*.rb

Array

Rails/HttpPositionalArguments

Required Rails version: 5.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.44

-

Identifies usages of http methods like get, post, put, patch without the usage of keyword arguments in your tests and change them to use keyword args. This cop only applies to Rails >= 5. If you are running Rails < 5 you should disable the Rails/HttpPositionalArguments cop or set your TargetRailsVersion in your .rubocop.yml file to 4.2.

It does not detect any cases where include Rack::Test::Methods is used which makes the http methods incompatible behavior.

Examples

# bad
get :new, { user_id: 1}

# good
get :new, params: { user_id: 1 }
get :new, **options

Configurable attributes

Name Default value Configurable values

Include

spec/**/*, test/**/*

Array

Rails/HttpStatus

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.54

2.11

Enforces use of symbolic or numeric value to define HTTP status.

Examples

EnforcedStyle: symbolic (default)

# bad
render :foo, status: 200
render :foo, status: '200'
render json: { foo: 'bar' }, status: 200
render plain: 'foo/bar', status: 304
redirect_to root_url, status: 301
head 200
assert_response 200
assert_redirected_to '/some/path', status: 301

# good
render :foo, status: :ok
render json: { foo: 'bar' }, status: :ok
render plain: 'foo/bar', status: :not_modified
redirect_to root_url, status: :moved_permanently
head :ok
assert_response :ok
assert_redirected_to '/some/path', status: :moved_permanently

EnforcedStyle: numeric

# bad
render :foo, status: :ok
render json: { foo: 'bar' }, status: :not_found
render plain: 'foo/bar', status: :not_modified
redirect_to root_url, status: :moved_permanently
head :ok
assert_response :ok
assert_redirected_to '/some/path', status: :moved_permanently

# good
render :foo, status: 200
render json: { foo: 'bar' }, status: 404
render plain: 'foo/bar', status: 304
redirect_to root_url, status: 301
head 200
assert_response 200
assert_redirected_to '/some/path', status: 301

Configurable attributes

Name Default value Configurable values

EnforcedStyle

symbolic

numeric, symbolic

Rails/I18nLazyLookup

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.14

-

Checks for places where I18n "lazy" lookup can be used.

This cop has two different enforcement modes. When the EnforcedStyle is lazy (the default), explicit lookups are added as offenses.

When the EnforcedStyle is explicit then lazy lookups are added as offenses.

Examples

EnforcedStyle: lazy (default)

# en.yml
# en:
#   books:
#     create:
#       success: Book created!

# bad
class BooksController < ApplicationController
  def create
    # ...
    redirect_to books_url, notice: t('books.create.success')
  end
end

# good
class BooksController < ApplicationController
  def create
    # ...
    redirect_to books_url, notice: t('.success')
  end
end

EnforcedStyle: explicit

# bad
class BooksController < ApplicationController
  def create
    # ...
    redirect_to books_url, notice: t('.success')
  end
end

# good
class BooksController < ApplicationController
  def create
    # ...
    redirect_to books_url, notice: t('books.create.success')
  end
end

Configurable attributes

Name Default value Configurable values

EnforcedStyle

lazy

lazy, explicit

Include

app/controllers/**/*.rb

Array

Rails/I18nLocaleAssignment

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.11

-

Checks for the use of I18n.locale= method.

The locale attribute persists for the rest of the Ruby runtime, potentially causing unexpected behavior at a later time. Using I18n.with_locale ensures the code passed in the block is the only place I18n.locale is affected. It eliminates the possibility of a locale sticking around longer than intended.

Examples

# bad
I18n.locale = :fr

# good
I18n.with_locale(:fr) do
end

Configurable attributes

Name Default value Configurable values

Include

spec/**/*.rb, test/**/*.rb

Array

Rails/I18nLocaleTexts

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.14

-

Enforces use of I18n and locale files instead of locale specific strings.

Examples

# bad
class User < ApplicationRecord
  validates :email, presence: { message: "must be present" }
end

# good
# config/locales/en.yml
# en:
#   activerecord:
#     errors:
#       models:
#         user:
#           blank: "must be present"

class User < ApplicationRecord
  validates :email, presence: true
end

# bad
class PostsController < ApplicationController
  def create
    # ...
    redirect_to root_path, notice: "Post created!"
  end
end

# good
# config/locales/en.yml
# en:
#   posts:
#     create:
#       success: "Post created!"

class PostsController < ApplicationController
  def create
    # ...
    redirect_to root_path, notice: t(".success")
  end
end

# bad
class UserMailer < ApplicationMailer
  def welcome(user)
    mail(to: user.email, subject: "Welcome to My Awesome Site")
  end
end

# good
# config/locales/en.yml
# en:
#   user_mailer:
#     welcome:
#       subject: "Welcome to My Awesome Site"

class UserMailer < ApplicationMailer
  def welcome(user)
    mail(to: user.email)
  end
end

Rails/IgnoredColumnsAssignment

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.17

-

Looks for assignments of ignored_columns that may override previous assignments.

Overwriting previous assignments is usually a mistake, since it will un-ignore the first set of columns. Since duplicate column names is not a problem, it is better to simply append to the list.

Examples

# bad
class User < ActiveRecord::Base
  self.ignored_columns = [:one]
end

# bad
class User < ActiveRecord::Base
  self.ignored_columns = [:one, :two]
end

# good
class User < ActiveRecord::Base
  self.ignored_columns += [:one, :two]
end

# good
class User < ActiveRecord::Base
  self.ignored_columns += [:one]
  self.ignored_columns += [:two]
end

Rails/IgnoredSkipActionFilterOption

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.63

-

Checks that if and only (or except) are not used together as options of skip_* action filter.

The if option will be ignored when if and only are used together. Similarly, the except option will be ignored when if and except are used together.

Examples

# bad
class MyPageController < ApplicationController
  skip_before_action :login_required,
    only: :show, if: :trusted_origin?
end

# good
class MyPageController < ApplicationController
  skip_before_action :login_required,
    if: -> { trusted_origin? && action_name == "show" }
end
# bad
class MyPageController < ApplicationController
  skip_before_action :login_required,
    except: :admin, if: :trusted_origin?
end

# good
class MyPageController < ApplicationController
  skip_before_action :login_required,
    if: -> { trusted_origin? && action_name != "admin" }
end

Configurable attributes

Name Default value Configurable values

Include

app/controllers/**/*.rb, app/mailers/**/*.rb

Array

Rails/IndexBy

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

2.5

2.8

Looks for uses of each_with_object({}) { …​ }, map { …​ }.to_h, and Hash[map { …​ }] that are transforming an enumerable into a hash where the values are the original elements. Rails provides the index_by method for this purpose.

Examples

# bad
[1, 2, 3].each_with_object({}) { |el, h| h[foo(el)] = el }
[1, 2, 3].to_h { |el| [foo(el), el] }
[1, 2, 3].map { |el| [foo(el), el] }.to_h
Hash[[1, 2, 3].collect { |el| [foo(el), el] }]

# good
[1, 2, 3].index_by { |el| foo(el) }

Rails/IndexWith

Required Rails version: 6.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

2.5

2.8

Looks for uses of each_with_object({}) { …​ }, map { …​ }.to_h, and Hash[map { …​ }] that are transforming an enumerable into a hash where the keys are the original elements. Rails provides the index_with method for this purpose.

Examples

# bad
[1, 2, 3].each_with_object({}) { |el, h| h[el] = foo(el) }
[1, 2, 3].to_h { |el| [el, foo(el)] }
[1, 2, 3].map { |el| [el, foo(el)] }.to_h
Hash[[1, 2, 3].collect { |el| [el, foo(el)] }]

# good
[1, 2, 3].index_with { |el| foo(el) }

Rails/Inquiry

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.7

-

Checks that Active Support’s inquiry method is not used.

Examples

# bad - String#inquiry
ruby = 'two'.inquiry
ruby.two?

# good
ruby = 'two'
ruby == 'two'

# bad - Array#inquiry
pets = %w(cat dog).inquiry
pets.gopher?

# good
pets = %w(cat dog)
pets.include? 'cat'

Rails/InverseOf

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.52

-

Looks for has_(one|many) and belongs_to associations where Active Record can’t automatically determine the inverse association because of a scope or the options used. Using the blog with order scope example below, traversing the a Blog’s association in both directions with blog.posts.first.blog would cause the blog to be loaded from the database twice.

:inverse_of must be manually specified for Active Record to use the associated object in memory, or set to false to opt-out. Note that setting nil does not stop Active Record from trying to determine the inverse automatically, and is not considered a valid value for this.

Examples

# good
class Blog < ApplicationRecord
  has_many :posts
end

class Post < ApplicationRecord
  belongs_to :blog
end
# bad
class Blog < ApplicationRecord
  has_many :posts, -> { order(published_at: :desc) }
end

class Post < ApplicationRecord
  belongs_to :blog
end

# good
class Blog < ApplicationRecord
  has_many(:posts,
           -> { order(published_at: :desc) },
           inverse_of: :blog)
end

class Post < ApplicationRecord
  belongs_to :blog
end

# good
class Blog < ApplicationRecord
  with_options inverse_of: :blog do
    has_many :posts, -> { order(published_at: :desc) }
  end
end

class Post < ApplicationRecord
  belongs_to :blog
end

# good
# When you don't want to use the inverse association.
class Blog < ApplicationRecord
  has_many(:posts,
           -> { order(published_at: :desc) },
           inverse_of: false)
end
# bad
class Picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

class Employee < ApplicationRecord
  has_many :pictures, as: :imageable
end

class Product < ApplicationRecord
  has_many :pictures, as: :imageable
end

# good
class Picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

class Employee < ApplicationRecord
  has_many :pictures, as: :imageable, inverse_of: :imageable
end

class Product < ApplicationRecord
  has_many :pictures, as: :imageable, inverse_of: :imageable
end
# bad
# However, RuboCop can not detect this pattern...
class Physician < ApplicationRecord
  has_many :appointments
  has_many :patients, through: :appointments
end

class Appointment < ApplicationRecord
  belongs_to :physician
  belongs_to :patient
end

class Patient < ApplicationRecord
  has_many :appointments
  has_many :physicians, through: :appointments
end

# good
class Physician < ApplicationRecord
  has_many :appointments
  has_many :patients, through: :appointments
end

class Appointment < ApplicationRecord
  belongs_to :physician, inverse_of: :appointments
  belongs_to :patient, inverse_of: :appointments
end

class Patient < ApplicationRecord
  has_many :appointments
  has_many :physicians, through: :appointments
end

IgnoreScopes: false (default)

# bad
class Blog < ApplicationRecord
  has_many :posts, -> { order(published_at: :desc) }
end

IgnoreScopes: true

# good
class Blog < ApplicationRecord
  has_many :posts, -> { order(published_at: :desc) }
end

Configurable attributes

Name Default value Configurable values

IgnoreScopes

false

Boolean

Include

app/models/**/*.rb

Array

Rails/LexicallyScopedActionFilter

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

No

0.52

-

Checks that methods specified in the filter’s only or except options are defined within the same class or module.

Safety

You can technically specify methods of superclass or methods added by mixins on the filter, but these can confuse developers. If you specify methods that are defined in other classes or modules, you should define the filter in that class or module.

If you rely on behavior defined in the superclass actions, you must remember to invoke super in the subclass actions.

Examples

# bad
class LoginController < ApplicationController
  before_action :require_login, only: %i[index settings logout]

  def index
  end
end

# good
class LoginController < ApplicationController
  before_action :require_login, only: %i[index settings logout]

  def index
  end

  def settings
  end

  def logout
  end
end
# bad
module FooMixin
  extend ActiveSupport::Concern

  included do
    before_action proc { authenticate }, only: :foo
  end
end

# good
module FooMixin
  extend ActiveSupport::Concern

  included do
    before_action proc { authenticate }, only: :foo
  end

  def foo
    # something
  end
end
class ContentController < ApplicationController
  def update
    @content.update(content_attributes)
  end
end

class ArticlesController < ContentController
  before_action :load_article, only: [:update]

  # the cop requires this method, but it relies on behavior defined
  # in the superclass, so needs to invoke `super`
  def update
    super
  end

  private

  def load_article
    @content = Article.find(params[:article_id])
  end
end

Configurable attributes

Name Default value Configurable values

Include

app/controllers/**/*.rb, app/mailers/**/*.rb

Array

Rails/LinkToBlank

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.62

-

Checks for calls to link_to, link_to_if, and link_to_unless methods that contain a target: '_blank' but no rel: 'noopener'. This can be a security risk as the loaded page will have control over the previous page and could change its location for phishing purposes.

The option rel: 'noreferrer' also blocks this behavior and removes the http-referrer header.

Examples

# bad
link_to 'Click here', url, target: '_blank'

# good
link_to 'Click here', url, target: '_blank', rel: 'noopener'

# good
link_to 'Click here', url, target: '_blank', rel: 'noreferrer'

Rails/MailerName

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.7

-

Enforces that mailer names end with Mailer suffix.

Without the Mailer suffix it isn’t immediately apparent what’s a mailer and which views are related to the mailer.

Safety

This cop’s autocorrection is unsafe because renaming a constant is always an unsafe operation.

Examples

# bad
class User < ActionMailer::Base
end

class User < ApplicationMailer
end

# good
class UserMailer < ActionMailer::Base
end

class UserMailer < ApplicationMailer
end

Configurable attributes

Name Default value Configurable values

Include

app/mailers/**/*.rb

Array

Rails/MatchRoute

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.7

-

Identifies places where defining routes with match can be replaced with a specific HTTP method.

Don’t use match to define any routes unless there is a need to map multiple request types among [:get, :post, :patch, :put, :delete] to a single action using the :via option.

Examples

# bad
match ':controller/:action/:id'
match 'photos/:id', to: 'photos#show', via: :get

# good
get ':controller/:action/:id'
get 'photos/:id', to: 'photos#show'
match 'photos/:id', to: 'photos#show', via: [:get, :post]
match 'photos/:id', to: 'photos#show', via: :all

Configurable attributes

Name Default value Configurable values

Include

config/routes.rb, config/routes/**/*.rb

Array

Rails/MigrationClassName

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.14

2.20

Makes sure that each migration file defines a migration class whose name matches the file name. (e.g. 20220224111111_create_users.rb should define CreateUsers class.)

Examples

# db/migrate/20220224111111_create_users.rb

# bad
class SellBooks < ActiveRecord::Migration[7.0]
end

# good
class CreateUsers < ActiveRecord::Migration[7.0]
end

Configurable attributes

Name Default value Configurable values

Include

db/**/*.rb

Array

Rails/NegateInclude

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Always (Unsafe)

2.7

2.9

Enforces the use of collection.exclude?(obj) over !collection.include?(obj).

Safety

This cop is unsafe because false positive will occur for receiver objects that do not have an exclude? method. (e.g. IPAddr)

Examples

# bad
!array.include?(2)
!hash.include?(:key)

# good
array.exclude?(2)
hash.exclude?(:key)

Rails/NotNullColumn

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.43

2.20

Checks for add_column calls with a NOT NULL constraint without a default value.

This cop only applies when adding a column to an existing table, since existing records will not have a value for the new column. New tables can freely use NOT NULL columns without defaults, since there are no records that could violate the constraint.

If you need to add a NOT NULL column to an existing table, you must add it as nullable first, back-fill the data, and then use change_column_null. Alternatively, you could add the column with a default first to have the database automatically backfill existing rows, and then use change_column_default to remove the default.

TEXT cannot have a default value in MySQL. The cop will automatically detect an adapter from development environment in config/database.yml or the environment variable DATABASE_URL when the Database option is not set. If the database is MySQL, this cop ignores offenses for TEXT columns.

Examples

# bad
add_column :users, :name, :string, null: false
add_reference :products, :category, null: false
change_table :users do |t|
  t.string :name, null: false
end

# good
add_column :users, :name, :string, null: true
add_column :users, :name, :string, null: false, default: ''
change_table :users do |t|
  t.string :name, null: false, default: ''
end
add_reference :products, :category
change_column_null :products, :category_id, false

Configurable attributes

Name Default value Configurable values

Database

<none>

mysql

Include

db/**/*.rb

Array

Rails/OrderById

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

2.8

-

Checks for places where ordering by id column is used.

Don’t use the id column for ordering. The sequence of ids is not guaranteed to be in any particular order, despite often (incidentally) being chronological. Use a timestamp column to order chronologically. As a bonus the intent is clearer.

Make sure the changed order column does not introduce performance bottlenecks and appropriate database indexes are added.

Examples

# bad
scope :chronological, -> { order(id: :asc) }
scope :chronological, -> { order(primary_key => :asc) }

# good
scope :chronological, -> { order(created_at: :asc) }

Rails/Output

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

0.15

0.19

Checks for the use of output calls like puts and print

Safety

This cop’s autocorrection is unsafe because depending on the Rails log level configuration, changing from puts to Rails.logger.debug could result in no output being shown.

Examples

# bad
puts 'A debug message'
pp 'A debug message'
print 'A debug message'

# good
Rails.logger.debug 'A debug message'

Configurable attributes

Name Default value Configurable values

Include

app/**/*.rb, config/**/*.rb, db/**/*.rb, lib/**/*.rb

Array

Rails/OutputSafety

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.41

-

Checks for the use of output safety calls like html_safe, raw, and safe_concat. These methods do not escape content. They simply return a SafeBuffer containing the content as is. Instead, use safe_join to join content and escape it and concat to concatenate content and escape it, ensuring its safety.

Examples

user_content = "<b>hi</b>"

# bad
"<p>#{user_content}</p>".html_safe
# => ActiveSupport::SafeBuffer "<p><b>hi</b></p>"

# good
content_tag(:p, user_content)
# => ActiveSupport::SafeBuffer "<p>&lt;b&gt;hi&lt;/b&gt;</p>"

# bad
out = ""
out << "<li>#{user_content}</li>"
out << "<li>#{user_content}</li>"
out.html_safe
# => ActiveSupport::SafeBuffer "<li><b>hi</b></li><li><b>hi</b></li>"

# good
out = []
out << content_tag(:li, user_content)
out << content_tag(:li, user_content)
safe_join(out)
# => ActiveSupport::SafeBuffer
#    "<li>&lt;b&gt;hi&lt;/b&gt;</li><li>&lt;b&gt;hi&lt;/b&gt;</li>"

# bad
out = "<h1>trusted content</h1>".html_safe
out.safe_concat(user_content)
# => ActiveSupport::SafeBuffer "<h1>trusted_content</h1><b>hi</b>"

# good
out = "<h1>trusted content</h1>".html_safe
out.concat(user_content)
# => ActiveSupport::SafeBuffer
#    "<h1>trusted_content</h1>&lt;b&gt;hi&lt;/b&gt;"

# safe, though maybe not good style
out = "trusted content"
result = out.concat(user_content)
# => String "trusted content<b>hi</b>"
# because when rendered in ERB the String will be escaped:
# <%= result %>
# => trusted content&lt;b&gt;hi&lt;/b&gt;

# bad
(user_content + " " + content_tag(:span, user_content)).html_safe
# => ActiveSupport::SafeBuffer "<b>hi</b> <span><b>hi</b></span>"

# good
safe_join([user_content, " ", content_tag(:span, user_content)])
# => ActiveSupport::SafeBuffer
#    "&lt;b&gt;hi&lt;/b&gt; <span>&lt;b&gt;hi&lt;/b&gt;</span>"

Rails/Pick

Required Rails version: 6.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Always (Unsafe)

2.6

-

Enforces the use of pick over pluck(…​).first.

Using pluck followed by first creates an intermediate array, which pick avoids. When called on an Active Record relation, pick adds a limit to the query so that only one value is fetched from the database.

Note that when pick is added to a relation with an existing limit, it causes a subquery to be added. In most cases this is undesirable, and care should be taken while resolving this violation.

Safety

This cop is unsafe because pluck is defined on both ActiveRecord::Relation and Enumerable, whereas pick is only defined on ActiveRecord::Relation in Rails 6.0. This was addressed in Rails 6.1 via rails/rails#38760, at which point the cop is safe.

Examples

# bad
Model.pluck(:a).first
[{ a: :b, c: :d }].pluck(:a, :b).first

# good
Model.pick(:a)
[{ a: :b, c: :d }].pick(:a, :b)

Rails/Pluck

Required Rails version: 5.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Always (Unsafe)

2.7

2.18

Enforces the use of pluck over map.

pluck can be used instead of map to extract a single key from each element in an enumerable. When called on an Active Record relation, it results in a more efficient query that only selects the necessary key.

If the receiver’s relation is not loaded and pluck is used inside an iteration, it may result in N+1 queries because pluck queries the database on each iteration. This cop ignores offenses for map/collect when they are suspected to be part of an iteration to prevent such potential issues.
users = User.all
5.times do
  users.map { |user| user[:foo] } # Only one query is executed
end

users = User.all
5.times do
  users.pluck(:id) # A query is executed on every iteration
end

Safety

This cop is unsafe because model can use column aliases.

# Original code
User.select('name AS nickname').map { |user| user[:nickname] } # => array of nicknames

# After autocorrection
User.select('name AS nickname').pluck(:nickname) # => raises ActiveRecord::StatementInvalid

Examples

# bad
Post.published.map { |post| post[:title] }
[{ a: :b, c: :d }].collect { |el| el[:a] }

# good
Post.published.pluck(:title)
[{ a: :b, c: :d }].pluck(:a)

Rails/PluckId

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

No

Always (Unsafe)

2.7

-

Enforces the use of ids over pluck(:id) and pluck(primary_key).

Safety

This cop is unsafe if the receiver object is not an Active Record object.

Examples

# bad
User.pluck(:id)
user.posts.pluck(:id)

def self.user_ids
  pluck(primary_key)
end

# good
User.ids
user.posts.ids

def self.user_ids
  ids
end

Rails/PluckInWhere

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Always (Unsafe)

2.7

2.8

Identifies places where pluck is used in where query methods and can be replaced with select.

Since pluck is an eager method and hits the database immediately, using select helps to avoid additional database queries by running as a subquery.

This cop has two modes of enforcement. When the EnforcedStyle is set to conservative (the default), only calls to pluck on a constant (e.g. a model class) within where are considered offenses.

Safety

When EnforcedStyle is set to aggressive, all calls to pluck within where are considered offenses. This might lead to false positives because the check cannot distinguish between calls to pluck on an ActiveRecord::Relation instance and calls to pluck on an Array instance.

Additionally, when using a subquery with the SQL IN operator, databases like PostgreSQL and MySQL can’t optimize complex queries as well. They need to scan all records of the outer table against the subquery result sequentially, rather than using an index. This can cause significant performance issues compared to writing the query differently or using pluck.

Examples

# bad
Post.where(user_id: User.active.pluck(:id))
Post.where(user_id: User.active.ids)
Post.where.not(user_id: User.active.pluck(:id))

# good
Post.where(user_id: User.active.select(:id))
Post.where(user_id: active_users.select(:id))
Post.where.not(user_id: active_users.select(:id))

EnforcedStyle: conservative (default)

# good
Post.where(user_id: active_users.pluck(:id))

EnforcedStyle: aggressive

# bad
Post.where(user_id: active_users.pluck(:id))

Configurable attributes

Name Default value Configurable values

EnforcedStyle

conservative

conservative, aggressive

Rails/PluralizationGrammar

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.35

-

Checks for correct grammar when using ActiveSupport’s core extensions to the numeric classes.

Examples

# bad
3.day.ago
1.months.ago
5.megabyte
1.gigabytes

# good
3.days.ago
1.month.ago
5.megabytes
1.gigabyte

Rails/Presence

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.52

-

Checks code that can be written more easily using Object#presence defined by Active Support.

Examples

# bad
a.present? ? a : nil

# bad
!a.present? ? nil : a

# bad
a.blank? ? nil : a

# bad
!a.blank? ? a : nil

# good
a.presence
# bad
a.present? ? a : b

# bad
!a.present? ? b : a

# bad
a.blank? ? b : a

# bad
!a.blank? ? a : b

# good
a.presence || b

Rails/Present

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.48

0.67

Checks for code that can be written with simpler conditionals using Object#present? defined by Active Support.

Interaction with Style/UnlessElse: The configuration of NotBlank will not produce an offense in the context of unless else if Style/UnlessElse is enabled. This is to prevent interference between the autocorrection of the two cops.

Examples

NotNilAndNotEmpty: true (default)

# Converts usages of `!nil? && !empty?` to `present?`

# bad
!foo.nil? && !foo.empty?

# bad
foo != nil && !foo.empty?

# good
foo.present?

NotBlank: true (default)

# Converts usages of `!blank?` to `present?`

# bad
!foo.blank?

# bad
not foo.blank?

# good
foo.present?

UnlessBlank: true (default)

# Converts usages of `unless blank?` to `if present?`

# bad
something unless foo.blank?

# good
something if foo.present?

Configurable attributes

Name Default value Configurable values

NotNilAndNotEmpty

true

Boolean

NotBlank

true

Boolean

UnlessBlank

true

Boolean

Rails/RakeEnvironment

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Always (Unsafe)

2.4

2.6

Checks for Rake tasks without the :environment task dependency. The :environment task loads application code for other Rake tasks. Without it, tasks cannot make use of application code like models.

You can ignore the offense if the task satisfies at least one of the following conditions:

  • The task does not need application code.

  • The task invokes the :environment task.

Safety

Probably not a problem in most cases, but it is possible that calling :environment task will break a behavior. It’s also slower. E.g. some task that only needs one gem to be loaded to run will run significantly faster without loading the whole application.

Examples

# bad
task :foo do
  do_something
end

# good
task foo: :environment do
  do_something
end

Configurable attributes

Name Default value Configurable values

Include

**/Rakefile, **/*.rake

Array

Exclude

lib/capistrano/tasks/**/*.rake

Array

Rails/ReadWriteAttribute

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.20

0.29

Checks for the use of the read_attribute or write_attribute methods and recommends square brackets instead.

If an attribute is missing from the instance (for example, when initialized by a partial select) then read_attribute will return nil, but square brackets will raise an ActiveModel::MissingAttributeError.

Explicitly raising an error in this situation is preferable, and that is why rubocop recommends using square brackets.

When called from within a method with the same name as the attribute, read_attribute and write_attribute must be used to prevent an infinite loop:

Examples

# bad
x = read_attribute(:attr)
write_attribute(:attr, val)

# good
x = self[:attr]
self[:attr] = val
# good
def foo
  bar || read_attribute(:foo)
end

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/RedundantActiveRecordAllMethod

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Always (Unsafe)

2.21

-

Detect redundant all used as a receiver for Active Record query methods.

For the methods delete_all and destroy_all, this cop will only check cases where the receiver is a model. It will ignore cases where the receiver is an association (e.g., user.articles.all.delete_all). This is because omitting all from an association changes the methods from ActiveRecord::Relation to ActiveRecord::Associations::CollectionProxy, which can affect their behavior.

Safety

This cop is unsafe for autocorrection if the receiver for all is not an Active Record object.

Examples

# bad
User.all.find(id)
User.all.order(:created_at)
users.all.where(id: ids)
user.articles.all.order(:created_at)

# good
User.find(id)
User.order(:created_at)
users.where(id: ids)
user.articles.order(:created_at)

AllowedReceivers: ['ActionMailer::Preview', 'ActiveSupport::TimeZone'] (default)

# good
ActionMailer::Preview.all.first
ActiveSupport::TimeZone.all.first

Configurable attributes

Name Default value Configurable values

AllowedReceivers

ActionMailer::Preview, ActiveSupport::TimeZone

Array

Rails/RedundantAllowNil

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.67

-

Checks Rails model validations for a redundant allow_nil when allow_blank is present.

Examples

# bad
validates :x, length: { is: 5 }, allow_nil: true, allow_blank: true

# bad
validates :x, length: { is: 5 }, allow_nil: false, allow_blank: true

# bad
validates :x, length: { is: 5 }, allow_nil: false, allow_blank: false

# good
validates :x, length: { is: 5 }, allow_blank: true

# good
validates :x, length: { is: 5 }, allow_blank: false

# good
# Here, `nil` is valid but `''` is not
validates :x, length: { is: 5 }, allow_nil: true, allow_blank: false

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/RedundantForeignKey

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

2.6

-

Detects cases where the :foreign_key option on associations is redundant.

Examples

# bad
class Post
  has_many :comments, foreign_key: 'post_id'
end

class Comment
  belongs_to :post, foreign_key: 'post_id'
end

# good
class Post
  has_many :comments
end

class Comment
  belongs_to :author, foreign_key: 'user_id'
end

Rails/RedundantPresenceValidationOnBelongsTo

Required Rails version: 5.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.13

-

Since Rails 5.0 the default for belongs_to is optional: false unless config.active_record.belongs_to_required_by_default is explicitly set to false. The presence validator is added automatically, and explicit presence validation is redundant.

Safety

This cop’s autocorrection is unsafe because it changes the default error message from "can’t be blank" to "must exist".

Examples

# bad
belongs_to :user
validates :user, presence: true

# bad
belongs_to :user
validates :user_id, presence: true

# bad
belongs_to :author, foreign_key: :user_id
validates :user_id, presence: true

# good
belongs_to :user

# good
belongs_to :author, foreign_key: :user_id

Rails/RedundantReceiverInWithOptions

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.52

-

Checks for redundant receiver in with_options. Receiver is implicit from Rails 4.2 or higher.

Examples

# bad
class Account < ApplicationRecord
  with_options dependent: :destroy do |assoc|
    assoc.has_many :customers
    assoc.has_many :products
    assoc.has_many :invoices
    assoc.has_many :expenses
  end
end

# good
class Account < ApplicationRecord
  with_options dependent: :destroy do
    has_many :customers
    has_many :products
    has_many :invoices
    has_many :expenses
  end
end
# bad
with_options options: false do |merger|
  merger.invoke(merger.something)
end

# good
with_options options: false do
  invoke(something)
end

# good
client = Client.new
with_options options: false do |merger|
  client.invoke(merger.something, something)
end

# ok
# When `with_options` includes a block, all scoping scenarios
# cannot be evaluated. Thus, it is ok to include the explicit
# receiver.
with_options options: false do |merger|
  merger.invoke
  with_another_method do |another_receiver|
    merger.invoke(another_receiver)
  end
end

Rails/RedundantTravelBack

Required Rails version: 5.2
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.12

-

Checks for redundant travel_back calls. Since Rails 5.2, travel_back is automatically called at the end of the test.

Examples

# bad
def teardown
  do_something
  travel_back
end

# good
def teardown
  do_something
end

# bad
after do
  do_something
  travel_back
end

# good
after do
  do_something
end

Configurable attributes

Name Default value Configurable values

Include

spec/**/*.rb, test/**/*.rb

Array

Rails/ReflectionClassName

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Always (Unsafe)

0.64

2.10

Checks if the value of the option class_name, in the definition of a reflection is a string.

Safety

This cop is unsafe because it cannot be determined whether constant or method return value specified to class_name is a string.

Examples

# bad
has_many :accounts, class_name: Account
has_many :accounts, class_name: Account.name

# good
has_many :accounts, class_name: 'Account'

Rails/RefuteMethods

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.56

-

Use assert_not methods instead of refute methods.

Examples

EnforcedStyle: assert_not (default)

# bad
refute false
refute_empty [1, 2, 3]
refute_equal true, false

# good
assert_not false
assert_not_empty [1, 2, 3]
assert_not_equal true, false

EnforcedStyle: refute

# bad
assert_not false
assert_not_empty [1, 2, 3]
assert_not_equal true, false

# good
refute false
refute_empty [1, 2, 3]
refute_equal true, false

Configurable attributes

Name Default value Configurable values

EnforcedStyle

assert_not

assert_not, refute

Include

**/test/**/*

Array

Rails/RelativeDateConstant

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

0.48

2.13

Checks whether constant value isn’t relative date. Because the relative date will be evaluated only once.

Safety

This cop’s autocorrection is unsafe because its dependence on the constant is not corrected.

Examples

# bad
class SomeClass
  EXPIRED_AT = 1.week.since
end

# good
class SomeClass
  EXPIRES = 1.week

  def self.expired_at
    EXPIRES.since
  end
end

# good
class SomeClass
  def self.expired_at
    1.week.since
  end
end

Rails/RenderInline

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.7

-

Looks for inline rendering within controller actions.

Examples

# bad
class ProductsController < ApplicationController
  def index
    render inline: "<% products.each do |p| %><p><%= p.name %></p><% end %>", type: :erb
  end
end

# good
# app/views/products/index.html.erb
# <% products.each do |p| %>
#   <p><%= p.name %></p>
# <% end %>

class ProductsController < ApplicationController
  def index
  end
end

Rails/RenderPlainText

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.7

-

Identifies places where render text: can be replaced with render plain:.

Examples

# bad - explicit MIME type to `text/plain`
render text: 'Ruby!', content_type: 'text/plain'

# good - short and precise
render plain: 'Ruby!'

# good - explicit MIME type not to `text/plain`
render text: 'Ruby!', content_type: 'text/html'

ContentTypeCompatibility: true (default)

# good - sets MIME type to `text/html`
render text: 'Ruby!'

ContentTypeCompatibility: false

# bad - sets MIME type to `text/html`
render text: 'Ruby!'

Configurable attributes

Name Default value Configurable values

ContentTypeCompatibility

true

Boolean

Rails/RequestReferer

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.41

-

Checks for consistent uses of request.referer or request.referrer, depending on the cop’s configuration.

Examples

EnforcedStyle: referer (default)

# bad
request.referrer

# good
request.referer

EnforcedStyle: referrer

# bad
request.referer

# good
request.referrer

Configurable attributes

Name Default value Configurable values

EnforcedStyle

referer

referer, referrer

Rails/RequireDependency

Required Rails version: 6.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

2.10

-

Checks for the usage of require_dependency.

require_dependency is an obsolete method for Rails applications running in Zeitwerk mode. In Zeitwerk mode, the semantics should match Ruby’s and no need to be defensive with load order, just refer to classes and modules normally. If the constant name is dynamic, camelize if needed, and constantize.

Applications running in Zeitwerk mode should not use require_dependency.

This cop is disabled by default. Please enable it if you are using Zeitwerk mode.

Examples

# bad
require_dependency 'some_lib'

Rails/ResponseParsedBody

Required Rails version: 5.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Always (Unsafe)

2.18

2.19

Prefer response.parsed_body to custom parsing logic for response.body.

Safety

This cop is unsafe because Content-Type may not be application/json or text/html. For example, the proprietary Content-Type provided by corporate entities such as application/vnd.github+json is not supported at response.parsed_body by default, so you still have to use JSON.parse(response.body) there.

Examples

# bad
JSON.parse(response.body)

# bad
Nokogiri::HTML.parse(response.body)

# bad
Nokogiri::HTML5.parse(response.body)

# good
response.parsed_body

Configurable attributes

Name Default value Configurable values

Include

spec/controllers/**/*.rb, spec/requests/**/*.rb, test/controllers/**/*.rb, test/integration/**/*.rb

Array

Rails/ReversibleMigration

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.47

2.13

Checks whether the change method of the migration file is reversible.

Examples

# bad
def change
  change_table :users do |t|
    t.remove :name
  end
end

# good
def change
  change_table :users do |t|
    t.remove :name, type: :string
  end
end

# good
def change
  create_table :users do |t|
    t.string :name
  end
end
# drop_table

# bad
def change
  drop_table :users
end

# good
def change
  drop_table :users do |t|
    t.string :name
  end
end
# change_column_default

# bad
def change
  change_column_default(:suppliers, :qualification, 'new')
end

# good
def change
  change_column_default(:posts, :state, from: nil, to: "draft")
end
# remove_column

# bad
def change
  remove_column(:suppliers, :qualification)
end

# good
def change
  remove_column(:suppliers, :qualification, :string)
end
# remove_foreign_key

# bad
def change
  remove_foreign_key :accounts, column: :owner_id
end

# good
def change
  remove_foreign_key :accounts, :branches
end

# good
def change
  remove_foreign_key :accounts, to_table: :branches
end
# change_table

# bad
def change
  change_table :users do |t|
    t.remove :name
    t.change_default :authorized, 1
    t.change :price, :string
  end
end

# good
def change
  change_table :users do |t|
    t.string :name
  end
end
# remove_columns

# bad
def change
  remove_columns :users, :name, :email
end

# good
def change
  reversible do |dir|
    dir.up do
      remove_columns :users, :name, :email
    end

    dir.down do
      add_column :users, :name, :string
      add_column :users, :email, :string
    end
  end
end

# good (Rails >= 6.1, see https://github.com/rails/rails/pull/36589)
def change
  remove_columns :users, :name, :email, type: :string
end
# remove_index

# bad
def change
  remove_index :users, name: :index_users_on_email
end

# good
def change
  remove_index :users, :email
end

# good
def change
  remove_index :users, column: :email
end

Configurable attributes

Name Default value Configurable values

Include

db/**/*.rb

Array

Rails/ReversibleMigrationMethodDefinition

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

2.10

2.13

Checks whether the migration implements either a change method or both an up and a down method.

Examples

# bad
class SomeMigration < ActiveRecord::Migration[6.0]
  def up
    # up migration
  end

  # <----- missing down method
end

class SomeMigration < ActiveRecord::Migration[6.0]
  # <----- missing up method

  def down
    # down migration
  end
end

# good
class SomeMigration < ActiveRecord::Migration[6.0]
  def change
    # reversible migration
  end
end

# good
class SomeMigration < ActiveRecord::Migration[6.0]
  def up
    # up migration
  end

  def down
    # down migration
  end
end

Configurable attributes

Name Default value Configurable values

Include

db/**/*.rb

Array

Rails/RootJoinChain

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.13

-

Use a single #join instead of chaining on Rails.root or Rails.public_path.

Examples

# bad
Rails.root.join('db').join('schema.rb')
Rails.root.join('db').join(migrate).join('migration.rb')
Rails.public_path.join('path').join('file.pdf')
Rails.public_path.join('path').join(to).join('file.pdf')

# good
Rails.root.join('db', 'schema.rb')
Rails.root.join('db', migrate, 'migration.rb')
Rails.public_path.join('path', 'file.pdf')
Rails.public_path.join('path', to, 'file.pdf')

Rails/RootPathnameMethods

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.16

-

Use Rails.root IO methods instead of passing it to File.

Rails.root is an instance of Pathname so we can apply many IO methods directly.

This cop works best when used together with Style/FileRead, Style/FileWrite and Rails/RootJoinChain.

Safety

This cop is unsafe for autocorrection because Dir's children, each_child, entries, and glob methods return string element, but these methods of Pathname return Pathname element.

Examples

# bad
File.open(Rails.root.join('db', 'schema.rb'))
File.open(Rails.root.join('db', 'schema.rb'), 'w')
File.read(Rails.root.join('db', 'schema.rb'))
File.binread(Rails.root.join('db', 'schema.rb'))
File.write(Rails.root.join('db', 'schema.rb'), content)
File.binwrite(Rails.root.join('db', 'schema.rb'), content)
Dir.glob(Rails.root.join('db', 'schema.rb'))
Dir[Rails.root.join('db', 'schema.rb')]

# good
Rails.root.join('db', 'schema.rb').open
Rails.root.join('db', 'schema.rb').open('w')
Rails.root.join('db', 'schema.rb').read
Rails.root.join('db', 'schema.rb').binread
Rails.root.join('db', 'schema.rb').write(content)
Rails.root.join('db', 'schema.rb').binwrite(content)
Rails.root.glob("db/schema.rb")

Rails/RootPublicPath

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.15

-

Favor Rails.public_path over Rails.root with 'public'

Examples

# bad
Rails.root.join('public')
Rails.root.join('public/file.pdf')
Rails.root.join('public', 'file.pdf')

# good
Rails.public_path
Rails.public_path.join('file.pdf')
Rails.public_path.join('file.pdf')

Rails/SafeNavigation

Requires Ruby version 2.3
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.43

-

Converts usages of try! to &.. It can also be configured to convert try. It will convert code to use safe navigation if the target Ruby version is set to 2.3+

Examples

ConvertTry: false (default)

# bad
foo.try!(:bar)
foo.try!(:bar, baz)
foo.try!(:bar) { |e| e.baz }

foo.try!(:[], 0)

# good
foo.try(:bar)
foo.try(:bar, baz)
foo.try(:bar) { |e| e.baz }

foo&.bar
foo&.bar(baz)
foo&.bar { |e| e.baz }

ConvertTry: true

# bad
foo.try!(:bar)
foo.try!(:bar, baz)
foo.try!(:bar) { |e| e.baz }
foo.try(:bar)
foo.try(:bar, baz)
foo.try(:bar) { |e| e.baz }

# good
foo&.bar
foo&.bar(baz)
foo&.bar { |e| e.baz }

Configurable attributes

Name Default value Configurable values

ConvertTry

false

Boolean

Rails/SafeNavigationWithBlank

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

2.4

-

Checks to make sure safe navigation isn’t used with blank? in a conditional.

Safety

While the safe navigation operator is generally a good idea, when checking foo&.blank? in a conditional, foo being nil will actually do the opposite of what the author intends.

For example:

foo&.blank? #=> nil
foo.blank? #=> true

Examples

# bad
do_something if foo&.blank?
do_something unless foo&.blank?

# good
do_something if foo.blank?
do_something unless foo.blank?

Rails/SaveBang

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

Always (Unsafe)

0.42

0.59

Identifies possible cases where Active Record save! or related should be used instead of save because the model might have failed to save and an exception is better than unhandled failure.

This will allow:

  • update or save calls, assigned to a variable, or used as a condition in an if/unless/case statement.

  • create calls, assigned to a variable that then has a call to persisted?, or whose return value is checked by persisted? immediately

  • calls if the result is explicitly returned from methods and blocks, or provided as arguments.

  • calls whose signature doesn’t look like an ActiveRecord persistence method.

By default it will also allow implicit returns from methods and blocks. that behavior can be turned off with AllowImplicitReturn: false.

You can permit receivers that are giving false positives with AllowedReceivers: []

Safety

This cop’s autocorrection is unsafe because a custom update method call would be changed to update!, but the method name in the definition would be unchanged.

# Original code
def update_attributes
end

update_attributes

# After running rubocop --safe-autocorrect
def update_attributes
end

update

Examples

# bad
user.save
user.update(name: 'Joe')
user.find_or_create_by(name: 'Joe')
user.destroy

# good
unless user.save
  # ...
end
user.save!
user.update!(name: 'Joe')
user.find_or_create_by!(name: 'Joe')
user.destroy!

user = User.find_or_create_by(name: 'Joe')
unless user.persisted?
  # ...
end

def save_user
  return user.save
end

AllowImplicitReturn: true (default)

# good
users.each { |u| u.save }

def save_user
  user.save
end

AllowImplicitReturn: false

# bad
users.each { |u| u.save }
def save_user
  user.save
end

# good
users.each { |u| u.save! }

def save_user
  user.save!
end

def save_user
  return user.save
end

AllowedReceivers: ['merchant.customers', 'Service::Mailer']

# bad
merchant.create
customers.builder.save
Mailer.create

module Service::Mailer
  self.create
end

# good
merchant.customers.create
MerchantService.merchant.customers.destroy
Service::Mailer.update(message: 'Message')
::Service::Mailer.update
Services::Service::Mailer.update(message: 'Message')
Service::Mailer::update

Configurable attributes

Name Default value Configurable values

AllowImplicitReturn

true

Boolean

AllowedReceivers

[]

Array

Rails/SchemaComment

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

2.13

-

Enforces the use of the comment option when adding a new table or column to the database during a migration.

Examples

# bad (no comment for a new column or table)
add_column :table, :column, :integer

create_table :table do |t|
  t.type :column
end

# good
add_column :table, :column, :integer, comment: 'Number of offenses'

create_table :table, comment: 'Table of offenses data' do |t|
  t.type :column, comment: 'Number of offenses'
end

Rails/ScopeArgs

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.19

2.12

Checks for scope calls where it was passed a method (usually a scope) instead of a lambda/proc.

Examples

# bad
scope :something, where(something: true)

# good
scope :something, -> { where(something: true) }

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/SelectMap

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Always (Unsafe)

2.21

-

Checks for uses of select(:column_name) with map(&:column_name). These can be replaced with pluck(:column_name).

There also should be some performance improvement since it skips instantiating the model class for matches.

Safety

This cop is unsafe because the model might override the attribute getter. Additionally, the model’s after_initialize hooks are skipped when using pluck.

Examples

# bad
Model.select(:column_name).map(&:column_name)

# good
Model.pluck(:column_name)

Rails/ShortI18n

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.7

-

Enforces that short forms of I18n methods are used: t instead of translate and l instead of localize.

This cop has two different enforcement modes. When the EnforcedStyle is conservative (the default) then only I18n.translate and I18n.localize calls are added as offenses.

When the EnforcedStyle is aggressive then all translate and localize calls without a receiver are added as offenses.

Examples

# bad
I18n.translate :key
I18n.localize Time.now

# good
I18n.t :key
I18n.l Time.now

EnforcedStyle: conservative (default)

# good
translate :key
localize Time.now
t :key
l Time.now

EnforcedStyle: aggressive

# bad
translate :key
localize Time.now

# good
t :key
l Time.now

Configurable attributes

Name Default value Configurable values

EnforcedStyle

conservative

conservative, aggressive

Rails/SkipsModelValidations

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

No

0.47

2.25

Checks for the use of methods which skip validations which are listed in https://guides.rubyonrails.org/active_record_validations.html#skipping-validations

Methods may be ignored from this rule by configuring a AllowedMethods.

Safety

This cop is unsafe if the receiver object is not an Active Record object.

Examples

# bad
Article.first.decrement!(:view_count)
DiscussionBoard.decrement_counter(:post_count, 5)
Article.first.increment!(:view_count)
DiscussionBoard.increment_counter(:post_count, 5)
person.toggle :active
product.touch
Billing.update_all("category = 'authorized', author = 'David'")
user.update_attribute(:website, 'example.com')
user.update_columns(last_request_at: Time.current)
Post.update_counters 5, comment_count: -1, action_count: 1

# good
user.update(website: 'example.com')
FileUtils.touch('file')

AllowedMethods: ["touch"]

# bad
DiscussionBoard.decrement_counter(:post_count, 5)
DiscussionBoard.increment_counter(:post_count, 5)
person.toggle :active

# good
user.touch

Configurable attributes

Name Default value Configurable values

ForbiddenMethods

decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all

Array

AllowedMethods

[]

Array

Rails/SquishedSQLHeredocs

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.8

2.9

Checks SQL heredocs to use .squish.

Safety

Some SQL syntax (e.g. PostgreSQL comments and functions) requires newlines to be preserved in order to work, thus autocorrection for this cop is not safe.

Examples

# bad
<<-SQL
  SELECT * FROM posts;
SQL

<<-SQL
  SELECT * FROM posts
    WHERE id = 1
SQL

execute(<<~SQL, "Post Load")
  SELECT * FROM posts
    WHERE post_id = 1
SQL

# good
<<-SQL.squish
  SELECT * FROM posts;
SQL

<<~SQL.squish
  SELECT * FROM table
    WHERE id = 1
SQL

execute(<<~SQL.squish, "Post Load")
  SELECT * FROM posts
    WHERE post_id = 1
SQL

Rails/StripHeredoc

Requires Ruby version 2.3
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.15

-

Enforces the use of squiggly heredoc over strip_heredoc.

Examples

# bad
<<EOS.strip_heredoc
  some text
EOS

# bad
<<-EOS.strip_heredoc
  some text
EOS

# good
<<~EOS
  some text
EOS

Rails/TableNameAssignment

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

2.14

-

Enforces the absence of explicit table name assignment.

self.table_name= should only be used for very good reasons, such as not having control over the database, or working on a legacy project.

If you need to change how your model’s name is translated to a table name, you may want to look at Inflections: https://api.rubyonrails.org/classes/ActiveSupport/Inflector/Inflections.html

If you wish to add a prefix in front of your model, or wish to change the default prefix, self.table_name_prefix might better suit your needs: https://api.rubyonrails.org/classes/ActiveRecord/ModelSchema.html#method-c-table_name_prefix-3D

STI base classes named Base are ignored by this cop. For more information: https://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html

Examples

# bad
self.table_name = 'some_table_name'
self.table_name = :some_other_name

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/ThreeStateBooleanColumn

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.19

-

Enforces that boolean columns are created with default values (false or true) and NOT NULL constraint.

Examples

# bad
add_column :users, :active, :boolean
t.column :active, :boolean
t.boolean :active

# good
add_column :users, :active, :boolean, default: true, null: false
t.column :active, :boolean, default: true, null: false
t.boolean :active, default: true, null: false

Configurable attributes

Name Default value Configurable values

Include

db/**/*.rb

Array

Rails/TimeZone

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

0.30

2.13

Checks for the use of Time methods without zone.

Built on top of Ruby on Rails style guide (https://rails.rubystyle.guide#time) and the article http://danilenko.org/2012/7/6/rails_timezones/

Two styles are supported for this cop. When EnforcedStyle is 'strict' then only use of Time.zone is allowed.

When EnforcedStyle is 'flexible' then it’s also allowed to use Time#in_time_zone.

Safety

This cop’s autocorrection is unsafe because it may change handling time.

Examples

# bad
Time.now
Time.parse('2015-03-02T19:05:37')
'2015-03-02T19:05:37'.to_time

# good
Time.current
Time.zone.now
Time.zone.parse('2015-03-02T19:05:37')
Time.zone.parse('2015-03-02T19:05:37Z') # Respect ISO 8601 format with timezone specifier.
Time.parse('2015-03-02T19:05:37Z') # Also respects ISO 8601
'2015-03-02T19:05:37Z'.to_time # Also respects ISO 8601

EnforcedStyle: flexible (default)

# `flexible` allows usage of `in_time_zone` instead of `zone`.

# good
Time.at(timestamp).in_time_zone

EnforcedStyle: strict

# `strict` means that `Time` should be used with `zone`.

# bad
Time.at(timestamp).in_time_zone

Configurable attributes

Name Default value Configurable values

EnforcedStyle

flexible

strict, flexible

Exclude

**/*.gemspec

Array

Rails/TimeZoneAssignment

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.10

-

Checks for the use of Time.zone= method.

The zone attribute persists for the rest of the Ruby runtime, potentially causing unexpected behavior at a later time. Using Time.use_zone ensures the code passed in the block is the only place Time.zone is affected. It eliminates the possibility of a zone sticking around longer than intended.

Examples

# bad
Time.zone = 'EST'

# good
Time.use_zone('EST') do
end

Configurable attributes

Name Default value Configurable values

Include

spec/**/*.rb, test/**/*.rb

Array

Rails/ToFormattedS

Required Rails version: 7.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.15

-

Checks for consistent uses of to_fs or to_formatted_s, depending on the cop’s configuration.

Examples

EnforcedStyle: to_fs (default)

# bad
time.to_formatted_s(:db)

# good
time.to_fs(:db)

EnforcedStyle: to_formatted_s

# bad
time.to_fs(:db)

# good
time.to_formatted_s(:db)

Configurable attributes

Name Default value Configurable values

EnforcedStyle

to_fs

to_fs, to_formatted_s

Rails/ToSWithArgument

Required Rails version: 7.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Always (Unsafe)

2.16

-

Identifies passing any argument to #to_s.

Safety

This cop is marked as unsafe because it may detect #to_s calls that are not related to Active Support implementation.

Examples

# bad
obj.to_s(:delimited)

# good
obj.to_formatted_s(:delimited)

Rails/TopLevelHashWithIndifferentAccess

Required Rails version: 5.1
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.16

2.18

Identifies top-level HashWithIndifferentAccess. This has been soft-deprecated since Rails 5.1.

Examples

# bad
HashWithIndifferentAccess.new(foo: 'bar')

# good
ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Rails/TransactionExitStatement

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.14

-

Checks for the use of exit statements (namely return, break and throw) in transactions. This is due to the eventual unexpected behavior when using ActiveRecord >= 7, where transactions exited using these statements are being rollbacked rather than committed (pre ActiveRecord 7 behavior).

As alternatives, it would be more intuitive to explicitly raise an error when rollback is desired, and to use next when commit is desired.

If you are defining custom transaction methods, you can configure it with TransactionMethods.

This cop is disabled on Rails >= 7.2 because transactions were restored to their historical behavior. In Rails 7.1, the behavior is controlled with the config active_record.commit_transaction_on_non_local_return.

Examples

# bad
ApplicationRecord.transaction do
  return if user.active?
end

# bad
ApplicationRecord.transaction do
  break if user.active?
end

# bad
ApplicationRecord.transaction do
  throw if user.active?
end

# bad, as `with_lock` implicitly opens a transaction too
user.with_lock do
  throw if user.active?
end

# bad, as `with_lock` implicitly opens a transaction too
ApplicationRecord.with_lock do
  break if user.active?
end

# good
ApplicationRecord.transaction do
  # Rollback
  raise "User is active" if user.active?
end

# good
ApplicationRecord.transaction do
  # Commit
  next if user.active?
end

TransactionMethods: ["custom_transaction"]

# bad
CustomModel.custom_transaction do
  return if user.active?
end

Configurable attributes

Name Default value Configurable values

TransactionMethods

[]

Array

Rails/UniqBeforePluck

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always (Unsafe)

0.40

2.13

Prefer using distinct before pluck instead of uniq after pluck.

The use of distinct before pluck is preferred because it executes by the database.

This cop has two different enforcement modes. When the EnforcedStyle is conservative (the default), then only calls to pluck on a constant (i.e. a model class) before uniq are added as offenses.

When the EnforcedStyle is aggressive then all calls to pluck before distinct are added as offenses. This may lead to false positives as the cop cannot distinguish between calls to pluck on an ActiveRecord::Relation vs a call to pluck on an ActiveRecord::Associations::CollectionProxy.

Safety

This cop is unsafe for autocorrection because the behavior may change depending on the database collation.

Examples

EnforcedStyle: conservative (default)

# bad - redundantly fetches duplicate values
Album.pluck(:band_name).uniq

# good
Album.distinct.pluck(:band_name)

EnforcedStyle: aggressive

# bad - redundantly fetches duplicate values
Album.pluck(:band_name).uniq

# bad - redundantly fetches duplicate values
Album.where(year: 1985).pluck(:band_name).uniq

# bad - redundantly fetches duplicate values
customer.favourites.pluck(:color).uniq

# good
Album.distinct.pluck(:band_name)
Album.distinct.where(year: 1985).pluck(:band_name)
customer.favourites.distinct.pluck(:color)

Configurable attributes

Name Default value Configurable values

EnforcedStyle

conservative

conservative, aggressive

Rails/UniqueValidationWithoutIndex

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

2.5

-

When you define a uniqueness validation in Active Record model, you also should add a unique index for the column. There are two reasons. First, duplicated records may occur even if Active Record’s validation is defined. Second, it will cause slow queries. The validation executes a SELECT statement with the target column when inserting/updating a record. If the column does not have an index and the table is large, the query will be heavy.

Note that the cop does nothing if db/schema.rb does not exist.

Examples

# bad - if the schema does not have a unique index
validates :account, uniqueness: true

# good - if the schema has a unique index
validates :account, uniqueness: true

# good - even if the schema does not have a unique index
validates :account, length: { minimum: MIN_LENGTH }

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/UnknownEnv

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.51

2.18

Checks that environments called with Rails.env predicates exist. By default the cop allows three environments which Rails ships with: development, test, and production. More can be added to the Environments config parameter.

Examples

# bad
Rails.env.proudction?
Rails.env == 'proudction'

# good
Rails.env.production?
Rails.env == 'production'

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Environments

development, test, production

Array

Rails/UnusedIgnoredColumns

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

2.11

2.25

Suggests you remove a column that does not exist in the schema from ignored_columns. ignored_columns is necessary to drop a column from RDBMS, but you don’t need it after the migration to drop the column. You avoid forgetting to remove ignored_columns by this cop.

This cop can’t be used to effectively check for unused columns because the development and production schema can be out of sync until the migration has been run on production. As such, this cop can cause ignored_columns to be removed even though the production schema still contains the column, which can lead to downtime when the migration is actually executed. Only enable this cop if you know your migrations will be run before any of your Rails applications boot with the modified code.

Examples

# bad
class User < ApplicationRecord
  self.ignored_columns = [:already_removed_column]
end

# good
class User < ApplicationRecord
  self.ignored_columns = [:still_existing_column]
end

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/UnusedRenderContent

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.21

-

If you try to render content along with a non-content status code (100-199, 204, 205, or 304), it will be dropped from the response.

This cop checks for uses of render which specify both body content and a non-content status.

Examples

# bad
render 'foo', status: :continue
render status: 100, plain: 'Ruby!'

# good
head :continue
head 100

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Rails/Validation

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Always

0.9

0.41

Checks for the use of old-style attribute validation macros.

Examples

# bad
validates_acceptance_of :foo
validates_comparison_of :foo
validates_confirmation_of :foo
validates_exclusion_of :foo
validates_format_of :foo
validates_inclusion_of :foo
validates_length_of :foo
validates_numericality_of :foo
validates_presence_of :foo
validates_absence_of :foo
validates_size_of :foo
validates_uniqueness_of :foo

# good
validates :foo, acceptance: true
validates :foo, confirmation: true
validates :foo, comparison: true
validates :foo, exclusion: true
validates :foo, format: true
validates :foo, inclusion: true
validates :foo, length: true
validates :foo, numericality: true
validates :foo, presence: true
validates :foo, absence: true
validates :foo, length: true
validates :foo, uniqueness: true

Configurable attributes

Name Default value Configurable values

Include

app/models/**/*.rb

Array

Rails/WhereEquals

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.9

2.26

Identifies places where manually constructed SQL in where and where.not can be replaced with where(attribute: value) and where.not(attribute: value).

Safety

This cop’s autocorrection is unsafe because is may change SQL. See: https://github.com/rubocop/rubocop-rails/issues/403

Examples

# bad
User.where('name = ?', 'Gabe')
User.where.not('name = ?', 'Gabe')
User.where('name = :name', name: 'Gabe')
User.where('name IS NULL')
User.where('name IN (?)', ['john', 'jane'])
User.where('name IN (:names)', names: ['john', 'jane'])
User.where('users.name = :name', name: 'Gabe')

# good
User.where(name: 'Gabe')
User.where.not(name: 'Gabe')
User.where(name: nil)
User.where(name: ['john', 'jane'])
User.where(users: { name: 'Gabe' })

Rails/WhereExists

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.7

2.10

Enforces consistent style when using exists?.

Two styles are supported for this cop. When EnforcedStyle is 'exists' then the cop enforces exists?(…​) over where(…​).exists?.

When EnforcedStyle is 'where' then the cop enforces where(…​).exists? over exists?(…​).

Safety

This cop is unsafe for autocorrection because the behavior may change on the following case:

Author.includes(:articles).where(articles: {id: id}).exists?
#=> Perform `eager_load` behavior (`LEFT JOIN` query) and get result.

Author.includes(:articles).exists?(articles: {id: id})
#=> Perform `preload` behavior and `ActiveRecord::StatementInvalid` error occurs.

Examples

EnforcedStyle: exists (default)

# bad
User.where(name: 'john').exists?
User.where(['name = ?', 'john']).exists?
User.where('name = ?', 'john').exists?
user.posts.where(published: true).exists?

# good
User.exists?(name: 'john')
User.where('length(name) > 10').exists?
user.posts.exists?(published: true)

EnforcedStyle: where

# bad
User.exists?(name: 'john')
User.exists?(['name = ?', 'john'])
user.posts.exists?(published: true)

# good
User.where(name: 'john').exists?
User.where(['name = ?', 'john']).exists?
User.where('name = ?', 'john').exists?
user.posts.where(published: true).exists?
User.where('length(name) > 10').exists?

Configurable attributes

Name Default value Configurable values

EnforcedStyle

exists

exists, where

Rails/WhereMissing

Required Rails version: 6.1
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.16

-

Use where.missing(…​) to find missing relationship records.

This cop is enabled in Rails 6.1 or higher.

Examples

# bad
Post.left_joins(:author).where(authors: { id: nil })

# good
Post.where.missing(:author)

Rails/WhereNot

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always

2.8

-

Identifies places where manually constructed SQL in where can be replaced with where.not(…​).

Examples

# bad
User.where('name != ?', 'Gabe')
User.where('name != :name', name: 'Gabe')
User.where('name <> ?', 'Gabe')
User.where('name <> :name', name: 'Gabe')
User.where('name IS NOT NULL')
User.where('name NOT IN (?)', ['john', 'jane'])
User.where('name NOT IN (:names)', names: ['john', 'jane'])
User.where('users.name != :name', name: 'Gabe')

# good
User.where.not(name: 'Gabe')
User.where.not(name: nil)
User.where.not(name: ['john', 'jane'])
User.where.not(users: { name: 'Gabe' })

Rails/WhereNotWithMultipleConditions

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

2.17

2.18

Identifies calls to where.not with multiple hash arguments.

The behavior of where.not changed in Rails 6.1. Prior to the change, .where.not(trashed: true, role: 'admin') evaluated to WHERE trashed != TRUE AND role != 'admin'. From Rails 6.1 onwards, this executes the query WHERE NOT (trashed == TRUE AND roles == 'admin').

Examples

# bad
User.where.not(trashed: true, role: 'admin')
User.where.not(trashed: true, role: ['moderator', 'admin'])
User.joins(:posts).where.not(posts: { trashed: true, title: 'Rails' })

# good
User.where.not(trashed: true)
User.where.not(role: ['moderator', 'admin'])
User.where.not(trashed: true).where.not(role: ['moderator', 'admin'])
User.where.not('trashed = ? OR role = ?', true, 'admin')

Configurable attributes

Name Default value Configurable values

Severity

warning

String

Rails/WhereRange

Requires Ruby version 2.6
Required Rails version: 6.0
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Always (Unsafe)

2.25

-

Identifies places where manually constructed SQL in where can be replaced with ranges.

Safety

This cop’s autocorrection is unsafe because it can change the query by explicitly attaching the column to the wrong table. For example, Booking.joins(:events).where('end_at < ?', Time.current) will correctly implicitly attach the end_at column to the events table. But when autocorrected to Booking.joins(:events).where(end_at: …​Time.current), it will now be incorrectly explicitly attached to the bookings table.

Examples

# bad
User.where('age >= ?', 18)
User.where.not('age >= ?', 18)
User.where('age < ?', 18)
User.where('age >= ? AND age < ?', 18, 21)
User.where('age >= :start', start: 18)
User.where('users.age >= ?', 18)

# good
User.where(age: 18..)
User.where.not(age: 18..)
User.where(age: ...18)
User.where(age: 18...21)
User.where(users: { age: 18.. })

# good
# There are no beginless ranges in ruby.
User.where('age > ?', 18)