////
  Do NOT edit this file by hand directly, as it is automatically generated.

  Please make any necessary changes to the cop documentation within the source files themselves.
////

= Rails

[#railsactioncontrollerflashbeforerender]
== 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-railsactioncontrollerflashbeforerender]
=== 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-railsactioncontrollerflashbeforerender]
=== Examples

[source,ruby]
----
# 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
----

[#railsactioncontrollertestcase]
== Rails/ActionControllerTestCase

NOTE: Requires 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-railsactioncontrollertestcase]
=== 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-railsactioncontrollertestcase]
=== Examples

[source,ruby]
----
# bad
class MyControllerTest < ActionController::TestCase
end

# good
class MyControllerTest < ActionDispatch::IntegrationTest
end
----

[#configurable-attributes-railsactioncontrollertestcase]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/test/**/*.rb+`
| Array
|===

[#references-railsactioncontrollertestcase]
=== References

* https://rails.rubystyle.guide/#integration-testing
* https://api.rubyonrails.org/classes/ActionController/TestCase.html

[#railsactionfilter]
== 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.

IMPORTANT: 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-railsactionfilter]
=== Examples

[#enforcedstyle_-action-_default_-railsactionfilter]
==== EnforcedStyle: action (default)

[source,ruby]
----
# 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-railsactionfilter]
==== EnforcedStyle: filter

[source,ruby]
----
# 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-railsactionfilter]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `action`
| `action`, `filter`

| Include
| `+**/app/controllers/**/*.rb+`, `+**/app/mailers/**/*.rb+`
| Array
|===

[#railsactionorder]
== 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.

[source,yaml]
----
 Rails/ActionOrder:
   ExpectedOrder:
     - index
     - show
     - new
     - edit
     - create
     - update
     - destroy
----

[#examples-railsactionorder]
=== Examples

[source,ruby]
----
# bad
def index; end
def destroy; end
def show; end

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

[#configurable-attributes-railsactionorder]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| ExpectedOrder
| `index`, `show`, `new`, `edit`, `create`, `update`, `destroy`
| Array

| Include
| `+**/app/controllers/**/*.rb+`
| Array
|===

[#railsactiverecordaliases]
== 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-railsactiverecordaliases]
=== 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-railsactiverecordaliases]
=== Examples

[source,ruby]
----
# bad
book.update_attributes!(author: 'Alice')

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

[#railsactiverecordcallbacksorder]
== 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-railsactiverecordcallbacksorder]
=== Examples

[source,ruby]
----
# 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-railsactiverecordcallbacksorder]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#references-railsactiverecordcallbacksorder]
=== References

* https://rails.rubystyle.guide/#callbacks-order

[#railsactiverecordoverride]
== 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-railsactiverecordoverride]
=== Examples

[source,ruby]
----
# 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-railsactiverecordoverride]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#railsactivesupportaliases]
== 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-railsactivesupportaliases]
=== Examples

[source,ruby]
----
# 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')
----

[#railsactivesupportonload]
== 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-railsactivesupportonload]
=== 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-railsactivesupportonload]
=== Examples

[source,ruby]
----
# bad
ActiveRecord::Base.include(MyClass)

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

[#references-railsactivesupportonload]
=== References

* https://api.rubyonrails.org/classes/ActiveSupport/LazyLoadHooks.html
* https://guides.rubyonrails.org/engines.html#available-load-hooks

[#railsaddcolumnindex]
== 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-railsaddcolumnindex]
=== Examples

[source,ruby]
----
# 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-railsaddcolumnindex]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+db/**/*.rb+`
| Array
|===

[#railsaftercommitoverride]
== 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-railsaftercommitoverride]
=== Examples

[source,ruby]
----
# 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
----

[#railsapplicationcontroller]
== 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-railsapplicationcontroller]
=== 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-railsapplicationcontroller]
=== Examples

[source,ruby]
----
# good
class MyController < ApplicationController
  # ...
end

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

[#railsapplicationjob]
== Rails/ApplicationJob

NOTE: Requires 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-railsapplicationjob]
=== 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-railsapplicationjob]
=== Examples

[source,ruby]
----
# good
class Rails5Job < ApplicationJob
  # ...
end

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

[#railsapplicationmailer]
== Rails/ApplicationMailer

NOTE: Requires 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-railsapplicationmailer]
=== 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-railsapplicationmailer]
=== Examples

[source,ruby]
----
# good
class MyMailer < ApplicationMailer
  # ...
end

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

[#railsapplicationrecord]
== Rails/ApplicationRecord

NOTE: Requires 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-railsapplicationrecord]
=== 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-railsapplicationrecord]
=== Examples

[source,ruby]
----
# good
class Rails5Model < ApplicationRecord
  # ...
end

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

