rubocop-discourse 2.1.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +3 -0
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/config/default.yml +16 -1
- data/default.yml +1 -1
- data/lib/rubocop/cop/discourse/no_add_reference_active_record_migrations.rb +84 -0
- data/lib/rubocop/cop/discourse/no_nokogiri_html_fragment.rb +31 -0
- data/lib/rubocop/cop/discourse/no_reset_column_information_in_migrations.rb +23 -0
- data/rubocop-discourse.gemspec +2 -1
- data/spec/lib/rubocop/cop/no_add_reference_active_record_migrations_spec.rb +50 -0
- data/spec/lib/rubocop/cop/no_reset_column_information_migrations_spec.rb +35 -0
- data/spec/spec_helper.rb +15 -0
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4393a2ad64c2662ce3c1dcdb1608501b93b6c13bc31b9a7a2a25b2c8a1404e89
|
4
|
+
data.tar.gz: eabd1def336f6290d8a694c209256827585b4a28baa2f0a84fa74f65cfc76f07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40a5a38b088a3ab91c3a155ab4a2bcf78ed44ecd62a4fc98ccbf2b21cba5aac0d6d906b460e5be293f48dace6be3295d73841b0c147f9c4296002b094971d95b
|
7
|
+
data.tar.gz: 72b08959eb26c04941c3fc972a3d9725f6465355f02c0fb1913dfa7826a76ca42ad716327a1a40ea541eebadbfc01715783dc2467fa255d204cff7d09c36f213
|
data/.github/workflows/ci.yml
CHANGED
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/config/default.yml
CHANGED
@@ -7,9 +7,24 @@ Discourse/NoChdir:
|
|
7
7
|
Discourse/NoTimeNewWithoutArgs:
|
8
8
|
Enabled: true
|
9
9
|
|
10
|
-
Discourse/
|
10
|
+
Discourse/NoURIEscapeEncode:
|
11
11
|
Enabled: true
|
12
12
|
|
13
|
+
Discourse/NoAddReferenceOrAliasesActiveRecordMigration:
|
14
|
+
Enabled: true
|
15
|
+
Include:
|
16
|
+
- "**/db/migrate/*"
|
17
|
+
- "**/db/post_migrate/*"
|
18
|
+
|
19
|
+
Discourse/NoNokogiriHtmlFragment:
|
20
|
+
Enabled: true
|
21
|
+
|
22
|
+
Discourse/NoResetColumnInformationInMigrations:
|
23
|
+
Enabled: false
|
24
|
+
Include:
|
25
|
+
- "**/db/migrate/*"
|
26
|
+
- "**/db/post_migrate/*"
|
27
|
+
|
13
28
|
# Specs
|
14
29
|
|
15
30
|
Discourse/NoDirectMultisiteManipulation:
|
data/default.yml
CHANGED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Discourse
|
6
|
+
# The methods:
|
7
|
+
#
|
8
|
+
# * add_reference
|
9
|
+
# * add_belongs_to
|
10
|
+
# * t.references
|
11
|
+
# * t.belongs_to
|
12
|
+
#
|
13
|
+
# in ActiveRecord migrations are magic, and they all do
|
14
|
+
# some unexpected things in the background. For example, by default
|
15
|
+
# add_reference adds an index at the same time, but not concurrently,
|
16
|
+
# which is a nightmare for large tables.
|
17
|
+
#
|
18
|
+
# Instead, inside a disable_ddl_transaction! migration we should create
|
19
|
+
# the new column (with any defaults and options required) and the index
|
20
|
+
# concurrently using hand-written SQL. This also allows us to handle
|
21
|
+
# IF NOT EXISTS cases, which enable re-runnable migrations. Also we
|
22
|
+
# can pick a better name for the index at the same time.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
#
|
26
|
+
# # bad
|
27
|
+
# def up
|
28
|
+
# add_reference :posts, :image_upload
|
29
|
+
# # or add_belongs_to :posts, :image_upload
|
30
|
+
# # or t.references :image_upload when doing create_table do |t|
|
31
|
+
# # or t.belongs_to :image_upload when doing create_table do |t|
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# # good
|
35
|
+
# disable_ddl_transaction!
|
36
|
+
# def up
|
37
|
+
# execute <<~SQL
|
38
|
+
# ALTER TABLE posts
|
39
|
+
# ADD COLUMN IF NOT EXISTS image_upload_id bigint
|
40
|
+
# SQL
|
41
|
+
#
|
42
|
+
# execute <<~SQL
|
43
|
+
# CREATE INDEX CONCURRENTLY IF NOT EXISTS
|
44
|
+
# index_posts_on_image_upload_id ON posts USING btree (image_upload_id)
|
45
|
+
# SQL
|
46
|
+
# end
|
47
|
+
class NoAddReferenceOrAliasesActiveRecordMigration < Cop
|
48
|
+
MSG = <<~MSG
|
49
|
+
AR methods add_reference, add_belongs_to, t.references, and t.belongs_to are
|
50
|
+
high-risk for large tables and have too many background magic operations.
|
51
|
+
Instead, write a disable_ddl_transactions! migration and write custom SQL to
|
52
|
+
add the new column and CREATE INDEX CONCURRENTLY. Use the IF NOT EXISTS clause
|
53
|
+
to make the migration re-runnable if it fails partway through.
|
54
|
+
MSG
|
55
|
+
|
56
|
+
def_node_matcher :using_add_reference?, <<-MATCHER
|
57
|
+
(send nil? :add_reference ...)
|
58
|
+
MATCHER
|
59
|
+
|
60
|
+
def_node_matcher :using_add_belongs_to?, <<-MATCHER
|
61
|
+
(send nil? :add_belongs_to ...)
|
62
|
+
MATCHER
|
63
|
+
|
64
|
+
def_node_matcher :using_t_references?, <<-MATCHER
|
65
|
+
(send (lvar _) :references ...)
|
66
|
+
MATCHER
|
67
|
+
|
68
|
+
def_node_matcher :using_t_belongs_to?, <<-MATCHER
|
69
|
+
(send (lvar _) :belongs_to ...)
|
70
|
+
MATCHER
|
71
|
+
|
72
|
+
def on_send(node)
|
73
|
+
return if [
|
74
|
+
using_add_reference?(node),
|
75
|
+
using_add_belongs_to?(node),
|
76
|
+
using_t_references?(node),
|
77
|
+
using_t_belongs_to?(node)
|
78
|
+
].none?
|
79
|
+
add_offense(node, message: MSG)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Discourse
|
6
|
+
# Do not use Nokogiri::HTML.fragment
|
7
|
+
# Instead use Nokogiri::HTML5.fragment, which is using Nokogumbo parser
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# Nokogiri::HTML.fragment("<p>test</p>")
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# Nokogiri::HTML5.fragment("<p>test</p>")
|
15
|
+
class NoNokogiriHtmlFragment < Cop
|
16
|
+
MSG = "Nokogiri::HTML.fragment is deprecated and should not be used."
|
17
|
+
|
18
|
+
def_node_matcher :using_nokogiri_html_fragment?, <<-MATCHER
|
19
|
+
(send
|
20
|
+
(const
|
21
|
+
(const nil? :Nokogiri) :HTML) :fragment ...)
|
22
|
+
MATCHER
|
23
|
+
|
24
|
+
def on_send(node)
|
25
|
+
return if !using_nokogiri_html_fragment?(node)
|
26
|
+
add_offense(node, message: MSG)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Discourse
|
6
|
+
# Do not use `ActiveRecord::ModelSchema.reset_column_information` in
|
7
|
+
# migrations. The method is not thread safe and we run migrations
|
8
|
+
# concurrently for multisites. Also, we don't encourage the use of
|
9
|
+
# ActiveRecord methods in migrations and prefer to write SQL directly.
|
10
|
+
class NoResetColumnInformationInMigrations < Cop
|
11
|
+
MSG = "ActiveRecord::ModelSchema.reset_column_information is not thread-safe " \
|
12
|
+
"and we run migrations concurrently on multisite clusters. Using this " \
|
13
|
+
"method also means ActiveRecord methods are being used in migration "\
|
14
|
+
"which is discouraged at Discourse. Instead, you should write SQL in your migrations instead."
|
15
|
+
|
16
|
+
def on_send(node)
|
17
|
+
return if node.method_name != :reset_column_information
|
18
|
+
add_offense(node, message: MSG)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/rubocop-discourse.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "rubocop-discourse"
|
5
|
-
s.version = "2.
|
5
|
+
s.version = "2.2.0"
|
6
6
|
s.summary = "Custom rubocop cops used by Discourse"
|
7
7
|
s.authors = ["David Taylor"]
|
8
8
|
s.files = `git ls-files`.split($/)
|
@@ -13,4 +13,5 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.add_runtime_dependency "rubocop-rspec", ">= 1.39.0"
|
14
14
|
|
15
15
|
s.add_development_dependency "rake", "~> 13.0"
|
16
|
+
s.add_development_dependency "rspec"
|
16
17
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe RuboCop::Cop::Discourse::NoAddReferenceOrAliasesActiveRecordMigration, :config do
|
6
|
+
subject(:cop) { described_class.new(config) }
|
7
|
+
let(:config) do
|
8
|
+
RuboCop::Config.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "raises an offense if add_reference is used, with or without arguments" do
|
12
|
+
inspect_source(<<~RUBY)
|
13
|
+
add_reference :posts, :users, foreign_key: true, null: false
|
14
|
+
RUBY
|
15
|
+
expect(cop.offenses.first.message).to eq(described_class::MSG)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "raises an offense if add_belongs_to is used, with or without arguments" do
|
19
|
+
inspect_source(<<~RUBY)
|
20
|
+
add_belongs_to :posts, :users, foreign_key: true, null: false
|
21
|
+
RUBY
|
22
|
+
expect(cop.offenses.first.message).to eq(described_class::MSG)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises an offense if t.references, or any variable.references is used, with or without arguments" do
|
26
|
+
inspect_source(<<~RUBY)
|
27
|
+
create_table do |t|
|
28
|
+
t.references :topic, null: false
|
29
|
+
end
|
30
|
+
create_table do |mytable|
|
31
|
+
mytable.references :topic, null: false
|
32
|
+
end
|
33
|
+
RUBY
|
34
|
+
expect(cop.offenses.count).to eq(2)
|
35
|
+
expect(cop.offenses.first.message).to eq(described_class::MSG)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "raises an offense if t.belongs_to, or any variable.belongs_to is used, with or without arguments" do
|
39
|
+
inspect_source(<<~RUBY)
|
40
|
+
create_table do |t|
|
41
|
+
t.belongs_to :topic, null: false
|
42
|
+
end
|
43
|
+
create_table do |mytable|
|
44
|
+
mytable.belongs_to :topic, null: false
|
45
|
+
end
|
46
|
+
RUBY
|
47
|
+
expect(cop.offenses.count).to eq(2)
|
48
|
+
expect(cop.offenses.first.message).to eq(described_class::MSG)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe RuboCop::Cop::Discourse::NoResetColumnInformationInMigrations, :config do
|
6
|
+
subject(:cop) { described_class.new(config) }
|
7
|
+
|
8
|
+
let(:config) do
|
9
|
+
RuboCop::Config.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "raises an offense if reset_column_information is used" do
|
13
|
+
inspect_source(<<~RUBY)
|
14
|
+
class SomeMigration < ActiveRecord::Migration[6.0]
|
15
|
+
def up
|
16
|
+
Post.reset_column_information
|
17
|
+
end
|
18
|
+
end
|
19
|
+
RUBY
|
20
|
+
|
21
|
+
expect(cop.offenses.first.message).to eq(described_class::MSG)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raise an offense if reset_column_information is used without AR model" do
|
25
|
+
inspect_source(<<~RUBY)
|
26
|
+
class SomeMigration < ActiveRecord::Migration[6.0]
|
27
|
+
def up
|
28
|
+
"post".classify.constantize.reset_column_information
|
29
|
+
end
|
30
|
+
end
|
31
|
+
RUBY
|
32
|
+
|
33
|
+
expect(cop.offenses.first.message).to eq(described_class::MSG)
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
Bundler.setup
|
5
|
+
|
6
|
+
# Require supporting files exposed for testing.
|
7
|
+
require 'rubocop'
|
8
|
+
require 'rubocop/rspec/support'
|
9
|
+
|
10
|
+
require 'rubocop-discourse' # and any other gems you need
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.include RuboCop::RSpec::ExpectOffense
|
14
|
+
# some (optional) config here
|
15
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-discourse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Taylor
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '13.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description:
|
56
70
|
email:
|
57
71
|
executables: []
|
@@ -68,9 +82,12 @@ files:
|
|
68
82
|
- config/default.yml
|
69
83
|
- default.yml
|
70
84
|
- lib/rubocop-discourse.rb
|
85
|
+
- lib/rubocop/cop/discourse/no_add_reference_active_record_migrations.rb
|
71
86
|
- lib/rubocop/cop/discourse/no_chdir.rb
|
72
87
|
- lib/rubocop/cop/discourse/no_direct_multisite_manipulation.rb
|
73
88
|
- lib/rubocop/cop/discourse/no_json_parse_response.rb
|
89
|
+
- lib/rubocop/cop/discourse/no_nokogiri_html_fragment.rb
|
90
|
+
- lib/rubocop/cop/discourse/no_reset_column_information_in_migrations.rb
|
74
91
|
- lib/rubocop/cop/discourse/no_time_new_without_args.rb
|
75
92
|
- lib/rubocop/cop/discourse/no_uri_escape_encode.rb
|
76
93
|
- lib/rubocop/cop/discourse/time_eq_matcher.rb
|
@@ -80,6 +97,9 @@ files:
|
|
80
97
|
- rubocop-core.yml
|
81
98
|
- rubocop-discourse.gemspec
|
82
99
|
- rubocop-rspec.yml
|
100
|
+
- spec/lib/rubocop/cop/no_add_reference_active_record_migrations_spec.rb
|
101
|
+
- spec/lib/rubocop/cop/no_reset_column_information_migrations_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
83
103
|
homepage:
|
84
104
|
licenses:
|
85
105
|
- MIT
|