rubocop-discourse 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +44 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +4 -0
- data/Gemfile +6 -0
- data/LICENSE +20 -0
- data/Rakefile +8 -0
- data/config/default.yml +14 -0
- data/lib/rubocop-discourse.rb +9 -0
- data/lib/rubocop/cop/discourse/no_chdir.rb +34 -0
- data/lib/rubocop/cop/discourse/no_direct_multisite_manipulation.rb +37 -0
- data/lib/rubocop/cop/discourse/no_time_new_without_args.rb +35 -0
- data/lib/rubocop/cop/discourse/no_uri_escape_encode.rb +49 -0
- data/lib/rubocop/cop/discourse/time_eq_matcher.rb +49 -0
- data/lib/rubocop/cop/discourse_cops.rb +4 -0
- data/lib/rubocop/discourse.rb +12 -0
- data/lib/rubocop/discourse/inject.rb +18 -0
- data/rubocop-discourse.gemspec +15 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '09c74d2dcf1477ce0ae014f1f22d5d0683963a963ebd4e326eb870fb492398e9'
|
4
|
+
data.tar.gz: 0aec169456ac388da0e57f01c3f941f8604978d32150d00ccd35ed04c0f45237
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c93f323c3161d55ad1f8aba8f545904757683f0628184b7414171c0713e24fc54c76df328a15ec02969689273649ef804eaa9ed363a51442c5cc7486786934fa
|
7
|
+
data.tar.gz: 9d68351058a663411c9a860fcdf19a3a3406663143316b20586ba32be835e82ff68ff35b43aecc1adf21d9b53289612e246a6a570a9822c9d8df905c4b952db6
|
@@ -0,0 +1,44 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
push:
|
6
|
+
branches:
|
7
|
+
- master
|
8
|
+
tags:
|
9
|
+
- v*
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
build:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v1
|
17
|
+
|
18
|
+
- name: Setup ruby
|
19
|
+
uses: actions/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: 2.6
|
22
|
+
architecture: 'x64'
|
23
|
+
|
24
|
+
- name: Setup bundler
|
25
|
+
run: gem install bundler
|
26
|
+
|
27
|
+
- name: Setup gems
|
28
|
+
run: bundle install
|
29
|
+
|
30
|
+
- name: Rubocop
|
31
|
+
run: bundle exec rubocop
|
32
|
+
|
33
|
+
publish:
|
34
|
+
if: contains(github.ref, 'refs/tags/v')
|
35
|
+
needs: build
|
36
|
+
runs-on: ubuntu-latest
|
37
|
+
|
38
|
+
steps:
|
39
|
+
- uses: actions/checkout@v2
|
40
|
+
|
41
|
+
- name: Release Gem
|
42
|
+
uses: CvX/publish-rubygems-action@master
|
43
|
+
env:
|
44
|
+
RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Civilized Discourse Construction Kit, Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/config/default.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Discourse
|
6
|
+
# Avoid using chdir - it is not thread safe.
|
7
|
+
#
|
8
|
+
# Instead, you may be able to use:
|
9
|
+
# Discourse::Utils.execute_command(chdir: "test") do |runner|
|
10
|
+
# runner.exec("pwd")
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# # bad
|
15
|
+
# Dir.chdir("test")
|
16
|
+
class NoChdir < Cop
|
17
|
+
MSG = "Chdir is not thread safe."
|
18
|
+
|
19
|
+
def_node_matcher :using_dir_chdir?, <<-MATCHER
|
20
|
+
(send (const nil? :Dir) :chdir ...)
|
21
|
+
MATCHER
|
22
|
+
|
23
|
+
def_node_matcher :using_fileutils_cd?, <<-MATCHER
|
24
|
+
(send (const nil? :FileUtils) :cd ...)
|
25
|
+
MATCHER
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
return if !(using_dir_chdir?(node) || using_fileutils_cd?(node))
|
29
|
+
add_offense(node, message: MSG)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Discourse
|
6
|
+
# Use `type: :multisite` example setting instead of modifying `Rails.configuration.multisite`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# it "works" do
|
11
|
+
# Rails.configuration.multisite = true
|
12
|
+
# do_something
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# it "works", type: :multisite do
|
17
|
+
# do_something
|
18
|
+
# end
|
19
|
+
class NoDirectMultisiteManipulation < Cop
|
20
|
+
MSG = "Use `type: :multisite` example setting instead of modifying `Rails.configuration.multisite`."
|
21
|
+
|
22
|
+
def_node_matcher :multisite_setter?, <<-MATCHER
|
23
|
+
(send
|
24
|
+
(send
|
25
|
+
(const nil? :Rails) :configuration) :multisite=
|
26
|
+
_)
|
27
|
+
MATCHER
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
return unless multisite_setter?(node)
|
31
|
+
|
32
|
+
add_offense(node, message: MSG)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Discourse
|
6
|
+
# Use `Time.zone.now` instead of `Time.new` without arguments.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# now = Time.new
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# now = Time.zone.now
|
14
|
+
class NoTimeNewWithoutArgs < Cop
|
15
|
+
MSG = "Use `Time.zone.now` instead of `Time.new` without arguments."
|
16
|
+
|
17
|
+
def_node_matcher :time_new_without_args?, <<-MATCHER
|
18
|
+
(send (const nil? :Time) :new)
|
19
|
+
MATCHER
|
20
|
+
|
21
|
+
def on_send(node)
|
22
|
+
return unless time_new_without_args?(node)
|
23
|
+
|
24
|
+
add_offense(node, message: MSG)
|
25
|
+
end
|
26
|
+
|
27
|
+
def autocorrect(node)
|
28
|
+
lambda do |corrector|
|
29
|
+
corrector.replace(node.loc.expression, "Time.zone.now")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Discourse
|
6
|
+
# Do not use URI.escape and its ilk, they are deprecated
|
7
|
+
# with a warning in the ruby source. Instead use
|
8
|
+
# Addressable::URI, which has encode, encode_component,
|
9
|
+
# and unencode methods. UrlHelper has helper methods for this.
|
10
|
+
#
|
11
|
+
# # @example
|
12
|
+
# # bad
|
13
|
+
# URI.encode("https://a%20a.com?a='a%22")
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# UrlHelper.encode("https://a%20a.com?a='a%22")
|
17
|
+
# Addressable::URI.encode("https://a%20a.com?a='a%22")
|
18
|
+
class NoURIEscapeEncode < Cop
|
19
|
+
MSG = "URI.escape, URI.encode, URI.unescape, URI.decode are deprecated and should not be used."
|
20
|
+
|
21
|
+
def_node_matcher :using_uri_escape?, <<-MATCHER
|
22
|
+
(send (const nil? :URI) :escape ...)
|
23
|
+
MATCHER
|
24
|
+
|
25
|
+
def_node_matcher :using_uri_encode?, <<-MATCHER
|
26
|
+
(send (const nil? :URI) :encode ...)
|
27
|
+
MATCHER
|
28
|
+
|
29
|
+
def_node_matcher :using_uri_unescape?, <<-MATCHER
|
30
|
+
(send (const nil? :URI) :unescape ...)
|
31
|
+
MATCHER
|
32
|
+
|
33
|
+
def_node_matcher :using_uri_decode?, <<-MATCHER
|
34
|
+
(send (const nil? :URI) :decode ...)
|
35
|
+
MATCHER
|
36
|
+
|
37
|
+
def on_send(node)
|
38
|
+
return if [
|
39
|
+
using_uri_escape?(node),
|
40
|
+
using_uri_encode?(node),
|
41
|
+
using_uri_unescape?(node),
|
42
|
+
using_uri_decode?(node)
|
43
|
+
].none?
|
44
|
+
add_offense(node, message: MSG)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Discourse
|
6
|
+
# Use `time_eq` matcher with timestamps in specs.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# expect(user.created_at).to eq(Time.zone.now)
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# expect(user.created_at).to eq_time(Time.zone.now)
|
14
|
+
class TimeEqMatcher < Cop
|
15
|
+
MSG = "Use time_eq when testing timestamps"
|
16
|
+
|
17
|
+
def_node_matcher :using_eq_matcher_with_timestamp?, <<-MATCHER
|
18
|
+
(send
|
19
|
+
(send nil? :expect
|
20
|
+
(send ... #timestamp_suffix?))
|
21
|
+
{:to :not_to :to_not}
|
22
|
+
(send nil? :eq #not_nil))
|
23
|
+
MATCHER
|
24
|
+
|
25
|
+
def on_send(node)
|
26
|
+
return unless using_eq_matcher_with_timestamp?(node)
|
27
|
+
|
28
|
+
add_offense(node, message: MSG)
|
29
|
+
end
|
30
|
+
|
31
|
+
def autocorrect(node)
|
32
|
+
lambda do |corrector|
|
33
|
+
corrector.replace(node.children.last.loc.selector, "eq_time")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def timestamp_suffix?(property)
|
40
|
+
property =~ /_at$/
|
41
|
+
end
|
42
|
+
|
43
|
+
def not_nil(expression)
|
44
|
+
expression != [s(:nil)]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
# RuboCop Discourse project namespace
|
5
|
+
module Discourse
|
6
|
+
PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
|
7
|
+
CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
|
8
|
+
CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
|
9
|
+
|
10
|
+
private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Discourse
|
5
|
+
# Because RuboCop doesn't yet support plugins, we have to monkey patch in a
|
6
|
+
# bit of our configuration.
|
7
|
+
module Inject
|
8
|
+
def self.defaults!
|
9
|
+
path = CONFIG_DEFAULT.to_s
|
10
|
+
hash = ConfigLoader.send(:load_yaml_configuration, path)
|
11
|
+
config = Config.new(hash, path)
|
12
|
+
puts "configuration from #{path}" if ConfigLoader.debug?
|
13
|
+
config = ConfigLoader.merge_with_default(config, path)
|
14
|
+
ConfigLoader.instance_variable_set(:@default_configuration, config)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "rubocop-discourse"
|
5
|
+
s.version = "2.0.0"
|
6
|
+
s.summary = "Custom rubocop cops used by Discourse"
|
7
|
+
s.authors = ["David Taylor"]
|
8
|
+
s.files = `git ls-files`.split($/)
|
9
|
+
s.license = "MIT"
|
10
|
+
s.require_paths = ["lib"]
|
11
|
+
|
12
|
+
s.add_runtime_dependency "rubocop", ">= 0.69.0"
|
13
|
+
|
14
|
+
s.add_development_dependency "rake", "~> 13.0"
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubocop-discourse
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Taylor
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-03-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubocop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.69.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.69.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '13.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '13.0'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- ".github/workflows/ci.yml"
|
48
|
+
- ".gitignore"
|
49
|
+
- ".rubocop.yml"
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE
|
52
|
+
- Rakefile
|
53
|
+
- config/default.yml
|
54
|
+
- lib/rubocop-discourse.rb
|
55
|
+
- lib/rubocop/cop/discourse/no_chdir.rb
|
56
|
+
- lib/rubocop/cop/discourse/no_direct_multisite_manipulation.rb
|
57
|
+
- lib/rubocop/cop/discourse/no_time_new_without_args.rb
|
58
|
+
- lib/rubocop/cop/discourse/no_uri_escape_encode.rb
|
59
|
+
- lib/rubocop/cop/discourse/time_eq_matcher.rb
|
60
|
+
- lib/rubocop/cop/discourse_cops.rb
|
61
|
+
- lib/rubocop/discourse.rb
|
62
|
+
- lib/rubocop/discourse/inject.rb
|
63
|
+
- rubocop-discourse.gemspec
|
64
|
+
homepage:
|
65
|
+
licenses:
|
66
|
+
- MIT
|
67
|
+
metadata: {}
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubygems_version: 3.0.3
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: Custom rubocop cops used by Discourse
|
87
|
+
test_files: []
|