rubocop-discourse 2.1.0 → 2.3.1

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: 6655fa53ae9c2bd93548aae8db88b78294303626031e03896131ca120c083021
4
- data.tar.gz: f21f3ab9e4b3b184f7e26ab16e917ad77598c076fa3a4e0bc9a4280517b9bbcf
3
+ metadata.gz: ab3bab04445e93feec2c095fbd6f2c39c4a2df8e304727bd61138205fe32aa6f
4
+ data.tar.gz: 0b5727aa611459b01ccc2f5c664c8944f97682079dd698434b6b90468c99929e
5
5
  SHA512:
6
- metadata.gz: df675e48cb73e337f4256161d8e16da66ca0c2b00c7402b983f221cc979b1594d0b8636d71672b8d16b7ea74b8be0d4d2ca94accb4dd49c2a3f7810ea13bde11
7
- data.tar.gz: a2a12b9f318aa4fcb2f18adac55efc2bc8c05ffbb151085d9d1f607b0abaf623c13960fcbd18074efc92540b7feff7f1285efc3215dc8cf50615a437192b5a03
6
+ metadata.gz: c4b717bce864cf92b1e3340f826e6671df6bc1fe3d1eecb5488ce1775c42fd8e321e345f728ef8c5852136e43d97f8a0605656b8b22b14b30637791b8666866b
7
+ data.tar.gz: 445c4334c24ee08db442ac0dd7f6b24908f206797d60613b8a22cce5f672753da955d8f084928f9cd8556aca5e3e4e853fbc23f7db1e90008a2914ee306ee496
@@ -29,6 +29,9 @@ jobs:
29
29
 
30
30
  - name: Rubocop
31
31
  run: bundle exec rubocop
32
+
33
+ - name: Rspec
34
+ run: bundle exec rspec spec
32
35
 
33
36
  publish:
34
37
  if: contains(github.ref, 'refs/tags/v')
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  .rubocop-http*
2
2
  Gemfile.lock
3
+ .byebug_history
@@ -1,20 +1,2 @@
1
- require:
2
- - rubocop-discourse
3
-
4
1
  inherit_from:
5
- - ./rubocop-core.yml
6
- - ./rubocop-rspec.yml
7
-
8
- AllCops:
9
- TargetRubyVersion: 2.6
10
- DisabledByDefault: true
11
- Exclude:
12
- - "db/schema.rb"
13
- - "bundle/**/*"
14
- - "vendor/**/*"
15
- - "node_modules/**/*"
16
- - "public/**/*"
17
- - "plugins/**/gems/**/*"
18
-
19
- Discourse:
20
- Enabled: true
2
+ - default.yml
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
data/README.md CHANGED
@@ -7,5 +7,5 @@ Then add the following to `.rubocop.yml`:
7
7
 
8
8
  ```yml
9
9
  inherit_gem:
10
- rubocop-discourse: .rubocop.yml
10
+ rubocop-discourse: default.yml
11
11
  ```
@@ -4,14 +4,49 @@ Discourse/NoChdir:
4
4
  - 'spec/**/*' # Specs are run sequentially, so chdir can be used
5
5
  - 'plugins/*/spec/**/*'
6
6
 
7
- Discourse/NoDirectMultisiteManipulation:
7
+ Discourse/NoTimeNewWithoutArgs:
8
8
  Enabled: true
9
9
 
10
- Discourse/NoTimeNewWithoutArgs:
10
+ Discourse/NoURIEscapeEncode:
11
11
  Enabled: true
12
12
 
13
- Discourse/NoUriEscapeEncode:
13
+ Discourse/NoAddReferenceOrAliasesActiveRecordMigration:
14
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
+
28
+ # Specs
29
+
30
+ Discourse/NoDirectMultisiteManipulation:
31
+ Enabled: true
32
+ Patterns:
33
+ - _spec.rb
34
+ - "(?:^|/)spec/"
15
35
 
16
36
  Discourse/TimeEqMatcher:
17
37
  Enabled: true
38
+ Patterns:
39
+ - _spec.rb
40
+ - "(?:^|/)spec/"
41
+
42
+ Discourse/NoJsonParseResponse:
43
+ Enabled: false
44
+ Patterns:
45
+ - _spec.rb
46
+ - "(?:^|/)spec/"
47
+
48
+ Discourse/NoMockingJobs:
49
+ Enabled: true
50
+ Patterns:
51
+ - _spec.rb
52
+ - "(?:^|/)spec/"
@@ -0,0 +1,20 @@
1
+ require:
2
+ - rubocop-discourse
3
+
4
+ inherit_from:
5
+ - ./rubocop-core.yml
6
+ - ./rubocop-rspec.yml
7
+
8
+ AllCops:
9
+ TargetRubyVersion: 2.6
10
+ DisabledByDefault: true
11
+ Exclude:
12
+ - "db/schema.rb"
13
+ - "bundle/**/*"
14
+ - "vendor/**/*"
15
+ - "**/node_modules/**/*"
16
+ - "public/**/*"
17
+ - "plugins/**/gems/**/*"
18
+
19
+ Discourse:
20
+ Enabled: true
@@ -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,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Discourse
6
+ # Use `response.parsed_body` instead of `JSON.parse(response.body)` in specs.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # expect(::JSON.parse(response.body)).to eq({})
11
+ #
12
+ # # good
13
+ # expect(response.parsed_body).to eq({})
14
+ class NoJsonParseResponse < Cop
15
+ MSG = "Use `response.parsed_body` instead of `JSON.parse(response.body)` in specs."
16
+
17
+ def_node_matcher :json_parse_body?, <<-MATCHER
18
+ (send
19
+ (const {cbase nil?} :JSON) :parse
20
+ (send
21
+ (send nil? :response) :body))
22
+ MATCHER
23
+
24
+ def on_send(node)
25
+ return unless json_parse_body?(node)
26
+
27
+ add_offense(node, message: MSG)
28
+ end
29
+
30
+ def autocorrect(node)
31
+ lambda do |corrector|
32
+ corrector.replace(node.loc.expression, "response.parsed_body")
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ 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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "rubocop-discourse"
5
- s.version = "2.1.0"
5
+ s.version = "2.3.1"
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::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.0
4
+ version: 2.3.1
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-02 00:00:00.000000000 Z
11
+ date: 2020-07-24 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: []
@@ -66,9 +80,15 @@ files:
66
80
  - README.md
67
81
  - Rakefile
68
82
  - config/default.yml
83
+ - default.yml
69
84
  - lib/rubocop-discourse.rb
85
+ - lib/rubocop/cop/discourse/no_add_reference_active_record_migrations.rb
70
86
  - lib/rubocop/cop/discourse/no_chdir.rb
71
87
  - lib/rubocop/cop/discourse/no_direct_multisite_manipulation.rb
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
72
92
  - lib/rubocop/cop/discourse/no_time_new_without_args.rb
73
93
  - lib/rubocop/cop/discourse/no_uri_escape_encode.rb
74
94
  - lib/rubocop/cop/discourse/time_eq_matcher.rb
@@ -78,6 +98,10 @@ files:
78
98
  - rubocop-core.yml
79
99
  - rubocop-discourse.gemspec
80
100
  - rubocop-rspec.yml
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
81
105
  homepage:
82
106
  licenses:
83
107
  - MIT