rubocop-discourse 2.1.2 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 938e8b3d2af662cc5d3a325fa317cf727164287036c47203481262f0fb00839c
4
- data.tar.gz: d7c47414ffccdd5e240f152a9994c1eba1bf96497f8d5ec96f5e11ec1972a8e7
3
+ metadata.gz: bced25894fc049474ea8907181b9866a6a020f6ae7e0fd75661922ce14953221
4
+ data.tar.gz: 74b1eeb9043546aa8b4a64207ddf08b2f8fd9f7d60e16dd919afdaba56ca2825
5
5
  SHA512:
6
- metadata.gz: a8f462667a26681e75d9a492f7b07c836d249ba6f653acf200c66513ec890c7d5b97fddec986525994f1968e24563382d8504da8cd2ff6a070edde3e9b317a22
7
- data.tar.gz: 2f79d3ff37c7689fcea71e84ff4dffed726db49cca9063a79a212acd35a281b57231aa844093b63d50fcb2d8b89e1cf0e4477740b49e4dfdaab99a4fd3ed7b63
6
+ metadata.gz: 6ffe6e341f332a027a2e6231585d164405ef31d18b35b678635fbda8ee694b6219deb904faaae4ffe0604af51391907cd9c7333c4b7bae537145ee872afc9ff2
7
+ data.tar.gz: 942fd4cc5909d43f269a0f8ef9f96f7268945c5c59289ea87f0c1c261d4e113df13adcf8e0cff3e86ba25393be9a404c46f1aad759f09a20b8b689270ff0fe4f
@@ -19,7 +19,6 @@ jobs:
19
19
  uses: actions/setup-ruby@v1
20
20
  with:
21
21
  ruby-version: 2.6
22
- architecture: 'x64'
23
22
 
24
23
  - name: Setup bundler
25
24
  run: gem install bundler
@@ -29,6 +28,9 @@ jobs:
29
28
 
30
29
  - name: Rubocop
31
30
  run: bundle exec rubocop
31
+
32
+ - name: Rspec
33
+ run: bundle exec rspec spec
32
34
 
33
35
  publish:
34
36
  if: contains(github.ref, 'refs/tags/v')
data/.gitignore CHANGED
@@ -1,2 +1 @@
1
- .rubocop-http*
2
1
  Gemfile.lock
data/Gemfile CHANGED
@@ -4,3 +4,7 @@ source "https://rubygems.org"
4
4
 
5
5
  # Specify dependencies in gemspec
6
6
  gemspec
7
+
8
+ group :development, :test do
9
+ gem 'byebug'
10
+ end
@@ -7,9 +7,24 @@ Discourse/NoChdir:
7
7
  Discourse/NoTimeNewWithoutArgs:
8
8
  Enabled: true
9
9
 
10
- Discourse/NoUriEscapeEncode:
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:
@@ -29,3 +44,9 @@ Discourse/NoJsonParseResponse:
29
44
  Patterns:
30
45
  - _spec.rb
31
46
  - "(?:^|/)spec/"
47
+
48
+ Discourse/NoMockingJobs:
49
+ Enabled: true
50
+ Patterns:
51
+ - _spec.rb
52
+ - "(?:^|/)spec/"
@@ -12,7 +12,7 @@ AllCops:
12
12
  - "db/schema.rb"
13
13
  - "bundle/**/*"
14
14
  - "vendor/**/*"
15
- - "node_modules/**/*"
15
+ - "**/node_modules/**/*"
16
16
  - "public/**/*"
17
17
  - "plugins/**/gems/**/*"
18
18
 
@@ -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,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Discourse
6
+ class NoMockingJobs < Cop
7
+ MSG = "Use the test helpers provided by Sidekiq instead of mocking `Jobs.expects(:enqueue)`."
8
+
9
+ def_node_matcher :mocking_jobs_enqueue?, <<~MATCHER
10
+ (send (const nil? :Jobs) :expects (:sym :enqueue))
11
+ MATCHER
12
+
13
+ def_node_matcher :mocking_jobs_enqueue_in?, <<~MATCHER
14
+ (send (const nil? :Jobs) :expects (:sym :enqueue_in))
15
+ MATCHER
16
+
17
+ def on_send(node)
18
+ if mocking_jobs_enqueue?(node) || mocking_jobs_enqueue_in?(node)
19
+ add_offense(node, message: MSG)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ 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
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Discourse
6
- # Use `time_eq` matcher with timestamps in specs.
6
+ # Use `eq_time` matcher with timestamps in specs.
7
7
  #
8
8
  # @example
9
9
  # # bad
@@ -12,7 +12,7 @@ module RuboCop
12
12
  # # good
13
13
  # expect(user.created_at).to eq_time(Time.zone.now)
14
14
  class TimeEqMatcher < Cop
