Skip to content

Commit 5819ca1

Browse files
author
Yorick Peterse
committed
Added Cop to blacklist polymorphic associations
One should really use a separate table instead of using polymorphic associations. See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11168 for more information.
1 parent 44d65c3 commit 5819ca1

20 files changed

+89
-27
lines changed

app/models/award_emoji.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class AwardEmoji < ActiveRecord::Base
55
include Participable
66
include GhostUser
77

8-
belongs_to :awardable, polymorphic: true
8+
belongs_to :awardable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
99
belongs_to :user
1010

1111
validates :awardable, :user, presence: true

app/models/deployment.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class Deployment < ActiveRecord::Base
44
belongs_to :project, required: true, validate: true
55
belongs_to :environment, required: true, validate: true
66
belongs_to :user
7-
belongs_to :deployable, polymorphic: true
7+
belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
88

99
validates :sha, presence: true
1010
validates :ref, presence: true

app/models/event.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class Event < ActiveRecord::Base
4747

4848
belongs_to :author, class_name: "User"
4949
belongs_to :project
50-
belongs_to :target, polymorphic: true
50+
belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
5151

5252
# For Hash only
5353
serialize :data # rubocop:disable Cop/ActiverecordSerialize

app/models/label_link.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class LabelLink < ActiveRecord::Base
22
include Importable
33

4-
belongs_to :target, polymorphic: true
4+
belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
55
belongs_to :label
66

77
validates :target, presence: true, unless: :importing?

app/models/member.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class Member < ActiveRecord::Base
88

99
belongs_to :created_by, class_name: "User"
1010
belongs_to :user
11-
belongs_to :source, polymorphic: true
11+
belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
1212

1313
delegate :name, :username, :email, to: :user, prefix: true
1414

app/models/note.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Note < ActiveRecord::Base
4141
participant :author
4242

4343
belongs_to :project
44-
belongs_to :noteable, polymorphic: true, touch: true
44+
belongs_to :noteable, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations
4545
belongs_to :author, class_name: "User"
4646
belongs_to :updated_by, class_name: "User"
4747
belongs_to :last_edited_by, class_name: 'User'

app/models/notification_setting.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class NotificationSetting < ActiveRecord::Base
44
default_value_for :level, NotificationSetting.levels[:global]
55

66
belongs_to :user
7-
belongs_to :source, polymorphic: true
7+
belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
88
belongs_to :project, foreign_key: 'source_id'
99

1010
validates :user, presence: true

app/models/redirect_route.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class RedirectRoute < ActiveRecord::Base
2-
belongs_to :source, polymorphic: true
2+
belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
33

44
validates :source, presence: true
55

app/models/route.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class Route < ActiveRecord::Base
2-
belongs_to :source, polymorphic: true
2+
belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
33

44
validates :source, presence: true
55

app/models/sent_notification.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ class SentNotification < ActiveRecord::Base
22
serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize
33

44
belongs_to :project
5-
belongs_to :noteable, polymorphic: true
5+
belongs_to :noteable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
66
belongs_to :recipient, class_name: "User"
77

88
validates :project, :recipient, presence: true

app/models/subscription.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class Subscription < ActiveRecord::Base
22
belongs_to :user
33
belongs_to :project
4-
belongs_to :subscribable, polymorphic: true
4+
belongs_to :subscribable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
55

66
validates :user, :subscribable, presence: true
77

app/models/todo.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class Todo < ActiveRecord::Base
2222
belongs_to :author, class_name: "User"
2323
belongs_to :note
2424
belongs_to :project
25-
belongs_to :target, polymorphic: true, touch: true
25+
belongs_to :target, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations
2626
belongs_to :user
2727

2828
delegate :name, :email, to: :author, prefix: true, allow_nil: true

app/models/upload.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ class Upload < ActiveRecord::Base
22
# Upper limit for foreground checksum processing
33
CHECKSUM_THRESHOLD = 100.megabytes
44

5-
belongs_to :model, polymorphic: true
5+
belongs_to :model, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
66

77
validates :size, presence: true
88
validates :path, presence: true

app/models/user_agent_detail.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class UserAgentDetail < ActiveRecord::Base
2-
belongs_to :subject, polymorphic: true
2+
belongs_to :subject, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
33

