Skip to content

Commit

Permalink
Merge pull request #574 from MaximeDucheneS/add_table_name_cop
Browse files Browse the repository at this point in the history
Add `TableName` cop
  • Loading branch information
koic committed Mar 7, 2022
2 parents 97faf22 + 9e73089 commit 3331b65
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -531,6 +531,7 @@
[@skryukov]: https://github.com/skryukov
[@johnsyweb]: https://github.com/johnsyweb
[@theunraveler]: https://github.com/theunraveler
[@MaximeDucheneS]: https://github.com/MaximeDucheneS
[@pirj]: https://github.com/pirj
[@vitormd]: https://github.com/vitormd
[@mattmccormick]: https://github.com/mattmccormick
Expand Down
1 change: 1 addition & 0 deletions changelog/new_table_name_assignment_cop.md
@@ -0,0 +1 @@
* [#574](https://github.com/rubocop/rubocop-rails/pull/574): Add new `Rails/TableNameAssignment` cop. ([@MaximeDucheneS][])
9 changes: 9 additions & 0 deletions config/default.yml
Expand Up @@ -868,6 +868,15 @@ Rails/SquishedSQLHeredocs:
# to be preserved in order to work, thus auto-correction is not safe.
SafeAutoCorrect: false

Rails/TableNameAssignment:
Description: >-
Do not use `self.table_name =`. Use Inflections or `table_name_prefix` instead.
StyleGuide: 'https://rails.rubystyle.guide/#keep-ar-defaults'
Enabled: false
VersionAdded: '<<next>>'
Include:
- app/models/**/*.rb

Rails/TimeZone:
Description: 'Checks the correct usage of time zone aware methods.'
StyleGuide: 'https://rails.rubystyle.guide#time'
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Expand Up @@ -105,6 +105,7 @@ based on the https://rails.rubystyle.guide/[Rails Style Guide].
* xref:cops_rails.adoc#railsshorti18n[Rails/ShortI18n]
* xref:cops_rails.adoc#railsskipsmodelvalidations[Rails/SkipsModelValidations]
* xref:cops_rails.adoc#railssquishedsqlheredocs[Rails/SquishedSQLHeredocs]
* xref:cops_rails.adoc#railstablenameassignment[Rails/TableNameAssignment]
* xref:cops_rails.adoc#railstimezone[Rails/TimeZone]
* xref:cops_rails.adoc#railstimezoneassignment[Rails/TimeZoneAssignment]
* xref:cops_rails.adoc#railsuniqbeforepluck[Rails/UniqBeforePluck]
Expand Down
53 changes: 53 additions & 0 deletions docs/modules/ROOT/pages/cops_rails.adoc
Expand Up @@ -4922,6 +4922,59 @@ SQL

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

== Rails/TableNameAssignment

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

| Disabled
| Yes
| No
| 2.13
| -
|===

This cop 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

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

=== Configurable attributes

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

| Exclude
| `spec/**/*`, `db/migrate/*.rb`
| Array
|===

=== References

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

== Rails/TimeZone

|===
Expand Down
44 changes: 44 additions & 0 deletions lib/rubocop/cop/rails/table_name_assignment.rb
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Rails
# This cop 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
#
# @example
# # bad
# self.table_name = 'some_table_name'
# self.table_name = :some_other_name
class TableNameAssignment < Base
include ActiveRecordHelper

MSG = 'Do not use `self.table_name =`.'

def_node_matcher :base_class?, <<~PATTERN
(class (const ... :Base) ...)
PATTERN

def on_class(class_node)
return if base_class?(class_node)

find_set_table_name(class_node).each { |node| add_offense(node) }
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/rails_cops.rb
Expand Up @@ -104,6 +104,7 @@
require_relative 'rails/short_i18n'
require_relative 'rails/skips_model_validations'
require_relative 'rails/squished_sql_heredocs'
require_relative 'rails/table_name_assignment'
require_relative 'rails/time_zone'
require_relative 'rails/time_zone_assignment'
require_relative 'rails/uniq_before_pluck'
Expand Down
93 changes: 93 additions & 0 deletions spec/rubocop/cop/rails/table_name_assignment_spec.rb
@@ -0,0 +1,93 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Rails::TableNameAssignment, :config do
context 'when table_name is defined' do
context 'when string' do
it 'registers an offense' do
expect_offense(<<~RUBY)
class AModule::SomeModel < ApplicationRecord
self.table_name = 'some_other_table_name'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use `self.table_name =`.
end
RUBY
end
end

context 'when string = foo' do
it 'registers an offense' do
expect_offense(<<~RUBY)
class AModule::SomeModel < ApplicationRecord
self.table_name = 'foo'
^^^^^^^^^^^^^^^^^^^^^^^ Do not use `self.table_name =`.
end
RUBY
end
end

context 'when symbol' do
it 'registers an offense' do
expect_offense(<<~RUBY)
class AModule::SomeModel < ApplicationRecord
self.table_name = :some_other_table_name
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use `self.table_name =`.
end
RUBY
end
end

context 'when symbol = :foo' do
it 'registers an offense' do
expect_offense(<<~RUBY)
class AModule::SomeModel < ApplicationRecord
self.table_name = :foo
^^^^^^^^^^^^^^^^^^^^^^ Do not use `self.table_name =`.
end
RUBY
end
end
end

context 'when table_name is not defined' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
class AModule::SomeModel < ApplicationRecord
has_many :other_thing
has_one :parent_thing
end
RUBY
end
end

# Case for STI base classes
context 'when class is named `Base`' do
context 'when class is declared with module' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
class A::B::Base < ApplicationRecord
has_many :other_thing
has_one :parent_thing
self.table_name = 'special_table_name'
end
RUBY
end
end

context 'when class is declared within module' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
module A
module B
class Base < ApplicationRecord
has_many :other_thing
has_one :parent_thing
self.table_name = 'special_table_name'
end
end
end
RUBY
end
end
end
end

0 comments on commit 3331b65

Please sign in to comment.