Skip to content

Commit 3331b65

Browse files
authored
Merge pull request #574 from MaximeDucheneS/add_table_name_cop
Add `TableName` cop
2 parents 97faf22 + 9e73089 commit 3331b65

File tree

8 files changed

+203
-0
lines changed

8 files changed

+203
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@
531531
[@skryukov]: https://github.com/skryukov
532532
[@johnsyweb]: https://github.com/johnsyweb
533533
[@theunraveler]: https://github.com/theunraveler
534+
[@MaximeDucheneS]: https://github.com/MaximeDucheneS
534535
[@pirj]: https://github.com/pirj
535536
[@vitormd]: https://github.com/vitormd
536537
[@mattmccormick]: https://github.com/mattmccormick
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#574](https://github.com/rubocop/rubocop-rails/pull/574): Add new `Rails/TableNameAssignment` cop. ([@MaximeDucheneS][])

config/default.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,15 @@ Rails/SquishedSQLHeredocs:
868868
# to be preserved in order to work, thus auto-correction is not safe.
869869
SafeAutoCorrect: false
870870

871+
Rails/TableNameAssignment:
872+
Description: >-
873+
Do not use `self.table_name =`. Use Inflections or `table_name_prefix` instead.
874+
StyleGuide: 'https://rails.rubystyle.guide/#keep-ar-defaults'
875+
Enabled: false
876+
VersionAdded: '<<next>>'
877+
Include:
878+
- app/models/**/*.rb
879+
871880
Rails/TimeZone:
872881
Description: 'Checks the correct usage of time zone aware methods.'
873882
StyleGuide: 'https://rails.rubystyle.guide#time'

docs/modules/ROOT/pages/cops.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ based on the https://rails.rubystyle.guide/[Rails Style Guide].
105105
* xref:cops_rails.adoc#railsshorti18n[Rails/ShortI18n]
106106
* xref:cops_rails.adoc#railsskipsmodelvalidations[Rails/SkipsModelValidations]
107107
* xref:cops_rails.adoc#railssquishedsqlheredocs[Rails/SquishedSQLHeredocs]
108+
* xref:cops_rails.adoc#railstablenameassignment[Rails/TableNameAssignment]
108109
* xref:cops_rails.adoc#railstimezone[Rails/TimeZone]
109110
* xref:cops_rails.adoc#railstimezoneassignment[Rails/TimeZoneAssignment]
110111
* xref:cops_rails.adoc#railsuniqbeforepluck[Rails/UniqBeforePluck]

docs/modules/ROOT/pages/cops_rails.adoc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4922,6 +4922,59 @@ SQL
49224922

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

4925+
== Rails/TableNameAssignment
4926+
4927+
|===
4928+
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
4929+
4930+
| Disabled
4931+
| Yes
4932+
| No
4933+
| 2.13
4934+
| -
4935+
|===
4936+
4937+
This cop enforces the absence of explicit table name assignment.
4938+
4939+
`self.table_name=` should only be used for very good reasons,
4940+
such as not having control over the database, or working
4941+
on a legacy project.
4942+
4943+
If you need to change how your model's name is translated to
4944+
a table name, you may want to look at Inflections:
4945+
https://api.rubyonrails.org/classes/ActiveSupport/Inflector/Inflections.html
4946+
4947+
If you wish to add a prefix in front of your model, or wish to change
4948+
the default prefix, `self.table_name_prefix` might better suit your needs:
4949+
https://api.rubyonrails.org/classes/ActiveRecord/ModelSchema.html#method-c-table_name_prefix-3D
4950+
4951+
4952+
STI base classes named `Base` are ignored by this cop.
4953+
For more information: https://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html
4954+
4955+
=== Examples
4956+
4957+
[source,ruby]
4958+
----
4959+
# bad
4960+
self.table_name = 'some_table_name'
4961+
self.table_name = :some_other_name
4962+
----
4963+
4964+
=== Configurable attributes
4965+
4966+
|===
4967+
| Name | Default value | Configurable values
4968+
4969+
| Exclude
4970+
| `spec/**/*`, `db/migrate/*.rb`
4971+
| Array
4972+
|===
4973+
4974+
=== References
4975+
4976+
* https://rails.rubystyle.guide/#keep-ar-defaults
4977+
49254978
== Rails/TimeZone
49264979