[#configurable-attributes-railsapplicationrecord]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Exclude
| `+db/**/*.rb+`
| Array
|===

[#railsarelstar]
== 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. `my_model`.`*`). This causes the
database to look for a column named `\*` (or `"*"`) as opposed
to expanding the column list as one would likely expect.

[#safety-railsarelstar]
=== 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-railsarelstar]
=== Examples

[source,ruby]
----
# bad
MyTable.arel_table["*"]

# good
MyTable.arel_table[Arel.star]
----

[#railsassertnot]
== Rails/AssertNot

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Enabled
| Yes
| Always
| 0.56
| -
|===

Use `assert_not` instead of `assert !`.

[#examples-railsassertnot]
=== Examples

[source,ruby]
----
# bad
assert !x

# good
assert_not x
----

[#configurable-attributes-railsassertnot]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/test/**/*+`
| Array
|===

[#railsattributedefaultblockvalue]
== 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-railsattributedefaultblockvalue]
=== Examples

[source,ruby]
----
# 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-railsattributedefaultblockvalue]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*+`
| Array
|===

[#railsbelongsto]
== Rails/BelongsTo

NOTE: Requires 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-railsbelongsto]
=== Examples

[source,ruby]
----
# 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
----

[#references-railsbelongsto]
=== References

* https://guides.rubyonrails.org/5_0_release_notes.html
* https://github.com/rails/rails/pull/18937

[#railsblank]
== 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-railsblank]
=== 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-railsblank]
=== Examples

[#nilorempty_-true-_default_-railsblank]
==== NilOrEmpty: true (default)

[source,ruby]
----
# Converts usages of `nil? || empty?` to `blank?`

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

# good
foo.blank?
----

[#notpresent_-true-_default_-railsblank]
==== NotPresent: true (default)

[source,ruby]
----
# Converts usages of `!present?` to `blank?`

# bad
!foo.present?

# good
foo.blank?
----

[#unlesspresent_-true-_default_-railsblank]
==== UnlessPresent: true (default)

[source,ruby]
----
# 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-railsblank]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| NilOrEmpty
| `true`
| Boolean

| NotPresent
| `true`
| Boolean

| UnlessPresent
| `true`
| Boolean
|===

[#railsbulkchangetable]
== 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-railsbulkchangetable]
=== Examples

[source,ruby]
----
# 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
----

[source,ruby]
----
# 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-railsbulkchangetable]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Database
| `<none>`
| `mysql`, `postgresql`

| Include
| `+db/**/*.rb+`
| Array
|===

[#references-railsbulkchangetable]
=== References

* https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_table
* https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html

[#railscompactblank]
== Rails/CompactBlank

NOTE: Requires 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-railscompactblank]
=== 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-railscompactblank]
=== Examples

[source,ruby]
----
# 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!
----

[#railscontenttag]
== Rails/ContentTag

NOTE: Requires 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`

NOTE: 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-railscontenttag]
=== Examples

[source,ruby]
----
# bad
tag(:p)
tag(:br, class: 'classname')

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

[#configurable-attributes-railscontenttag]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Exclude
| `+**/app/models/**/*.rb+`, `+**/config/**/*.rb+`
| Array
|===

[#references-railscontenttag]
=== References

* https://github.com/rubocop/rubocop-rails/issues/260
* https://github.com/rails/rails/issues/25195
* https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag

[#railscreatetablewithtimestamps]
== 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.

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

[#examples-railscreatetablewithtimestamps]
=== Examples

[source,ruby]
----
# 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-railscreatetablewithtimestamps]
=== 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
|===

[#railsdangerouscolumnnames]
== 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-railsdangerouscolumnnames]
=== Examples

[source,ruby]
----
# bad
add_column :users, :save

# good
add_column :users, :saved
----

[#configurable-attributes-railsdangerouscolumnnames]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String

| Include
| `+db/**/*.rb+`
| Array
|===

[#railsdate]
== 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-railsdate]
=== Safety

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

[#examples-railsdate]
=== Examples

[#enforcedstyle_-flexible-_default_-railsdate]
==== EnforcedStyle: flexible (default)

[source,ruby]
----
# bad
Date.today

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

[#enforcedstyle_-strict-railsdate]
==== EnforcedStyle: strict

[source,ruby]
----
# bad
Date.current
Date.yesterday
Date.today

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

[#allowtotime_-true-_default_-railsdate]
==== AllowToTime: true (default)

[source,ruby]
----
# good
date.to_time
----

[#allowtotime_-false-railsdate]
==== AllowToTime: false

[source,ruby]
----
# bad
date.to_time
----

[#configurable-attributes-railsdate]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `flexible`
| `strict`, `flexible`

| AllowToTime
| `true`
| Boolean
|===

[#railsdefaultscope]
== Rails/DefaultScope

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Disabled
| Yes
| No
| 2.7
| -
|===

Looks for uses of `default_scope`.

[#examples-railsdefaultscope]
=== Examples

[source,ruby]
----
# 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
----

[#railsdelegate]
== Rails/Delegate

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Enabled
| Yes
| Always
| 0.21
| 2.30
|===

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.

It is disabled for controllers in order to keep controller actions
explicitly defined.

[#examples-railsdelegate]
=== Examples

[source,ruby]
----
# 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_-railsdelegate]
==== EnforceForPrefixed: true (default)

[source,ruby]
----
# bad
def foo_bar
  foo.bar
end

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

[#enforceforprefixed_-false-railsdelegate]
==== EnforceForPrefixed: false

[source,ruby]
----
# good
def foo_bar
  foo.bar
end

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

[#configurable-attributes-railsdelegate]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforceForPrefixed
| `true`
| Boolean

| Exclude
| `+**/app/controllers/**/*.rb+`
| Array
|===

[#railsdelegateallowblank]
== 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-railsdelegateallowblank]
=== Examples

[source,ruby]
----
# bad
delegate :foo, to: :bar, allow_blank: true

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

[#railsdeprecatedactivemodelerrorsmethods]
== 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-railsdeprecatedactivemodelerrorsmethods]
=== 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-railsdeprecatedactivemodelerrorsmethods]
=== Examples

[source,ruby]
----
# 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-railsdeprecatedactivemodelerrorsmethods]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String
|===

[#railsdotseparatedkeys]
== 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-railsdotseparatedkeys]
=== Examples

[source,ruby]
----
# 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'
----

[#references-railsdotseparatedkeys]
=== References

* https://rails.rubystyle.guide/#dot-separated-keys

[#railsduplicateassociation]
== 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-railsduplicateassociation]
=== Examples

[source,ruby]
----
# 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-railsduplicateassociation]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String
|===

[#railsduplicatescope]
== 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 expression. This
often means you copy/pasted a scope, updated the name, and forgot to change the condition.

[#examples-railsduplicatescope]
=== Examples

[source,ruby]
----
# bad
scope :visible, -> { where(visible: true) }
scope :hidden, -> { where(visible: true) }

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

[#configurable-attributes-railsduplicatescope]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String
|===

[#railsdurationarithmetic]
== 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-railsdurationarithmetic]
=== Examples

[source,ruby]
----
# 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
----

[#references-railsdurationarithmetic]
=== References

* https://rails.rubystyle.guide#duration-arithmetic

[#railsdynamicfindby]
== 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-railsdynamicfindby]
=== 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-railsdynamicfindby]
=== Examples

[source,ruby]
----
# 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_-railsdynamicfindby]
==== AllowedMethods: ['find_by_sql', 'find_by_token_for'] (default)

[source,ruby]
----
# 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_-railsdynamicfindby]
==== AllowedReceivers: ['Gem::Specification', 'page'] (default)

[source,ruby]
----
# 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-railsdynamicfindby]
=== 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
|===

[#references-railsdynamicfindby]
=== References

* https://rails.rubystyle.guide#find_by

[#railseagerevaluationlogmessage]
== 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-railseagerevaluationlogmessage]
=== Examples

[source,ruby]
----
# bad
Rails.logger.debug "The time is #{Time.zone.now}."

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

[#references-railseagerevaluationlogmessage]
=== References

* https://guides.rubyonrails.org/debugging_rails_applications.html#impact-of-logs-on-performance

[#railsenumhash]
== 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-railsenumhash]
=== Examples

[source,ruby]
----
# 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-railsenumhash]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#references-railsenumhash]
=== References

* https://rails.rubystyle.guide#enums

[#railsenumsyntax]
== Rails/EnumSyntax

NOTE: Requires Ruby version 3.0

NOTE: Requires 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-railsenumsyntax]
=== Examples

[source,ruby]
----
# bad
enum status: { active: 0, archived: 1 }, _prefix: true

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

[#configurable-attributes-railsenumsyntax]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String

| Include
| `+**/app/models/**/*.rb+`, `+**/lib/**/*.rb+`
| Array
|===

[#railsenumuniqueness]
== 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-railsenumuniqueness]
=== Examples

[source,ruby]
----
# 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-railsenumuniqueness]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#railsenv]
== Rails/Env

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Disabled
| Yes
| No
| 2.34
| -
|===

Checks for usage of `Rails.env` which can be replaced with Feature Flags

The cop does not flag `Rails.env.local?`, the built-in alias for
"development or test" introduced in Rails 7.1. Unlike per-environment
predicates such as `development?` or `production?`, `local?` expresses
the intent of guarding code that must only ever run in development or
test (sanity checks, devtools, seed data) rather than gating an
environment rollout, so a Feature Flag is not a suitable replacement.

[#examples-railsenv]
=== Examples

[source,ruby]
----
# bad
Rails.env.production? || Rails.env.development?

# good
if FeatureFlag.enabled?(:new_feature)
  # new feature code
end

# good
raise 'This should never run in production' unless Rails.env.local?
----

[#railsenvlocal]
== Rails/EnvLocal

NOTE: Requires 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-railsenvlocal]
=== Examples

[source,ruby]
----
# bad
Rails.env.development? || Rails.env.test?

# good
Rails.env.local?
----

[#railsenvironmentcomparison]
== 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-railsenvironmentcomparison]
=== Examples

[source,ruby]
----
# bad
Rails.env == 'production'
Rails.env.to_sym == :production

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

# good
Rails.env.production?
----

[#railsenvironmentvariableaccess]
== 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-railsenvironmentvariableaccess]
=== Examples

[source,ruby]
----
# good
Rails.application.config.foo
Rails.application.config.x.foo.bar
Rails.application.secrets.foo
Rails.application.config.foo = "bar"
----

[#allowreads_-false-_default_-railsenvironmentvariableaccess]
==== AllowReads: false (default)

[source,ruby]
----
# bad
ENV["FOO"]
ENV.fetch("FOO")
----

[#allowreads_-true-railsenvironmentvariableaccess]
==== AllowReads: true

[source,ruby]
----
# good
ENV["FOO"]
ENV.fetch("FOO")
----

[#allowwrites_-false-_default_-railsenvironmentvariableaccess]
==== AllowWrites: false (default)

[source,ruby]
----
# bad
ENV["FOO"] = "bar"
----

[#allowwrites_-true-railsenvironmentvariableaccess]
==== AllowWrites: true

[source,ruby]
----
# good
ENV["FOO"] = "bar"
----

[#configurable-attributes-railsenvironmentvariableaccess]
=== 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
|===

[#railsexit]
== Rails/Exit

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Enabled
| Yes
| No
| 0.41
| -
|===

Enforces that `exit` and `abort` 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-railsexit]
=== Examples

[source,ruby]
----
# bad
exit(0)

# good
raise 'a bad error has happened'
----

[#configurable-attributes-railsexit]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/**/*.rb+`, `+**/config/**/*.rb+`, `+**/lib/**/*.rb+`
| Array

| Exclude
| `+**/lib/**/*.rake+`
| Array
|===

[#railsexpandeddaterange]
== Rails/ExpandedDateRange

NOTE: Requires 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-railsexpandeddaterange]
=== Examples

[source,ruby]
----
# 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
----

[#references-railsexpandeddaterange]
=== References

* https://rails.rubystyle.guide/#date-time-range

[#railsfilepath]
== 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.

NOTE: This cop ignores leading slashes in string literal arguments for `Rails.root.join`
      and multiple slashes in string literal arguments for `Rails.root.join` and `File.join`.

[#examples-railsfilepath]
=== Examples

[#enforcedstyle_-slashes-_default_-railsfilepath]
==== EnforcedStyle: slashes (default)

[source,ruby]
----
# 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-railsfilepath]
==== EnforcedStyle: arguments

[source,ruby]
----
# 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-railsfilepath]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `slashes`
| `slashes`, `arguments`
|===

[#railsfindby]
== 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-railsfindby]
=== Examples

[source,ruby]
----
# bad
User.where(name: 'Bruce').take

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

[#ignorewherefirst_-true-_default_-railsfindby]
==== IgnoreWhereFirst: true (default)

[source,ruby]
----
# good
User.where(name: 'Bruce').first
----

[#ignorewherefirst_-false-railsfindby]
==== IgnoreWhereFirst: false

[source,ruby]
----
# bad
User.where(name: 'Bruce').first
----

[#configurable-attributes-railsfindby]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| IgnoreWhereFirst
| `true`
| Boolean
|===

[#references-railsfindby]
=== References

* https://rails.rubystyle.guide#find_by

[#railsfindbyid]
== 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-railsfindbyid]
=== Examples

[source,ruby]
----
# bad
User.where(id: id).take!
User.find_by_id!(id)
User.find_by!(id: id)

# good
User.find(id)
----

[#references-railsfindbyid]
=== References

* https://rails.rubystyle.guide/#find

[#railsfindbyorassignmentmemoization]
== Rails/FindByOrAssignmentMemoization

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| No
| Always (Unsafe)
| 2.33
| -
|===

Avoid memoizing `find_by` results with `||=`.

It is common to see code that attempts to memoize `find_by` result by `||=`,
but `find_by` may return `nil`, in which case it is not memoized as intended.

NOTE: Respecting the object shapes introduced in Ruby 3.2, instance variables used
for memoization that are initialized at object creation are ignored.

[#safety-railsfindbyorassignmentmemoization]
=== Safety

This cop is unsafe because detected `find_by` may not be Active Record's method,
or the code may have a different purpose than memoization.

[#examples-railsfindbyorassignmentmemoization]
=== Examples

[source,ruby]
----
# bad - exclusively doing memoization
def current_user
  @current_user ||= User.find_by(id: session[:user_id])
end

# good
def current_user
  return @current_user if defined?(@current_user)

  @current_user = User.find_by(id: session[:user_id])
end

# bad - method contains other code
def current_user
  @current_user ||= User.find_by(id: session[:user_id])
  @current_user.do_something
end

# good
def current_user
  if defined?(@current_user)
    @current_user
  else
    @current_user = User.find_by(id: session[:user_id])
  end
  @current_user.do_something
end
----

[#references-railsfindbyorassignmentmemoization]
=== References

* https://rails.rubystyle.guide/#find-by-memoization

[#railsfindeach]
== 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-railsfindeach]
=== 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-railsfindeach]
=== Examples

[source,ruby]
----
# bad
User.all.each

# good
User.all.find_each
----

[#allowedmethods_-__order__-railsfindeach]
==== AllowedMethods: ['order']

[source,ruby]
----
# good
User.order(:foo).each
----

[#allowedpattern_-__order__-railsfindeach]
==== AllowedPattern: ['order']

[source,ruby]
----
# good
User.order(:foo).each
----

[#configurable-attributes-railsfindeach]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| AllowedMethods
| `order`, `limit`, `select`, `lock`
| Array

| AllowedPatterns
| `[]`
| Array
|===

[#references-railsfindeach]
=== References

* https://rails.rubystyle.guide#find-each

[#railsfreezetime]
== Rails/FreezeTime

NOTE: Requires 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-railsfreezetime]
=== 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-railsfreezetime]
=== Examples

[source,ruby]
----
# 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
----

[#references-railsfreezetime]
=== References

* https://rails.rubystyle.guide/#freeze-time

[#railshasandbelongstomany]
== 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-railshasandbelongstomany]
=== Examples

[source,ruby]
----
# bad
# has_and_belongs_to_many :ingredients

# good
# has_many :ingredients, through: :recipe_ingredients
----

[#configurable-attributes-railshasandbelongstomany]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#references-railshasandbelongstomany]
=== References

* https://rails.rubystyle.guide#has-many-through

[#railshasmanyorhasonedependent]
== 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-railshasmanyorhasonedependent]
=== Examples

[source,ruby]
----
# 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-railshasmanyorhasonedependent]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#references-railshasmanyorhasonedependent]
=== References

* https://rails.rubystyle.guide#has_many-has_one-dependent-option

[#railshelperinstancevariable]
== 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 an instance variable belongs to a class,
an offense will not be registered.

[#examples-railshelperinstancevariable]
=== Examples

[source,ruby]
----
# bad
def welcome_message
  "Hello #{@user.name}"
end

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

# good
module ButtonHelper
  class Welcome
    def initialize(text:)
      @text = text
    end
  end

  def welcome(**)
    render Welcome.new(**)
  end
end
----

[#configurable-attributes-railshelperinstancevariable]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/helpers/**/*.rb+`
| Array
|===

[#railshttppositionalarguments]
== Rails/HttpPositionalArguments

NOTE: Requires 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.

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

[#examples-railshttppositionalarguments]
=== Examples

[source,ruby]
----
# bad
get :new, { user_id: 1}

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

[#configurable-attributes-railshttppositionalarguments]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/spec/**/*+`, `+**/test/**/*+`
| Array
|===

[#railshttpstatus]
== 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-railshttpstatus]
=== Examples

[#enforcedstyle_-symbolic-_default_-railshttpstatus]
==== EnforcedStyle: symbolic (default)

[source,ruby]
----
# 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-railshttpstatus]
==== EnforcedStyle: numeric

[source,ruby]
----
# 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-railshttpstatus]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `symbolic`
| `numeric`, `symbolic`
|===

[#railshttpstatusnameconsistency]
== Rails/HttpStatusNameConsistency

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| Yes
| Always
| 2.34
| -
|===

Enforces consistency by using the current HTTP status names.

[#examples-railshttpstatusnameconsistency]
=== Examples

[source,ruby]
----
# bad
render json: { error: "Invalid data" }, status: :unprocessable_entity
head :payload_too_large

# good
render json: { error: "Invalid data" }, status: :unprocessable_content
head :content_too_large
----

[#configurable-attributes-railshttpstatusnameconsistency]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String

| Include
| `+**/app/controllers/**/*.rb+`
| Array
|===

[#railsi18nlazylookup]
== 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-railsi18nlazylookup]
=== Examples

[#enforcedstyle_-lazy-_default_-railsi18nlazylookup]
==== EnforcedStyle: lazy (default)

[source,ruby]
----
# 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-railsi18nlazylookup]
==== EnforcedStyle: explicit

[source,ruby]
----
# 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-railsi18nlazylookup]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `lazy`
| `lazy`, `explicit`

| Include
| `+**/app/controllers/**/*.rb+`
| Array
|===

[#references-railsi18nlazylookup]
=== References

* https://rails.rubystyle.guide/#lazy-lookup
* https://guides.rubyonrails.org/i18n.html#lazy-lookup

[#railsi18nlocaleassignment]
== 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-railsi18nlocaleassignment]
=== Examples

[source,ruby]
----
# bad
I18n.locale = :fr

# good
I18n.with_locale(:fr) do
end
----

[#configurable-attributes-railsi18nlocaleassignment]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/spec/**/*.rb+`, `+**/test/**/*.rb+`
| Array
|===

[#railsi18nlocaletexts]
== 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-railsi18nlocaletexts]
=== Examples

[source,ruby]
----
# 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 PostsController < ApplicationController
  def update
    # ...
    redirect_back_or_to root_path, alert: "Failed to update!"
  end
end

# good
# config/locales/en.yml
# en:
#   posts:
#     update:
#       failure: "Failed to update!"

class PostsController < ApplicationController
  def update
    # ...
    redirect_back_or_to root_path, alert: t(".failure")
  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
----

[#references-railsi18nlocaletexts]
=== References

* https://rails.rubystyle.guide/#locale-texts

[#railsignoredcolumnsassignment]
== 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-railsignoredcolumnsassignment]
=== Examples

[source,ruby]
----
# 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
----

[#references-railsignoredcolumnsassignment]
=== References

* https://rails.rubystyle.guide/#append-ignored-columns

[#railsignoredskipactionfilteroption]
== 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-railsignoredskipactionfilteroption]
=== Examples

[source,ruby]
----
# 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
----

[source,ruby]
----
# 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-railsignoredskipactionfilteroption]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/controllers/**/*.rb+`, `+**/app/mailers/**/*.rb+`
| Array
|===

[#references-railsignoredskipactionfilteroption]
=== References

* https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options

[#railsindexby]
== 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-railsindexby]
=== Examples

[source,ruby]
----
# 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) }
----

[#railsindexwith]
== Rails/IndexWith

NOTE: Requires Rails version 6.0

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Enabled
| Yes
| Always (Unsafe)
| 2.5
| 2.33
|===

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.

[#safety-railsindexwith]
=== Safety

This cop is marked as unsafe autocorrection, because `nil.to_h` returns {}
but `nil.with_index` throws `NoMethodError`. Therefore, autocorrection is not
compatible if the receiver is nil.

[#examples-railsindexwith]
=== Examples

[source,ruby]
----
# 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) }
----

[#railsinquiry]
== 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-railsinquiry]
=== Examples

[source,ruby]
----
# 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'
----

[#references-railsinquiry]
=== References

* https://rails.rubystyle.guide/#inquiry

[#railsinverseof]
== 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-railsinverseof]
=== Examples

[source,ruby]
----
# good
class Blog < ApplicationRecord
  has_many :posts
end

class Post < ApplicationRecord
  belongs_to :blog
end
----

[source,ruby]
----
# 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
----

[source,ruby]
----
# 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
----

[source,ruby]
----
# 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_-railsinverseof]
==== IgnoreScopes: false (default)

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

[#ignorescopes_-true-railsinverseof]
==== IgnoreScopes: true

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

[#configurable-attributes-railsinverseof]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| IgnoreScopes
| `false`
| Boolean

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#references-railsinverseof]
=== References

* https://guides.rubyonrails.org/association_basics.html#bi-directional-associations
* https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses

[#railslexicallyscopedactionfilter]
== 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-railslexicallyscopedactionfilter]
=== 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-railslexicallyscopedactionfilter]
=== Examples

[source,ruby]
----
# 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
----

[source,ruby]
----
# 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
----

[source,ruby]
----
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-railslexicallyscopedactionfilter]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/controllers/**/*.rb+`, `+**/app/mailers/**/*.rb+`
| Array
|===

[#references-railslexicallyscopedactionfilter]
=== References

* https://rails.rubystyle.guide#lexically-scoped-action-filter

[#railslinktoblank]
== 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-railslinktoblank]
=== Examples

[source,ruby]
----
# 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'
----

[#references-railslinktoblank]
=== References

* https://mathiasbynens.github.io/rel-noopener/
* https://html.spec.whatwg.org/multipage/links.html#link-type-noopener
* https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer

[#railsmailername]
== 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-railsmailername]
=== Safety

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

[#examples-railsmailername]
=== Examples

[source,ruby]
----
# bad
class User < ActionMailer::Base
end

class User < ApplicationMailer
end

# good
class UserMailer < ActionMailer::Base
end

class UserMailer < ApplicationMailer
end
----

[#configurable-attributes-railsmailername]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/mailers/**/*.rb+`
| Array
|===

[#references-railsmailername]
=== References

* https://rails.rubystyle.guide/#mailer-name

[#railsmatchroute]
== 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-railsmatchroute]
=== Examples

[source,ruby]
----
# 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-railsmatchroute]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/config/routes.rb+`, `+**/config/routes/**/*.rb+`
| Array
|===

[#references-railsmatchroute]
=== References

* https://rails.rubystyle.guide/#no-match-routes

[#railsmigrationclassname]
== 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-railsmigrationclassname]
=== Examples

[source,ruby]
----
# db/migrate/20220224111111_create_users.rb

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

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

[#configurable-attributes-railsmigrationclassname]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+db/**/*.rb+`
| Array
|===

[#railsmultipleroutepaths]
== Rails/MultipleRoutePaths

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| Yes
| Always
| 2.29
| -
|===

Checks for mapping a route with multiple paths, which is deprecated and will be removed in Rails 8.1.

[#examples-railsmultipleroutepaths]
=== Examples

[source,ruby]
----
# bad
get '/users', '/other_path', to: 'users#index'

# good
get '/users', to: 'users#index'
get '/other_path', to: 'users#index'
----

[#configurable-attributes-railsmultipleroutepaths]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String

| Include
| `+**/config/routes.rb+`, `+**/config/routes/**/*.rb+`
| Array
|===

[#railsnegateinclude]
== 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-railsnegateinclude]
=== Safety

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

[#examples-railsnegateinclude]
=== Examples

[source,ruby]
----
# bad
!array.include?(2)
!hash.include?(:key)

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

[#references-railsnegateinclude]
=== References

* https://rails.rubystyle.guide#exclude

[#railsnotnullcolumn]
== 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-railsnotnullcolumn]
=== Examples

[source,ruby]
----
# 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-railsnotnullcolumn]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Database
| `<none>`
| `mysql`

| Include
| `+db/**/*.rb+`
| Array
|===

[#railsorderarguments]
== Rails/OrderArguments

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| No
| Always (Unsafe)
| 2.33
| -
|===

Prefer symbol arguments over strings in `order` method.

[#safety-railsorderarguments]
=== Safety

Cop is unsafe because the receiver might not be an Active Record query.

[#examples-railsorderarguments]
=== Examples

[source,ruby]
----
# bad
User.order('name')
User.order('name DESC')

# good
User.order(:name)
User.order(name: :desc)
----

[#references-railsorderarguments]
=== References

* https://rails.rubystyle.guide/#order-arguments

[#railsorderbyid]
== 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.

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

[#examples-railsorderbyid]
=== Examples

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

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

[#references-railsorderbyid]
=== References

* https://rails.rubystyle.guide/#order-by-id

[#railsoutput]
== 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-railsoutput]
=== 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-railsoutput]
=== Examples

[source,ruby]
----
# bad
puts 'A debug message'
pp 'A debug message'
print 'A debug message'

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

[#configurable-attributes-railsoutput]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/**/*.rb+`, `+**/config/**/*.rb+`, `+db/**/*.rb+`, `+**/lib/**/*.rb+`
| Array
|===

[#railsoutputsafety]
== 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-railsoutputsafety]
=== Examples

[source,ruby]
----
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>"
----

[#railspick]
== Rails/Pick

NOTE: Requires 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-railspick]
=== 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.

See: https://github.com/rubocop/rubocop-rails/pull/249

[#examples-railspick]
=== Examples

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

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

[#references-railspick]
=== References

* https://rails.rubystyle.guide#pick

[#railspluck]
== Rails/Pluck

NOTE: Requires 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.

NOTE: 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.

[source,ruby]
----
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
----

If a method call has no receiver, like `do_something { users.map { |user| user[:foo] }`,
it is not considered part of an iteration and will be detected.

[#safety-railspluck]
=== Safety

This cop is unsafe because model can use column aliases.

[source,ruby]
----
# 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-railspluck]
=== Examples

[source,ruby]
----
# 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)
----

[#references-railspluck]
=== References

* https://rails.rubystyle.guide#pluck

[#railspluckid]
== 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-railspluckid]
=== Safety

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

[#examples-railspluckid]
=== Examples

[source,ruby]
----
# 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
----

[#references-railspluckid]
=== References

* https://rails.rubystyle.guide/#ids

[#railspluckinwhere]
== 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-railspluckinwhere]
=== 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-railspluckinwhere]
=== Examples

[source,ruby]
----
# 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_-railspluckinwhere]
==== EnforcedStyle: conservative (default)

[source,ruby]
----
# good
Post.where(user_id: active_users.pluck(:id))
----

[#enforcedstyle_-aggressive-railspluckinwhere]
==== EnforcedStyle: aggressive

[source,ruby]
----
# bad
Post.where(user_id: active_users.pluck(:id))
----

[#configurable-attributes-railspluckinwhere]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `conservative`
| `conservative`, `aggressive`
|===

[#railspluralizationgrammar]
== 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-railspluralizationgrammar]
=== Examples

[source,ruby]
----
# bad
3.day.ago
1.months.ago
5.megabyte
1.gigabytes

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

[#railspresence]
== Rails/Presence

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Enabled
| Yes
| Always
| 0.52
| 2.34
|===

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

[#examples-railspresence]
=== Examples

[source,ruby]
----
# bad
a.present? ? a : nil

# bad
!a.present? ? nil : a

# bad
a.blank? ? nil : a

# bad
!a.blank? ? a : nil

# good
a.presence
----

[source,ruby]
----
# bad
a.present? ? a : b

# bad
!a.present? ? b : a

# bad
a.blank? ? b : a

# bad
!a.blank? ? a : b

# good
a.presence || b
----

[source,ruby]
----
# bad
a.present? ? a.foo : nil

# bad
!a.present? ? nil : a.foo

# bad
a.blank? ? nil : a.foo

# bad
!a.blank? ? a.foo : nil

# good
a.presence&.foo
----

[source,ruby]
----
# good
a.present? ? a[1] : nil

# good
a[:key] = value if a.present?

# good
a.present? ? a > 1 : nil

# good
a <= 0 if a.present?

# good
a << "bar" if a.present?
----

[#railspresent]
== 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-railspresent]
=== Examples

[#notnilandnotempty_-true-_default_-railspresent]
==== NotNilAndNotEmpty: true (default)

[source,ruby]
----
# Converts usages of `!nil? && !empty?` to `present?`

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

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

# good
foo.present?
----

[#notblank_-true-_default_-railspresent]
==== NotBlank: true (default)

[source,ruby]
----
# Converts usages of `!blank?` to `present?`

# bad
!foo.blank?

# bad
not foo.blank?

# good
foo.present?
----

[#unlessblank_-true-_default_-railspresent]
==== UnlessBlank: true (default)

[source,ruby]
----
# Converts usages of `unless blank?` to `if present?`

# bad
something unless foo.blank?

# good
something if foo.present?
----

[#configurable-attributes-railspresent]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| NotNilAndNotEmpty
| `true`
| Boolean

| NotBlank
| `true`
| Boolean

| UnlessBlank
| `true`
| Boolean
|===

[#railsrakeenvironment]
== 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-railsrakeenvironment]
=== 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-railsrakeenvironment]
=== Examples

[source,ruby]
----
# bad
task :foo do
  do_something
end

# good
task foo: :environment do
  do_something
end
----

[#configurable-attributes-railsrakeenvironment]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/Rakefile+`, `+**/*.rake+`
| Array

| Exclude
| `+**/lib/capistrano/tasks/**/*.rake+`
| Array
|===

[#railsreadwriteattribute]
== 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-railsreadwriteattribute]
=== Examples

[source,ruby]
----
# bad
x = read_attribute(:attr)
write_attribute(:attr, val)

# good
x = self[:attr]
self[:attr] = val
----

[source,ruby]
----
# good
def foo
  bar || read_attribute(:foo)
end
----

[#configurable-attributes-railsreadwriteattribute]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#references-railsreadwriteattribute]
=== References

* https://rails.rubystyle.guide#read-attribute

[#railsredirectbackorto]
== Rails/RedirectBackOrTo

NOTE: Requires Rails version 7.0

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| Yes
| Always
| 2.34
| -
|===

Checks for uses of `redirect_back(fallback_location: ...)` and
suggests using `redirect_back_or_to(...)` instead.

`redirect_back(fallback_location: ...)` was soft deprecated in Rails 7.0 and
`redirect_back_or_to` was introduced as a replacement.

[#examples-railsredirectbackorto]
=== Examples

[source,ruby]
----
# bad
redirect_back(fallback_location: root_path)

# good
redirect_back_or_to(root_path)

# bad
redirect_back(fallback_location: root_path, allow_other_host: false)

# good
redirect_back_or_to(root_path, allow_other_host: false)
----

[#configurable-attributes-railsredirectbackorto]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String
|===

[#railsredundantactiverecordallmethod]
== 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-railsredundantactiverecordallmethod]
=== Safety

This cop is unsafe because false positives will occur if the receiver is not an Active Record object.

[#examples-railsredundantactiverecordallmethod]
=== Examples

[source,ruby]
----
# 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_-railsredundantactiverecordallmethod]
==== AllowedReceivers: ['ActionMailer::Preview', 'ActiveSupport::TimeZone'] (default)

[source,ruby]
----
# good
ActionMailer::Preview.all.first
ActiveSupport::TimeZone.all.first
----

[#configurable-attributes-railsredundantactiverecordallmethod]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| AllowedReceivers
| `ActionMailer::Preview`, `ActiveSupport::TimeZone`
| Array
|===

[#references-railsredundantactiverecordallmethod]
=== References

* https://rails.rubystyle.guide/#redundant-all

[#railsredundantallownil]
== 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-railsredundantallownil]
=== Examples

[source,ruby]
----
# 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-railsredundantallownil]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#railsredundantforeignkey]
== 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-railsredundantforeignkey]
=== Examples

[source,ruby]
----
# 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
----

[#railsredundantpresencevalidationonbelongsto]
== Rails/RedundantPresenceValidationOnBelongsTo

NOTE: Requires 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-railsredundantpresencevalidationonbelongsto]
=== Safety

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

[#examples-railsredundantpresencevalidationonbelongsto]
=== Examples

[source,ruby]
----
# 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
----

[#railsredundantreceiverinwithoptions]
== 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-railsredundantreceiverinwithoptions]
=== Examples

[source,ruby]
----
# 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
----

[source,ruby]
----
# 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
----

[#railsredundanttravelback]
== Rails/RedundantTravelBack

NOTE: Requires 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-railsredundanttravelback]
=== Examples

[source,ruby]
----
# 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-railsredundanttravelback]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/spec/**/*.rb+`, `+**/test/**/*.rb+`
| Array
|===

[#railsreflectionclassname]
== 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-railsreflectionclassname]
=== Safety

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

[#examples-railsreflectionclassname]
=== Examples

[source,ruby]
----
# bad
has_many :accounts, class_name: Account
has_many :accounts, class_name: Account.name

# good
has_many :accounts, class_name: 'Account'
----

[#railsrefutemethods]
== 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-railsrefutemethods]
=== Examples

[#enforcedstyle_-assert_not-_default_-railsrefutemethods]
==== EnforcedStyle: assert_not (default)

[source,ruby]
----
# 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-railsrefutemethods]
==== EnforcedStyle: refute

[source,ruby]
----
# 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-railsrefutemethods]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `assert_not`
| `assert_not`, `refute`

| Include
| `+**/test/**/*+`
| Array
|===

[#railsrelativedateconstant]
== 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-railsrelativedateconstant]
=== Safety

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

[#examples-railsrelativedateconstant]
=== Examples

[source,ruby]
----
# 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
----

[#railsrenderinline]
== 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-railsrenderinline]
=== Examples

[source,ruby]
----
# 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
----

[#references-railsrenderinline]
=== References

* https://rails.rubystyle.guide/#inline-rendering

[#railsrenderplaintext]
== 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-railsrenderplaintext]
=== Examples

[source,ruby]
----
# 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_-railsrenderplaintext]
==== ContentTypeCompatibility: true (default)

[source,ruby]
----
# good - sets MIME type to `text/html`
render text: 'Ruby!'
----

[#contenttypecompatibility_-false-railsrenderplaintext]
==== ContentTypeCompatibility: false

[source,ruby]
----
# bad - sets MIME type to `text/html`
render text: 'Ruby!'
----

[#configurable-attributes-railsrenderplaintext]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| ContentTypeCompatibility
| `true`
| Boolean
|===

[#references-railsrenderplaintext]
=== References

* https://rails.rubystyle.guide/#plain-text-rendering

[#railsrequestreferer]
== 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-railsrequestreferer]
=== Examples

[#enforcedstyle_-referer-_default_-railsrequestreferer]
==== EnforcedStyle: referer (default)

[source,ruby]
----
# bad
request.referrer

# good
request.referer
----

[#enforcedstyle_-referrer-railsrequestreferer]
==== EnforcedStyle: referrer

[source,ruby]
----
# bad
request.referer

# good
request.referrer
----

[#configurable-attributes-railsrequestreferer]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `referer`
| `referer`, `referrer`
|===

[#railsrequiredependency]
== Rails/RequireDependency

NOTE: Requires 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`.

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

[#examples-railsrequiredependency]
=== Examples

[source,ruby]
----
# bad
require_dependency 'some_lib'
----

[#references-railsrequiredependency]
=== References

* https://guides.rubyonrails.org/autoloading_and_reloading_constants.html

[#railsresponseparsedbody]
== Rails/ResponseParsedBody

NOTE: Requires 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-railsresponseparsedbody]
=== 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-railsresponseparsedbody]
=== Examples

[source,ruby]
----
# bad
JSON.parse(response.body)
Nokogiri::HTML(response.body)
Nokogiri::HTML4(response.body)
Nokogiri::HTML5(response.body)
Nokogiri::HTML.parse(response.body)
Nokogiri::HTML4.parse(response.body)
Nokogiri::HTML5.parse(response.body)
Nokogiri::HTML::Document.parse(response.body)
Nokogiri::HTML4::Document.parse(response.body)
Nokogiri::HTML5::Document.parse(response.body)

# good
response.parsed_body
----

[#configurable-attributes-railsresponseparsedbody]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/spec/controllers/**/*.rb+`, `+**/spec/requests/**/*.rb+`, `+**/test/controllers/**/*.rb+`, `+**/test/integration/**/*.rb+`
| Array
|===

[#railsreversiblemigration]
== 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-railsreversiblemigration]
=== Examples

[source,ruby]
----
# 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
----

[source,ruby]
----
# drop_table

# bad
def change
  drop_table :users
end

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

[source,ruby]
----
# 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
----

[source,ruby]
----
# remove_column

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

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

[source,ruby]
----
# 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
----

[source,ruby]
----
# 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
----

[source,ruby]
----
# 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
----

[source,ruby]
----
# 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-railsreversiblemigration]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+db/**/*.rb+`
| Array
|===

[#references-railsreversiblemigration]
=== References

* https://rails.rubystyle.guide#reversible-migration
* https://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html

[#railsreversiblemigrationmethoddefinition]
== 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-railsreversiblemigrationmethoddefinition]
=== Examples

[source,ruby]
----
# 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-railsreversiblemigrationmethoddefinition]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+db/**/*.rb+`
| Array
|===

[#railsrootjoinchain]
== 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-railsrootjoinchain]
=== Examples

[source,ruby]
----
# 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')
----

[#railsrootpathnamemethods]
== 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-railsrootpathnamemethods]
=== 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-railsrootpathnamemethods]
=== Examples

[source,ruby]
----
# 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")
----

[#railsrootpublicpath]
== 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-railsrootpublicpath]
=== Examples

[source,ruby]
----
# 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')
----

[#railssafenavigation]
== Rails/SafeNavigation

NOTE: 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-railssafenavigation]
=== Examples

[#converttry_-false-_default_-railssafenavigation]
==== ConvertTry: false (default)

[source,ruby]
----
# bad
foo.try!(:bar)
foo.try!(:bar, baz)
foo.try!(:bar) { |e| e.baz }
foo.try!(&:bar)

foo.try!(:[], 0)
foo.try!(:==, baz)

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

foo&.bar
foo&.bar(baz)
foo&.bar { |e| e.baz }
foo&.[](0)
foo&.==(baz)
----

[#converttry_-true-railssafenavigation]
==== ConvertTry: true

[source,ruby]
----
# bad
foo.try!(:bar)
foo.try!(:bar, baz)
foo.try!(:bar) { |e| e.baz }
foo.try!(&:bar)
foo.try(:bar)
foo.try(:bar, baz)
foo.try(:bar) { |e| e.baz }
foo.try(&:bar)
foo.try(:[], 0)
foo.try(:==, baz)

# good
foo&.bar
foo&.bar(baz)
foo&.bar { |e| e.baz }
foo&.[](0)
foo&.==(baz)
----

[#configurable-attributes-railssafenavigation]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| ConvertTry
| `false`
| Boolean
|===

[#railssafenavigationwithblank]
== 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-railssafenavigationwithblank]
=== 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:

[source,ruby]
----
foo&.blank? #=> nil
foo.blank? #=> true
----

[#examples-railssafenavigationwithblank]
=== Examples

[source,ruby]
----
# bad
do_something if foo&.blank?
do_something unless foo&.blank?

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

[#railssavebang]
== 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-railssavebang]
=== 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.

[source,ruby]
----
# Original code
def update_attributes
end

update_attributes

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

update
----

[#examples-railssavebang]
=== Examples

[source,ruby]
----
# 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_-railssavebang]
==== AllowImplicitReturn: true (default)

[source,ruby]
----
# good
users.each { |u| u.save }

def save_user
  user.save
end
----

[#allowimplicitreturn_-false-railssavebang]
==== AllowImplicitReturn: false

[source,ruby]
----
# 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__-railssavebang]
==== AllowedReceivers: ['merchant.customers', 'Service::Mailer']

[source,ruby]
----
# 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-railssavebang]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| AllowImplicitReturn
| `true`
| Boolean

| AllowedReceivers
| `[]`
| Array
|===

[#references-railssavebang]
=== References

* https://rails.rubystyle.guide#save-bang

[#railsschemacomment]
== 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-railsschemacomment]
=== Examples

[source,ruby]
----
# 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
----

[#railsscopeargs]
== 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-railsscopeargs]
=== Examples

[source,ruby]
----
# bad
scope :something, where(something: true)

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

[#configurable-attributes-railsscopeargs]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#railsselectmap]
== 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-railsselectmap]
=== 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-railsselectmap]
=== Examples

[source,ruby]
----
# bad
Model.select(:column_name).map(&:column_name)

# good
Model.pluck(:column_name)
----

[#railsshorti18n]
== 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-railsshorti18n]
=== Examples

[source,ruby]
----
# bad
I18n.translate :key
I18n.localize Time.now

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

[#enforcedstyle_-conservative-_default_-railsshorti18n]
==== EnforcedStyle: conservative (default)

[source,ruby]
----
# good
translate :key
localize Time.now
t :key
l Time.now
----

[#enforcedstyle_-aggressive-railsshorti18n]
==== EnforcedStyle: aggressive

[source,ruby]
----
# bad
translate :key
localize Time.now

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

[#configurable-attributes-railsshorti18n]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `conservative`
| `conservative`, `aggressive`
|===

[#references-railsshorti18n]
=== References

* https://rails.rubystyle.guide/#short-i18n

[#railsskipsmodelvalidations]
== 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-railsskipsmodelvalidations]
=== Safety

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

[#examples-railsskipsmodelvalidations]
=== Examples

[source,ruby]
----
# 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__-railsskipsmodelvalidations]
==== AllowedMethods: ["touch"]

[source,ruby]
----
# bad
DiscussionBoard.decrement_counter(:post_count, 5)
DiscussionBoard.increment_counter(:post_count, 5)
person.toggle :active

# good
user.touch
----

[#configurable-attributes-railsskipsmodelvalidations]
=== 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
|===

[#references-railsskipsmodelvalidations]
=== References

* https://guides.rubyonrails.org/active_record_validations.html#skipping-validations

[#railssquishedsqlheredocs]
== 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-railssquishedsqlheredocs]
=== 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-railssquishedsqlheredocs]
=== Examples

[source,ruby]
----
# 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
----

[#references-railssquishedsqlheredocs]
=== References

* https://rails.rubystyle.guide/#squished-heredocs

[#railsstripheredoc]
== Rails/StripHeredoc

NOTE: 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-railsstripheredoc]
=== Examples

[source,ruby]
----
# bad
<<EOS.strip_heredoc
  some text
EOS

# bad
<<-EOS.strip_heredoc
  some text
EOS

# good
<<~EOS
  some text
EOS
----

[#references-railsstripheredoc]
=== References

* https://rails.rubystyle.guide/#prefer-squiggly-heredoc

[#railsstrongparametersexpect]
== Rails/StrongParametersExpect

NOTE: Requires Rails version 8.0

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| Yes
| Always (Unsafe)
| 2.29
| -
|===

Enforces the use of `ActionController::Parameters#expect` as a method for strong parameter handling.

In the following cases, `params[:key]` is treated as a key that is expected to be passed from the HTTP client,
and the cop detects it using the `expect` method.

- Method calls on `params[:key]` without comparison methods, methods that are safe to call
  on `nil` (such as `to_i`, `to_s`, or `is_a?`), key-check methods such as `key?`,
  collection methods such as `keys`, `merge`, or `slice`, or block-style calls such as
  `params[:key].each { ... }` or `params[:key].map(&:to_s)`
- Passing `params[:key]` as an argument to finder methods that raise on missing records
- Strong parameter methods using `require` or `permit`

Other cases are not detected, as they are cases where `params[:key]` may not be passed from the HTTP client.

[#safety-railsstrongparametersexpect]
=== Safety

This cop's autocorrection is considered unsafe because there are cases where the HTTP status may change
from 500 to 400 when handling invalid parameters. This change, however, reflects an intentional
incompatibility introduced for valid reasons by the `expect` method, which aligns better with
strong parameter conventions.

It is also unsafe because `expect` is stricter about the structure of the parameters than
`require`/`permit`. Nested attributes that hold an array of records need an extra array wrapper,
such as `expect(user: [{ pets_attributes: [[:name]] }])`. The cop cannot tell a single nested hash
from an array of nested hashes, so it always generates the single-hash form, which can turn
a previously successful request into a failure.

It is also unsafe when `params[:key]` is passed to a finder method such as `find`, because
`find` accepts an array of IDs. `Model.find(params[:id])` loads every record for an array of IDs,
but the corrected `Model.find(params.expect(:id))` raises `ActionController::ParameterMissing`
for an array value, since `expect` requires a scalar. The cop cannot tell a scalar ID from
an array of IDs, so the autocorrection can turn a previously successful request into a failure.

[#examples-railsstrongparametersexpect]
=== Examples

[source,ruby]
----
# bad
params[:key].do_something

# good
params.expect(:key).do_something

# bad
Model.find(params[:id])

# good
Model.find(params.expect(:id))

# bad
Model.find_by!(key: params[:key])

# good
Model.find_by!(key: params.expect(:key))

# bad
params.require(:user).permit(:name, :age)
params.permit(user: [:name, :age]).require(:user)

# good
params.expect(user: [:name, :age])
----

[#configurable-attributes-railsstrongparametersexpect]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/controllers/**/*.rb+`
| Array
|===

[#references-railsstrongparametersexpect]
=== References

* https://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-expect

[#railstablenameassignment]
== 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-railstablenameassignment]
=== Examples

[source,ruby]
----
# bad
self.table_name = 'some_table_name'
self.table_name = :some_other_name
----

[#configurable-attributes-railstablenameassignment]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#references-railstablenameassignment]
=== References

* https://rails.rubystyle.guide/#keep-ar-defaults

[#railsthreestatebooleancolumn]
== 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-railsthreestatebooleancolumn]
=== Examples

[source,ruby]
----
# 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-railsthreestatebooleancolumn]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+db/**/*.rb+`
| Array
|===

[#references-railsthreestatebooleancolumn]
=== References

* https://rails.rubystyle.guide/#three-state-boolean

[#railstimezone]
== 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-railstimezone]
=== Safety

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

[#examples-railstimezone]
=== Examples

[source,ruby]
----
# 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_-railstimezone]
==== EnforcedStyle: flexible (default)

[source,ruby]
----
# `flexible` allows usage of `in_time_zone` instead of `zone`.

# good
Time.at(timestamp).in_time_zone
----

[#enforcedstyle_-strict-railstimezone]
==== EnforcedStyle: strict

[source,ruby]
----
# `strict` means that `Time` should be used with `zone`.

# bad
Time.at(timestamp).in_time_zone
----

[#configurable-attributes-railstimezone]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `flexible`
| `strict`, `flexible`

| Exclude
| `+**/*.gemspec+`
| Array
|===

[#references-railstimezone]
=== References

* https://rails.rubystyle.guide#time
* https://danilenko.org/2012/7/6/rails_timezones

[#railstimezoneassignment]
== 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-railstimezoneassignment]
=== Examples

[source,ruby]
----
# bad
Time.zone = 'EST'

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

[#configurable-attributes-railstimezoneassignment]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/spec/**/*.rb+`, `+**/test/**/*.rb+`
| Array
|===

[#references-railstimezoneassignment]
=== References

* https://thoughtbot.com/blog/its-about-time-zones

[#railstoformatteds]
== Rails/ToFormattedS

NOTE: Requires 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-railstoformatteds]
=== Examples

[#enforcedstyle_-to_fs-_default_-railstoformatteds]
==== EnforcedStyle: to_fs (default)

[source,ruby]
----
# bad
time.to_formatted_s(:db)

# good
time.to_fs(:db)
----

[#enforcedstyle_-to_formatted_s-railstoformatteds]
==== EnforcedStyle: to_formatted_s

[source,ruby]
----
# bad
time.to_fs(:db)

# good
time.to_formatted_s(:db)
----

[#configurable-attributes-railstoformatteds]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `to_fs`
| `to_fs`, `to_formatted_s`
|===

[#references-railstoformatteds]
=== References

* https://rails.rubystyle.guide/#prefer-to-fs

[#railstoswithargument]
== Rails/ToSWithArgument

NOTE: Requires 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-railstoswithargument]
=== Safety

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

[#examples-railstoswithargument]
=== Examples

[source,ruby]
----
# bad
obj.to_s(:delimited)

# good
obj.to_formatted_s(:delimited)
----

[#railstoplevelhashwithindifferentaccess]
== Rails/TopLevelHashWithIndifferentAccess

NOTE: Requires 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-railstoplevelhashwithindifferentaccess]
=== Examples

[source,ruby]
----
# bad
HashWithIndifferentAccess.new(foo: 'bar')

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

[#configurable-attributes-railstoplevelhashwithindifferentaccess]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String
|===

[#references-railstoplevelhashwithindifferentaccess]
=== References

* https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#top-level-hashwithindifferentaccess-is-soft-deprecated

[#railstransactionexitstatement]
== 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`.

NOTE: 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-railstransactionexitstatement]
=== Examples

[source,ruby]
----
# 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__-railstransactionexitstatement]
==== TransactionMethods: ["custom_transaction"]

[source,ruby]
----
# bad
CustomModel.custom_transaction do
  return if user.active?
end
----

[#configurable-attributes-railstransactionexitstatement]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| TransactionMethods
| `[]`
| Array
|===

[#references-railstransactionexitstatement]
=== References

* https://github.com/rails/rails/commit/15aa4200e083

[#railsuniqbeforepluck]
== 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-railsuniqbeforepluck]
=== Safety

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

[#examples-railsuniqbeforepluck]
=== Examples

[#enforcedstyle_-conservative-_default_-railsuniqbeforepluck]
==== EnforcedStyle: conservative (default)

[source,ruby]
----
# bad - redundantly fetches duplicate values
Album.pluck(:band_name).uniq

# good
Album.distinct.pluck(:band_name)
----

[#enforcedstyle_-aggressive-railsuniqbeforepluck]
==== EnforcedStyle: aggressive

[source,ruby]
----
# 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-railsuniqbeforepluck]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `conservative`
| `conservative`, `aggressive`
|===

[#railsuniquevalidationwithoutindex]
== 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-railsuniquevalidationwithoutindex]
=== Examples

[source,ruby]
----
# 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-railsuniquevalidationwithoutindex]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#railsunknownenv]
== 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-railsunknownenv]
=== Examples

[source,ruby]
----
# bad
Rails.env.proudction?
Rails.env == 'proudction'
Rails.env != 'proudction'

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

[source,ruby]
----
# bad
case Rails.env
when 'proudction'
  do_something
end

# good
case Rails.env
when 'production'
  do_something
end
----

[#configurable-attributes-railsunknownenv]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String

| Environments
| `development`, `test`, `production`
| Array
|===

[#railsunusedignoredcolumns]
== 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.

IMPORTANT: 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-railsunusedignoredcolumns]
=== Examples

[source,ruby]
----
# bad
class User < ApplicationRecord
  self.ignored_columns = [:already_removed_column]
end

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

[#configurable-attributes-railsunusedignoredcolumns]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#railsunusedrendercontent]
== 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-railsunusedrendercontent]
=== Examples

[source,ruby]
----
# bad
render 'foo', status: :continue
render status: 100, plain: 'Ruby!'

# good
head :continue
head 100
----

[#configurable-attributes-railsunusedrendercontent]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String
|===

[#railsvalidation]
== 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-railsvalidation]
=== Examples

[source,ruby]
----
# 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-railsvalidation]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Include
| `+**/app/models/**/*.rb+`
| Array
|===

[#railswhereequals]
== 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-railswhereequals]
=== Safety

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

[#examples-railswhereequals]
=== Examples

[source,ruby]
----
# 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' })
----

[#references-railswhereequals]
=== References

* https://rails.rubystyle.guide/#hash-conditions

[#railswhereexists]
== 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-railswhereexists]
=== Safety

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

[source,ruby]
----
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-railswhereexists]
=== Examples

[#enforcedstyle_-exists-_default_-railswhereexists]
==== EnforcedStyle: exists (default)

[source,ruby]
----
# 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-railswhereexists]
==== EnforcedStyle: where

[source,ruby]
----
# 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-railswhereexists]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| EnforcedStyle
| `exists`
| `exists`, `where`
|===

[#railswheremissing]
== Rails/WhereMissing

NOTE: Requires 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-railswheremissing]
=== Examples

[source,ruby]
----
# bad
Post.left_joins(:author).where(authors: { id: nil })

# good
Post.where.missing(:author)
----

[#references-railswheremissing]
=== References

* https://rails.rubystyle.guide/#finding-missing-relationship-records

[#railswherenot]
== 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-railswherenot]
=== Examples

[source,ruby]
----
# 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' })
----

[#references-railswherenot]
=== References

* https://rails.rubystyle.guide/#hash-conditions

[#railswherenotwithmultipleconditions]
== 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-railswherenotwithmultipleconditions]
=== Examples

[source,ruby]
----
# 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-railswherenotwithmultipleconditions]
=== Configurable attributes

|===
| Name | Default value | Configurable values

| Severity
| `warning`
| String
|===

[#references-railswherenotwithmultipleconditions]
=== References

* https://rails.rubystyle.guide/#where-not-with-multiple-attributes

[#railswhererange]
== Rails/WhereRange

NOTE: Requires Ruby version 2.6

NOTE: Requires 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-railswhererange]
=== 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-railswhererange]
=== Examples

[source,ruby]
----
# 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)
----

[#references-railswhererange]
=== References

* https://rails.rubystyle.guide/#where-ranges