44
validates :user_agent, :ip_address, :subject_id, :subject_type, presence: true
55

rubocop/cop/activerecord_serialize.rb

+5-11
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
1+
require_relative '../model_helpers'
2+
13
module RuboCop
24
module Cop
35
# Cop that prevents the use of `serialize` in ActiveRecord models.
46
class ActiverecordSerialize < RuboCop::Cop::Cop
7+
include ModelHelpers
8+
59
MSG = 'Do not store serialized data in the database, use separate columns and/or tables instead'.freeze
610

711
def on_send(node)
8-
return unless in_models?(node)
12+
return unless in_model?(node)
913

1014
add_offense(node, :selector) if node.children[1] == :serialize
1115
end
12-
13-
def models_path
14-
File.join(Dir.pwd, 'app', 'models')
15-
end
16-
17-
def in_models?(node)
18-
path = node.location.expression.source_buffer.name
19-
20-
path.start_with?(models_path)
21-
end
2216
end
2317
end
2418
end
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
require_relative '../model_helpers'
2+
3+
module RuboCop
4+
module Cop
5+
# Cop that prevents the use of polymorphic associations
6+
class PolymorphicAssociations < RuboCop::Cop::Cop
7+
include ModelHelpers
8+
9+
MSG = 'Do not use polymorphic associations, use separate tables instead'.freeze
10+
11+
def on_send(node)
12+
return unless in_model?(node)
13+
return unless node.children[1] == :belongs_to
14+
15+
node.children.last.each_node(:pair) do |pair|
16+
key_name = pair.children[0].children[0]
17+
18+
add_offense(pair, :expression) if key_name == :polymorphic
19+
end
20+
end
21+
end
22+
end
23+
end

rubocop/model_helpers.rb

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module RuboCop
2+
module ModelHelpers
3+
# Returns true if the given node originated from the models directory.
4+
def in_model?(node)
5+
path = node.location.expression.source_buffer.name
6+
models_path = File.join(Dir.pwd, 'app', 'models')
7+
8+
path.start_with?(models_path)
9+
end
10+
end
11+
end

rubocop/rubocop.rb

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
require_relative 'cop/gem_fetcher'
33
require_relative 'cop/activerecord_serialize'
44
require_relative 'cop/redirect_with_status'
5+
require_relative 'cop/polymorphic_associations'
56
require_relative 'cop/migration/add_column'
67
require_relative 'cop/migration/add_column_with_default_to_large_table'
78
require_relative 'cop/migration/add_concurrent_foreign_key'

spec/rubocop/cop/activerecord_serialize_spec.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
context 'inside the app/models directory' do
1212
it 'registers an offense when serialize is used' do
13-
allow(cop).to receive(:in_models?).and_return(true)
13+
allow(cop).to receive(:in_model?).and_return(true)
1414

1515
inspect_source(cop, 'serialize :foo')
1616

@@ -23,7 +23,7 @@
2323

2424
context 'outside the app/models directory' do
2525
it 'does nothing' do
26-
allow(cop).to receive(:in_models?).and_return(false)
26+
allow(cop).to receive(:in_model?).and_return(false)
2727

2828
inspect_source(cop, 'serialize :foo')
2929

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
require 'spec_helper'
2+
require 'rubocop'
3+
require 'rubocop/rspec/support'
4+
require_relative '../../../rubocop/cop/polymorphic_associations'
5+
6+
describe RuboCop::Cop::PolymorphicAssociations do
7+
include CopHelper
8+
9+
subject(:cop) { described_class.new }
10+
11+
context 'inside the app/models directory' do
12+
it 'registers an offense when polymorphic: true is used' do
13+
allow(cop).to receive(:in_model?).and_return(true)
14+
15+
inspect_source(cop, 'belongs_to :foo, polymorphic: true')
16+
17+
aggregate_failures do
18+
expect(cop.offenses.size).to eq(1)
19+
expect(cop.offenses.map(&:line)).to eq([1])
20+
end
21+
end
22+
end
23+
24+
context 'outside the app/models directory' do
25+
it 'does nothing' do
26+
allow(cop).to receive(:in_model?).and_return(false)
27+
28+
inspect_source(cop, 'belongs_to :foo, polymorphic: true')
29+
30+
expect(cop.offenses).to be_empty
31+
end
32+
end
33+
end

0 commit comments

Comments
 (0)