49274980
|===
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module Rails
6+
# This cop enforces the absence of explicit table name assignment.
7+
#
8+
# `self.table_name=` should only be used for very good reasons,
9+
# such as not having control over the database, or working
10+
# on a legacy project.
11+
#
12+
# If you need to change how your model's name is translated to
13+
# a table name, you may want to look at Inflections:
14+
# https://api.rubyonrails.org/classes/ActiveSupport/Inflector/Inflections.html
15+
#
16+
# If you wish to add a prefix in front of your model, or wish to change
17+
# the default prefix, `self.table_name_prefix` might better suit your needs:
18+
# https://api.rubyonrails.org/classes/ActiveRecord/ModelSchema.html#method-c-table_name_prefix-3D
19+
#
20+
# STI base classes named `Base` are ignored by this cop.
21+
# For more information: https://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html
22+
#
23+
# @example
24+
# # bad
25+
# self.table_name = 'some_table_name'
26+
# self.table_name = :some_other_name
27+
class TableNameAssignment < Base
28+
include ActiveRecordHelper
29+
30+
MSG = 'Do not use `self.table_name =`.'
31+
32+
def_node_matcher :base_class?, <<~PATTERN
33+
(class (const ... :Base) ...)
34+
PATTERN
35+
36+
def on_class(class_node)
37+
return if base_class?(class_node)
38+
39+
find_set_table_name(class_node).each { |node| add_offense(node) }
40+
end
41+
end
42+
end
43+
end
44+
end

lib/rubocop/cop/rails_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
require_relative 'rails/short_i18n'
105105
require_relative 'rails/skips_model_validations'
106106
require_relative 'rails/squished_sql_heredocs'
107+
require_relative 'rails/table_name_assignment'
107108
require_relative 'rails/time_zone'
108109
require_relative 'rails/time_zone_assignment'
109110
require_relative 'rails/uniq_before_pluck'
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::Rails::TableNameAssignment, :config do
4+
context 'when table_name is defined' do
5+
context 'when string' do
6+
it 'registers an offense' do
7+
expect_offense(<<~RUBY)
8+
class AModule::SomeModel < ApplicationRecord
9+
self.table_name = 'some_other_table_name'
10+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use `self.table_name =`.
11+
end
12+
RUBY
13+
end
14+
end
15+
16+
context 'when string = foo' do
17+
it 'registers an offense' do
18+
expect_offense(<<~RUBY)
19+
class AModule::SomeModel < ApplicationRecord
20+
self.table_name = 'foo'
21+
^^^^^^^^^^^^^^^^^^^^^^^ Do not use `self.table_name =`.
22+
end
23+
RUBY
24+
end
25+
end
26+
27+
context 'when symbol' do
28+
it 'registers an offense' do
29+
expect_offense(<<~RUBY)
30+
class AModule::SomeModel < ApplicationRecord
31+
self.table_name = :some_other_table_name
32+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use `self.table_name =`.
33+
end
34+
RUBY
35+
end
36+
end
37+
38+
context 'when symbol = :foo' do
39+
it 'registers an offense' do
40+
expect_offense(<<~RUBY)
41+
class AModule::SomeModel < ApplicationRecord
42+
self.table_name = :foo
43+
^^^^^^^^^^^^^^^^^^^^^^ Do not use `self.table_name =`.
44+
end
45+
RUBY
46+
end
47+
end
48+
end
49+
50+
context 'when table_name is not defined' do
51+
it 'does not register an offense' do
52+
expect_no_offenses(<<~RUBY)
53+
class AModule::SomeModel < ApplicationRecord
54+
has_many :other_thing
55+
has_one :parent_thing
56+
end
57+
RUBY
58+
end
59+
end
60+
61+
# Case for STI base classes
62+
context 'when class is named `Base`' do
63+
context 'when class is declared with module' do
64+
it 'does not register an offense' do
65+
expect_no_offenses(<<~RUBY)
66+
class A::B::Base < ApplicationRecord
67+
has_many :other_thing
68+
has_one :parent_thing
69+
70+
self.table_name = 'special_table_name'
71+
end
72+
RUBY
73+
end
74+
end
75+
76+
context 'when class is declared within module' do
77+
it 'does not register an offense' do
78+
expect_no_offenses(<<~RUBY)
79+
module A
80+
module B
81+
class Base < ApplicationRecord
82+
has_many :other_thing
83+
has_one :parent_thing
84+
85+
self.table_name = 'special_table_name'
86+
end
87+
end
88+
end
89+
RUBY
90+
end
91+
end
92+
end
93+
end

0 commit comments

Comments
 (0)