15
- MSG = "Use time_eq when testing timestamps"
15
+ MSG = "Use eq_time when testing timestamps"
16
16
 
17
17
  def_node_matcher :using_eq_matcher_with_timestamp?, <<-MATCHER
18
18
  (send
@@ -2,15 +2,18 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "rubocop-discourse"
5
- s.version = "2.1.2"
5
+ s.version = "2.4.0"
6
6
  s.summary = "Custom rubocop cops used by Discourse"
7
7
  s.authors = ["David Taylor"]
8
- s.files = `git ls-files`.split($/)
9
- s.license = "MIT"
8
+ s.license = "MIT"
9
+ s.homepage = "https://github.com/discourse/rubocop-discourse"
10
+
11
+ s.files = `git ls-files`.split($/)
10
12
  s.require_paths = ["lib"]
11
13
 
12
- s.add_runtime_dependency "rubocop", ">= 0.69.0"
13
- s.add_runtime_dependency "rubocop-rspec", ">= 1.39.0"
14
+ s.add_runtime_dependency "rubocop", ">= 1.1.0"
15
+ s.add_runtime_dependency "rubocop-rspec", ">= 2.0.0.pre"
14
16
 
15
17
  s.add_development_dependency "rake", "~> 13.0"
18
+ s.add_development_dependency "rspec"
16
19
  end
@@ -192,20 +192,20 @@ RSpec/VoidExpect:
192
192
  RSpec/Yield:
193
193
  Enabled: true
194
194
 
195
- Capybara/CurrentPathExpectation:
195
+ RSpec/Capybara/CurrentPathExpectation:
196
196
  Enabled: true
197
197
 
198
- Capybara/FeatureMethods:
198
+ RSpec/Capybara/FeatureMethods:
199
199
  Enabled: true
200
200
 
201
- FactoryBot/AttributeDefinedStatically:
201
+ RSpec/FactoryBot/AttributeDefinedStatically:
202
202
  Enabled: true
203
203
 
204
- FactoryBot/CreateList:
204
+ RSpec/FactoryBot/CreateList:
205
205
  Enabled: true
206
206
 
207
- FactoryBot/FactoryClassName:
207
+ RSpec/FactoryBot/FactoryClassName:
208
208
  Enabled: true
209
209
 
210
- Rails/HttpStatus:
210
+ RSpec/Rails/HttpStatus:
211
211
  Enabled: true
@@ -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::NoMockingJobs, :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 Jobs is mocked with :enqueue" do
13
+ inspect_source(<<~RUBY)
14
+ Jobs.expects(:enqueue)
15
+ RUBY
16
+
17
+ expect(cop.offenses.first.message).to eq(described_class::MSG)
18
+ end
19
+
20
+ it "raises an offense if Jobs is mocked with :enqueue_in" do
21
+ inspect_source(<<~RUBY)
22
+ Jobs.expects(:enqueue_in)
23
+ RUBY
24
+
25
+ expect(cop.offenses.first.message).to eq(described_class::MSG)
26
+ end
27
+
28
+ it "does not raise an offense if Jobs is not mocked with :enqueue or :enqueue_in" do
29
+ inspect_source(<<~RUBY)
30
+ Jobs.enqueue(:some_job)
31
+ RUBY
32
+
33
+ expect(cop.offenses).to eq([])
34
+ end
35
+ 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
@@ -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.1.2
4
+ version: 2.4.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-05-03 00:00:00.000000000 Z
11
+ date: 2020-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.69.0
19
+ version: 1.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.69.0
26
+ version: 1.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rubocop-rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 1.39.0
33
+ version: 2.0.0.pre
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 1.39.0
40
+ version: 2.0.0.pre
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -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,13 @@ 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_mocking_jobs.rb
90
+ - lib/rubocop/cop/discourse/no_nokogiri_html_fragment.rb
91
+ - lib/rubocop/cop/discourse/no_reset_column_information_in_migrations.rb
74
92
  - lib/rubocop/cop/discourse/no_time_new_without_args.rb
75
93
  - lib/rubocop/cop/discourse/no_uri_escape_encode.rb
76
94
  - lib/rubocop/cop/discourse/time_eq_matcher.rb
@@ -80,7 +98,11 @@ files:
80
98
  - rubocop-core.yml
81
99
  - rubocop-discourse.gemspec
82
100
  - rubocop-rspec.yml
83
- homepage:
101
+ - spec/lib/rubocop/cop/no_add_reference_active_record_migrations_spec.rb
102
+ - spec/lib/rubocop/cop/no_mocking_jobs_enqueue_spec.rb
103
+ - spec/lib/rubocop/cop/no_reset_column_information_migrations_spec.rb
104
+ - spec/spec_helper.rb
105
+ homepage: https://github.com/discourse/rubocop-discourse
84
106
  licenses:
85
107
  - MIT
86
108
  metadata: {}