ducalis 0.11.1 → 0.12.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/.codeclimate.yml +1 -1
- data/.gitignore +1 -0
- data/.travis.yml +4 -4
- data/Gemfile.lock +11 -9
- data/README.md +5 -5
- data/config/.ducalis.yml +5 -1
- data/ducalis.gemspec +1 -0
- data/lib/ducalis.rb +3 -33
- data/lib/ducalis/cli_arguments.rb +1 -0
- data/lib/ducalis/cops/black_list_suffix.rb +2 -0
- data/lib/ducalis/cops/callbacks_activerecord.rb +1 -0
- data/lib/ducalis/cops/complex_cases/smart_delete_check.rb +38 -0
- data/lib/ducalis/cops/complex_regex.rb +50 -0
- data/lib/ducalis/cops/controllers_except.rb +2 -0
- data/lib/ducalis/cops/data_access_objects.rb +1 -0
- data/lib/ducalis/cops/enforce_namespace.rb +2 -0
- data/lib/ducalis/cops/evlis_overusing.rb +2 -0
- data/lib/ducalis/cops/extensions/type_resolving.rb +1 -0
- data/lib/ducalis/cops/facade_pattern.rb +5 -2
- data/lib/ducalis/cops/fetch_expression.rb +2 -1
- data/lib/ducalis/cops/keyword_defaults.rb +5 -0
- data/lib/ducalis/cops/module_like_class.rb +4 -0
- data/lib/ducalis/cops/multiple_times.rb +4 -4
- data/lib/ducalis/cops/only_defs.rb +1 -0
- data/lib/ducalis/cops/options_argument.rb +1 -0
- data/lib/ducalis/cops/params_passing.rb +2 -0
- data/lib/ducalis/cops/possible_tap.rb +7 -3
- data/lib/ducalis/cops/preferable_methods.rb +4 -8
- data/lib/ducalis/cops/private_instance_assign.rb +2 -0
- data/lib/ducalis/cops/protected_scope_cop.rb +1 -0
- data/lib/ducalis/cops/public_send.rb +1 -0
- data/lib/ducalis/cops/raise_without_error_class.rb +1 -0
- data/lib/ducalis/cops/recursion.rb +1 -0
- data/lib/ducalis/cops/regex_cop.rb +1 -0
- data/lib/ducalis/cops/rest_only_cop.rb +2 -0
- data/lib/ducalis/cops/rubocop_disable.rb +2 -0
- data/lib/ducalis/cops/standard_methods.rb +1 -0
- data/lib/ducalis/cops/strings_in_activerecords.rb +3 -0
- data/lib/ducalis/cops/too_long_workers.rb +2 -0
- data/lib/ducalis/cops/uncommented_gem.rb +3 -0
- data/lib/ducalis/cops/unlocked_gem.rb +1 -0
- data/lib/ducalis/cops/useless_only.rb +2 -0
- data/lib/ducalis/documentation.rb +4 -4
- data/lib/ducalis/git_access.rb +4 -1
- data/lib/ducalis/patch.rb +4 -4
- data/lib/ducalis/patched_rubocop/git_turget_finder.rb +1 -0
- data/lib/ducalis/rubo_cop.rb +1 -0
- data/lib/ducalis/version.rb +1 -1
- metadata +18 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9aadaf2bffe42d747bfec8a03418ca1ba12fff39018e1b32305ad71c4108f5b3
|
|
4
|
+
data.tar.gz: 42115ba6b4ba38977f7cf65eeb8684b90dea5faaeaf4a7bea17e03b0768c79a2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 384403e2c35a226966cbe8e3102fbed36bff5bcba77cc963f3d2dd5c670a66ac7e766dd3f0231a7005bc97091bcb23f55180a6eda7de2b3553072c55a2a926c9
|
|
7
|
+
data.tar.gz: f267d508e2b72e8f70acbf842703c8563f8b16893bf534c8abf8b02d16b682e0fde539e976969791b27d3578dee9db9d3746302cf9d4875f643d35ba8fc34ef3
|
data/.codeclimate.yml
CHANGED
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
language: ruby
|
|
2
2
|
rvm:
|
|
3
3
|
- 2.2.9
|
|
4
|
-
- 2.3.
|
|
5
|
-
- 2.4.
|
|
4
|
+
- 2.3.8
|
|
5
|
+
- 2.4.5
|
|
6
6
|
before_install:
|
|
7
7
|
- gem update --system
|
|
8
8
|
- gem --version
|
|
@@ -12,5 +12,5 @@ script:
|
|
|
12
12
|
- bash bin/legacy_versions_test.sh
|
|
13
13
|
matrix:
|
|
14
14
|
include:
|
|
15
|
-
- rvm: 2.5.
|
|
16
|
-
env: IGNORE_LEGACY=true
|
|
15
|
+
- rvm: 2.5.3
|
|
16
|
+
env: IGNORE_LEGACY=true WITH_DOCS=true
|
data/Gemfile.lock
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
ducalis (0.
|
|
4
|
+
ducalis (0.12.0)
|
|
5
5
|
git (~> 1.3, >= 1.3.0)
|
|
6
6
|
octokit (>= 4.7.0)
|
|
7
7
|
regexp-examples (~> 1.3, >= 1.3.2)
|
|
8
|
+
regexp_parser (>= 0.5.0)
|
|
8
9
|
rubocop (>= 0.45.0)
|
|
9
10
|
|
|
10
11
|
GEM
|
|
@@ -15,25 +16,26 @@ GEM
|
|
|
15
16
|
ast (2.4.0)
|
|
16
17
|
coderay (1.1.2)
|
|
17
18
|
diff-lcs (1.3)
|
|
18
|
-
faraday (0.15.
|
|
19
|
+
faraday (0.15.3)
|
|
19
20
|
multipart-post (>= 1.2, < 3)
|
|
20
|
-
git (1.
|
|
21
|
-
jaro_winkler (1.5.1)
|
|
21
|
+
git (1.5.0)
|
|
22
|
+
jaro_winkler (1.5.1-x86_64-darwin-17)
|
|
22
23
|
method_source (0.9.0)
|
|
23
24
|
multipart-post (2.0.0)
|
|
24
|
-
octokit (4.
|
|
25
|
+
octokit (4.13.0)
|
|
25
26
|
sawyer (~> 0.8.0, >= 0.5.3)
|
|
26
27
|
parallel (1.12.1)
|
|
27
|
-
parser (2.5.
|
|
28
|
+
parser (2.5.3.0)
|
|
28
29
|
ast (~> 2.4.0)
|
|
29
30
|
powerpack (0.1.2)
|
|
30
31
|
pry (0.11.3)
|
|
31
32
|
coderay (~> 1.1.0)
|
|
32
33
|
method_source (~> 0.9.0)
|
|
33
|
-
public_suffix (3.0.
|
|
34
|
+
public_suffix (3.0.3)
|
|
34
35
|
rainbow (3.0.0)
|
|
35
36
|
rake (12.3.0)
|
|
36
37
|
regexp-examples (1.4.3)
|
|
38
|
+
regexp_parser (1.2.0)
|
|
37
39
|
rspec (3.7.0)
|
|
38
40
|
rspec-core (~> 3.7.0)
|
|
39
41
|
rspec-expectations (~> 3.7.0)
|
|
@@ -47,14 +49,14 @@ GEM
|
|
|
47
49
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
48
50
|
rspec-support (~> 3.7.0)
|
|
49
51
|
rspec-support (3.7.1)
|
|
50
|
-
rubocop (0.
|
|
52
|
+
rubocop (0.60.0)
|
|
51
53
|
jaro_winkler (~> 1.5.1)
|
|
52
54
|
parallel (~> 1.10)
|
|
53
55
|
parser (>= 2.5, != 2.5.1.1)
|
|
54
56
|
powerpack (~> 0.1)
|
|
55
57
|
rainbow (>= 2.2.2, < 4.0)
|
|
56
58
|
ruby-progressbar (~> 1.7)
|
|
57
|
-
unicode-display_width (~> 1.
|
|
59
|
+
unicode-display_width (~> 1.4.0)
|
|
58
60
|
ruby-progressbar (1.10.0)
|
|
59
61
|
sawyer (0.8.1)
|
|
60
62
|
addressable (>= 2.3.5, < 2.6)
|
data/README.md
CHANGED
|
@@ -24,7 +24,7 @@ __Ducalis__ is CLI application. By defaukt it will notify you about any possible
|
|
|
24
24
|
violations in CLI.
|
|
25
25
|
|
|
26
26
|
```
|
|
27
|
-
ducalis
|
|
27
|
+
ducalis .
|
|
28
28
|
ducalis app/controllers/
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -32,16 +32,16 @@ __Ducalis__ allows to pass build even with violations it's make sense to run
|
|
|
32
32
|
__Ducalis__ across current branch or index:
|
|
33
33
|
|
|
34
34
|
```
|
|
35
|
-
ducalis --branch
|
|
36
|
-
ducalis --index
|
|
35
|
+
ducalis --branch .
|
|
36
|
+
ducalis --index .
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Additionally you can pass `--reporter` argument to notify about found violations
|
|
40
40
|
in boundaries of PR:
|
|
41
41
|
|
|
42
42
|
```
|
|
43
|
-
ducalis --reporter "author/repo#42"
|
|
44
|
-
ducalis --reporter "circleci"
|
|
43
|
+
ducalis --reporter "author/repo#42" .
|
|
44
|
+
ducalis --reporter "circleci" .
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
_N.B._ You should provide `GITHUB_TOKEN` Env to allow __Ducalis__ download your
|
data/config/.ducalis.yml
CHANGED
|
@@ -31,6 +31,10 @@ Ducalis/PreferableMethods:
|
|
|
31
31
|
Ducalis/Recursion:
|
|
32
32
|
Enabled: true
|
|
33
33
|
|
|
34
|
+
Ducalis/ComplexRegex:
|
|
35
|
+
Enabled: true
|
|
36
|
+
MaxComplexity: 3
|
|
37
|
+
|
|
34
38
|
Ducalis/CaseMapping:
|
|
35
39
|
Enabled: true
|
|
36
40
|
|
|
@@ -48,7 +52,7 @@ Ducalis/PossibleTap:
|
|
|
48
52
|
Enabled: false
|
|
49
53
|
|
|
50
54
|
Ducalis/PublicSend:
|
|
51
|
-
Enabled:
|
|
55
|
+
Enabled: true
|
|
52
56
|
|
|
53
57
|
Ducalis/FetchExpression:
|
|
54
58
|
Enabled: true
|
data/ducalis.gemspec
CHANGED
|
@@ -30,5 +30,6 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
spec.add_dependency 'git', '~> 1.3', '>= 1.3.0'
|
|
31
31
|
spec.add_dependency 'octokit', '>= 4.7.0'
|
|
32
32
|
spec.add_dependency 'regexp-examples', '~> 1.3', '>= 1.3.2'
|
|
33
|
+
spec.add_dependency 'regexp_parser', '>= 0.5.0'
|
|
33
34
|
spec.add_dependency 'rubocop', '>= 0.45.0'
|
|
34
35
|
end
|
data/lib/ducalis.rb
CHANGED
|
@@ -30,36 +30,6 @@ require 'ducalis/cli_arguments'
|
|
|
30
30
|
require 'ducalis/patch'
|
|
31
31
|
require 'ducalis/git_access'
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
require
|
|
35
|
-
|
|
36
|
-
require 'ducalis/cops/controllers_except'
|
|
37
|
-
require 'ducalis/cops/data_access_objects'
|
|
38
|
-
require 'ducalis/cops/descriptive_block_names'
|
|
39
|
-
require 'ducalis/cops/enforce_namespace'
|
|
40
|
-
require 'ducalis/cops/evlis_overusing'
|
|
41
|
-
require 'ducalis/cops/extensions/type_resolving'
|
|
42
|
-
require 'ducalis/cops/facade_pattern'
|
|
43
|
-
require 'ducalis/cops/fetch_expression'
|
|
44
|
-
require 'ducalis/cops/keyword_defaults'
|
|
45
|
-
require 'ducalis/cops/module_like_class'
|
|
46
|
-
require 'ducalis/cops/multiple_times'
|
|
47
|
-
require 'ducalis/cops/only_defs'
|
|
48
|
-
require 'ducalis/cops/options_argument'
|
|
49
|
-
require 'ducalis/cops/params_passing'
|
|
50
|
-
require 'ducalis/cops/possible_tap'
|
|
51
|
-
require 'ducalis/cops/preferable_methods'
|
|
52
|
-
require 'ducalis/cops/private_instance_assign'
|
|
53
|
-
require 'ducalis/cops/protected_scope_cop'
|
|
54
|
-
require 'ducalis/cops/public_send'
|
|
55
|
-
require 'ducalis/cops/raise_without_error_class'
|
|
56
|
-
require 'ducalis/cops/recursion'
|
|
57
|
-
require 'ducalis/cops/regex_cop'
|
|
58
|
-
require 'ducalis/cops/rest_only_cop'
|
|
59
|
-
require 'ducalis/cops/rubocop_disable'
|
|
60
|
-
require 'ducalis/cops/standard_methods'
|
|
61
|
-
require 'ducalis/cops/strings_in_activerecords'
|
|
62
|
-
require 'ducalis/cops/too_long_workers'
|
|
63
|
-
require 'ducalis/cops/uncommented_gem'
|
|
64
|
-
require 'ducalis/cops/unlocked_gem'
|
|
65
|
-
require 'ducalis/cops/useless_only'
|
|
33
|
+
Dir[File.join('.', 'lib', 'ducalis', 'cops', '**', '*.rb')].each do |file|
|
|
34
|
+
require file
|
|
35
|
+
end
|
|
@@ -17,6 +17,7 @@ module Ducalis
|
|
|
17
17
|
def on_class(node)
|
|
18
18
|
classdef_node, _superclass, _body = *node
|
|
19
19
|
return unless with_blacklisted_suffix?(classdef_node.source)
|
|
20
|
+
|
|
20
21
|
add_offense(node, :expression, OFFENSE)
|
|
21
22
|
end
|
|
22
23
|
|
|
@@ -24,6 +25,7 @@ module Ducalis
|
|
|
24
25
|
|
|
25
26
|
def with_blacklisted_suffix?(name)
|
|
26
27
|
return if cop_config['BlackList'].to_a.empty?
|
|
28
|
+
|
|
27
29
|
cop_config['BlackList'].any? { |suffix| name.end_with?(suffix) }
|
|
28
30
|
end
|
|
29
31
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ComplexCases
|
|
4
|
+
class SmartDeleteCheck
|
|
5
|
+
WHITE_LIST = %w[File cache file params attrs options].freeze
|
|
6
|
+
|
|
7
|
+
def self.call(who, what, args)
|
|
8
|
+
!new(who, what, args).false_positive?
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(who, _what, args)
|
|
12
|
+
@who = who
|
|
13
|
+
@args = args
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def false_positive?
|
|
17
|
+
[
|
|
18
|
+
called_with_stringlike?,
|
|
19
|
+
many_args?,
|
|
20
|
+
whitelisted?
|
|
21
|
+
].any?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def called_with_stringlike?
|
|
27
|
+
%i[sym str].include?(@args.first && @args.first.type)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def many_args?
|
|
31
|
+
@args.count > 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def whitelisted?
|
|
35
|
+
WHITE_LIST.any? { |regex| @who.to_s.include?(regex) }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubocop'
|
|
4
|
+
require 'regexp_parser'
|
|
5
|
+
|
|
6
|
+
module Ducalis
|
|
7
|
+
class ComplexRegex < RuboCop::Cop::Cop
|
|
8
|
+
include RuboCop::Cop::DefNode
|
|
9
|
+
|
|
10
|
+
OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
|
|
11
|
+
| It seems like this regex is a little bit complex. It's better to increase code readability by using long form with "\\x".
|
|
12
|
+
MESSAGE
|
|
13
|
+
DEFAULT_COST = 0
|
|
14
|
+
COMPLEX_TYPES_COSTS = {
|
|
15
|
+
quantifier: 1,
|
|
16
|
+
meta: 1,
|
|
17
|
+
assertion: 1,
|
|
18
|
+
group: 0.5
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
def on_begin(node)
|
|
22
|
+
regex_using(node).each do |regex_desc|
|
|
23
|
+
next if formatted?(regex_desc) || simple?(regex_desc.first)
|
|
24
|
+
|
|
25
|
+
add_offense(regex_desc.first, :expression, OFFENSE)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def simple?(regex_node)
|
|
32
|
+
Regexp::Scanner.scan(
|
|
33
|
+
Regexp.new(regex_node.source)
|
|
34
|
+
).map do |type, _, _, _, _|
|
|
35
|
+
COMPLEX_TYPES_COSTS.fetch(type, DEFAULT_COST)
|
|
36
|
+
end.inject(:+) <= maximal_complexity
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def maximal_complexity
|
|
40
|
+
cop_config['MaxComplexity']
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def formatted?(regex_desc)
|
|
44
|
+
regex_desc.size > 1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def_node_search :regex_long_form?, '(regopt :x)'
|
|
48
|
+
def_node_search :regex_using, '(regexp $... (regopt ...))'
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -15,8 +15,10 @@ module Ducalis
|
|
|
15
15
|
_, method_name, *args = *node
|
|
16
16
|
hash_node = args.find { |subnode| subnode.type == :hash }
|
|
17
17
|
return unless FILTERS.include?(method_name) && hash_node
|
|
18
|
+
|
|
18
19
|
type, _method_names = decomposite_hash(hash_node)
|
|
19
20
|
return unless type == s(:sym, :except)
|
|
21
|
+
|
|
20
22
|
add_offense(node, :selector, OFFENSE)
|
|
21
23
|
end
|
|
22
24
|
|
|
@@ -12,12 +12,14 @@ module Ducalis
|
|
|
12
12
|
|
|
13
13
|
def on_class(node)
|
|
14
14
|
return if !node.parent.nil? || !in_service?
|
|
15
|
+
|
|
15
16
|
add_offense(node, :expression, OFFENSE)
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def on_module(node)
|
|
19
20
|
return if !node.parent.nil? || !in_service?
|
|
20
21
|
return if contains_class?(node) || contains_classes?(node)
|
|
22
|
+
|
|
21
23
|
add_offense(node, :expression, OFFENSE)
|
|
22
24
|
end
|
|
23
25
|
|
|
@@ -16,11 +16,13 @@ module Ducalis
|
|
|
16
16
|
|
|
17
17
|
def on_send(node)
|
|
18
18
|
return unless nested_try?(node)
|
|
19
|
+
|
|
19
20
|
add_offense(node, :expression, OFFENSE)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
def on_csend(node)
|
|
23
24
|
return unless node.child_nodes.any?(&:csend_type?)
|
|
25
|
+
|
|
24
26
|
add_offense(node, :expression, OFFENSE)
|
|
25
27
|
end
|
|
26
28
|
|
|
@@ -19,8 +19,11 @@ module Ducalis
|
|
|
19
19
|
def on_def(node)
|
|
20
20
|
return unless in_controller?
|
|
21
21
|
return if non_public?(node)
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
|
|
23
|
+
assigns = instance_variables_matches(node)
|
|
24
|
+
return if assigns.count < max_instance_variables
|
|
25
|
+
|
|
26
|
+
assigns.each { |assign| add_offense(assign, :expression, OFFENSE) }
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
private
|
|
@@ -4,7 +4,7 @@ require 'rubocop'
|
|
|
4
4
|
|
|
5
5
|
module Ducalis
|
|
6
6
|
class FetchExpression < RuboCop::Cop::Cop
|
|
7
|
-
HASH_CALLING_REGEX = /\:\[\]
|
|
7
|
+
HASH_CALLING_REGEX = /\:\[\]/.freeze # params[:key]
|
|
8
8
|
|
|
9
9
|
OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
|
|
10
10
|
| You can use `fetch` instead:
|
|
@@ -19,6 +19,7 @@ module Ducalis
|
|
|
19
19
|
|
|
20
20
|
def investigate(processed_source)
|
|
21
21
|
return unless processed_source.ast
|
|
22
|
+
|
|
22
23
|
matching_nodes(processed_source.ast).each do |node|
|
|
23
24
|
add_offense(node, :expression, format(OFFENSE,
|
|
24
25
|
source: correct_variant(node)))
|
|
@@ -6,12 +6,17 @@ module Ducalis
|
|
|
6
6
|
class KeywordDefaults < RuboCop::Cop::Cop
|
|
7
7
|
OFFENSE = <<-MESSAGE.gsub(/^ +\|\s/, '').strip
|
|
8
8
|
| Prefer to use keyword arguments for defaults. It increases readability and reduces ambiguities.
|
|
9
|
+
| It is ok if an argument is single and the name obvious from the function declaration.
|
|
9
10
|
MESSAGE
|
|
10
11
|
|
|
11
12
|
def on_def(node)
|
|
12
13
|
args = node.type == :defs ? node.to_a[2] : node.to_a[1]
|
|
14
|
+
|
|
15
|
+
return if args.to_a.one?
|
|
16
|
+
|
|
13
17
|
args.children.each do |arg_node|
|
|
14
18
|
next unless arg_node.type == :optarg
|
|
19
|
+
|
|
15
20
|
add_offense(node, :expression, OFFENSE)
|
|
16
21
|
end
|
|
17
22
|
end
|
|
@@ -13,8 +13,10 @@ module Ducalis
|
|
|
13
13
|
def on_class(node)
|
|
14
14
|
_name, inheritance, body = *node
|
|
15
15
|
return if !inheritance.nil? || body.nil? || allowed_include?(body)
|
|
16
|
+
|
|
16
17
|
matched = matched_args(body)
|
|
17
18
|
return if matched.empty?
|
|
19
|
+
|
|
18
20
|
add_offense(node, :expression,
|
|
19
21
|
format(OFFENSE, args:
|
|
20
22
|
matched.map { |arg| "`#{arg}`" }.join(', ')))
|
|
@@ -24,12 +26,14 @@ module Ducalis
|
|
|
24
26
|
|
|
25
27
|
def allowed_include?(body)
|
|
26
28
|
return if cop_config['AllowedIncludes'].to_a.empty?
|
|
29
|
+
|
|
27
30
|
(all_includes(body) & cop_config['AllowedIncludes']).any?
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
def matched_args(body)
|
|
31
34
|
methods_defintions = children(body).select(&public_method_definition?)
|
|
32
35
|
return [] if methods_defintions.count == 1 && with_initialize?(body)
|
|
36
|
+
|
|
33
37
|
methods_defintions.map(&method_args).inject(&:&).to_a
|
|
34
38
|
end
|
|
35
39
|
|
|
@@ -29,12 +29,11 @@ module Ducalis
|
|
|
29
29
|
|
|
30
30
|
def on_def(body)
|
|
31
31
|
multiple = [
|
|
32
|
-
date_today(body),
|
|
33
|
-
|
|
34
|
-
time_current(body),
|
|
35
|
-
time_now(body)
|
|
32
|
+
date_today(body), date_current(body), date_yesterday(body),
|
|
33
|
+
time_current(body), time_now(body)
|
|
36
34
|
].map(&:to_a).compact.flatten.to_a
|
|
37
35
|
return if multiple.count < 2
|
|
36
|
+
|
|
38
37
|
multiple.each do |time_node|
|
|
39
38
|
add_offense(time_node, :expression, OFFENSE)
|
|
40
39
|
end
|
|
@@ -44,6 +43,7 @@ module Ducalis
|
|
|
44
43
|
|
|
45
44
|
def_node_search :date_today, '(send (const _ :Date) :today)'
|
|
46
45
|
def_node_search :date_current, '(send (const _ :Date) :current)'
|
|
46
|
+
def_node_search :date_yesterday, '(send (const _ :Date) :yesterday)'
|
|
47
47
|
def_node_search :time_current, '(send (const _ :Time) :current)'
|
|
48
48
|
def_node_search :time_now, '(send (const _ :Time) :now)'
|
|
49
49
|
end
|
|
@@ -20,6 +20,7 @@ module Ducalis
|
|
|
20
20
|
|
|
21
21
|
def inspect_args(args)
|
|
22
22
|
return if Array(args).empty?
|
|
23
|
+
|
|
23
24
|
args.find { |arg| arg == PARAMS_CALL }.tap do |node|
|
|
24
25
|
return node if node
|
|
25
26
|
end
|
|
@@ -28,6 +29,7 @@ module Ducalis
|
|
|
28
29
|
|
|
29
30
|
def inspect_hash(args)
|
|
30
31
|
return if args.nil?
|
|
32
|
+
|
|
31
33
|
args.children.find { |arg| arg.to_a[1] == PARAMS_CALL }
|
|
32
34
|
end
|
|
33
35
|
end
|
|
@@ -26,31 +26,34 @@ module Ducalis
|
|
|
26
26
|
return if body.nil?
|
|
27
27
|
return unless (possibe_var = return_var?(body) || return_var_call?(body))
|
|
28
28
|
return unless (assign_node = find_assign(body, possibe_var))
|
|
29
|
+
|
|
29
30
|
add_offense(assign_node, :expression, OFFENSE)
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
private
|
|
33
34
|
|
|
34
|
-
def
|
|
35
|
+
def unwrap_assign(node)
|
|
35
36
|
node.type == :or_asgn ? node.children.first : node
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def find_assign(body, var_node)
|
|
39
40
|
subnodes(body).find do |subnode|
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
unwrap_assign(subnode).type == PAIRS[var_node.type] &&
|
|
42
|
+
unwrap_assign(subnode).to_a.first == var_node.to_a.first
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
def return_var?(body)
|
|
46
47
|
return unless body.children.last.respond_to?(:type)
|
|
47
48
|
return unless ASSIGNS.include?(body.children.last.type)
|
|
49
|
+
|
|
48
50
|
body.children.last
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
def return_var_call?(body)
|
|
52
54
|
return unless last_child(body).respond_to?(:children)
|
|
53
55
|
return if last_child(body).type == :if
|
|
56
|
+
|
|
54
57
|
subnodes(last_child(body).to_a.first).find do |node|
|
|
55
58
|
ASSIGNS.include?(node.type)
|
|
56
59
|
end
|
|
@@ -58,6 +61,7 @@ module Ducalis
|
|
|
58
61
|
|
|
59
62
|
def subnodes(node)
|
|
60
63
|
return [] unless node.respond_to?(:children)
|
|
64
|
+
|
|
61
65
|
([node] + node.children).select { |child| child.respond_to?(:type) }
|
|
62
66
|
end
|
|
63
67
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'rubocop'
|
|
4
|
+
require 'ducalis/cops/complex_cases/smart_delete_check'
|
|
4
5
|
|
|
5
6
|
module Ducalis
|
|
6
7
|
class PreferableMethods < RuboCop::Cop::Cop
|
|
@@ -8,15 +9,8 @@ module Ducalis
|
|
|
8
9
|
| Prefer to use %<alternative>s method instead of %<original>s because of %<reason>s.
|
|
9
10
|
MESSAGE
|
|
10
11
|
|
|
11
|
-
WHITE_LIST = %w[cache file params attrs options].freeze
|
|
12
|
-
|
|
13
12
|
ALWAYS_TRUE = ->(_who, _what, _args) { true }
|
|
14
13
|
|
|
15
|
-
DELETE_CHECK = lambda do |who, _what, args|
|
|
16
|
-
!%i[sym str].include?(args.first && args.first.type) &&
|
|
17
|
-
args.count <= 1 && WHITE_LIST.none? { |regex| who.to_s.include?(regex) }
|
|
18
|
-
end
|
|
19
|
-
|
|
20
14
|
VALIDATE_CHECK = lambda do |_who, _what, args|
|
|
21
15
|
(args.first && args.first.source) =~ /validate/
|
|
22
16
|
end
|
|
@@ -40,7 +34,7 @@ module Ducalis
|
|
|
40
34
|
delete: [
|
|
41
35
|
'`destroy`',
|
|
42
36
|
'it is not invoking callbacks',
|
|
43
|
-
|
|
37
|
+
ComplexCases::SmartDeleteCheck
|
|
44
38
|
],
|
|
45
39
|
delete_all: [
|
|
46
40
|
'`destroy_all`',
|
|
@@ -70,8 +64,10 @@ module Ducalis
|
|
|
70
64
|
def on_send(node)
|
|
71
65
|
who, what, *args = *node
|
|
72
66
|
return unless DESCRIPTION.key?(what)
|
|
67
|
+
|
|
73
68
|
alternative, reason, condition = DESCRIPTION.fetch(what)
|
|
74
69
|
return unless condition.call(who, what, args)
|
|
70
|
+
|
|
75
71
|
add_offense(node, :expression, format(OFFENSE, original: what,
|
|
76
72
|
alternative: alternative,
|
|
77
73
|
reason: reason))
|
|
@@ -22,6 +22,7 @@ module Ducalis
|
|
|
22
22
|
return unless in_controller?
|
|
23
23
|
return unless non_public?(node)
|
|
24
24
|
return check_memo(node) if node.parent.type == :or_asgn
|
|
25
|
+
|
|
25
26
|
add_offense(node, :expression, OFFENSE)
|
|
26
27
|
end
|
|
27
28
|
|
|
@@ -29,6 +30,7 @@ module Ducalis
|
|
|
29
30
|
|
|
30
31
|
def check_memo(node)
|
|
31
32
|
return if node.to_a.first.to_s.start_with?('@_')
|
|
33
|
+
|
|
32
34
|
add_offense(node, :expression, [OFFENSE, ADD_OFFENSE].join(' '))
|
|
33
35
|
end
|
|
34
36
|
end
|
|
@@ -10,8 +10,10 @@ module Ducalis
|
|
|
10
10
|
|
|
11
11
|
def investigate(processed_source)
|
|
12
12
|
return unless processed_source.ast
|
|
13
|
+
|
|
13
14
|
processed_source.comments.each do |comment_node|
|
|
14
15
|
next unless comment_node.loc.expression.source =~ /rubocop:disable/
|
|
16
|
+
|
|
15
17
|
add_offense(comment_node, :expression, OFFENSE)
|
|
16
18
|
end
|
|
17
19
|
end
|
|
@@ -19,8 +19,10 @@ module Ducalis
|
|
|
19
19
|
_, method_name, *args = *node
|
|
20
20
|
return unless VALIDATEBLE_METHODS.include?(method_name)
|
|
21
21
|
return if args.empty?
|
|
22
|
+
|
|
22
23
|
node.to_a.last.each_child_node do |current_node|
|
|
23
24
|
next if skip_node?(current_node)
|
|
25
|
+
|
|
24
26
|
add_offense(node, :selector, format(OFFENSE, method_name: method_name))
|
|
25
27
|
end
|
|
26
28
|
end
|
|
@@ -32,6 +34,7 @@ module Ducalis
|
|
|
32
34
|
return true unless current_node.type == :pair
|
|
33
35
|
return true unless %w[if unless].include?(key.source)
|
|
34
36
|
return true unless value.type == :str
|
|
37
|
+
|
|
35
38
|
false
|
|
36
39
|
end
|
|
37
40
|
end
|
|
@@ -16,9 +16,11 @@ module Ducalis
|
|
|
16
16
|
|
|
17
17
|
def investigate(processed_source)
|
|
18
18
|
return unless processed_source.ast
|
|
19
|
+
|
|
19
20
|
gem_declarations(processed_source.ast).select do |node|
|
|
20
21
|
_, _, gemname, _args = *node
|
|
21
22
|
next if commented?(processed_source, node)
|
|
23
|
+
|
|
22
24
|
add_offense(node, :selector,
|
|
23
25
|
format(OFFENSE, gem: gemname.loc.expression.source))
|
|
24
26
|
end
|
|
@@ -36,6 +38,7 @@ module Ducalis
|
|
|
36
38
|
|
|
37
39
|
def allowed_args?(args)
|
|
38
40
|
return false if args.nil? || args.type != :hash
|
|
41
|
+
|
|
39
42
|
args.children.any? do |arg_node|
|
|
40
43
|
!ALLOWED_KEYS.include?(arg_node.children.first.source)
|
|
41
44
|
end
|
|
@@ -32,9 +32,11 @@ module Ducalis
|
|
|
32
32
|
_, method_name, *args = *node
|
|
33
33
|
hash_node = args.find { |subnode| subnode.type == :hash }
|
|
34
34
|
return unless FILTERS.include?(method_name) && hash_node
|
|
35
|
+
|
|
35
36
|
type, method_names = decomposite_hash(hash_node)
|
|
36
37
|
return unless type == s(:sym, :only)
|
|
37
38
|
return unless method_names.children.count == 1
|
|
39
|
+
|
|
38
40
|
add_offense(node, :selector, OFFENSE)
|
|
39
41
|
end
|
|
40
42
|
|
|
@@ -8,10 +8,10 @@ require 'parser/current'
|
|
|
8
8
|
class SpecsProcessor < Parser::AST::Processor
|
|
9
9
|
attr_reader :cases
|
|
10
10
|
|
|
11
|
-
LINE_BEGIN_OPEN_SQUARE_BRACKET = /\A\[
|
|
12
|
-
CLOSE_SQUARE_BRACKET_END_LINE = /\]\z
|
|
13
|
-
LINE_BEGIN_QUOTE = /\A[\'|\"]
|
|
14
|
-
QUOTE_COMMA_END_LINE = /[\'|\"]\,?\z
|
|
11
|
+
LINE_BEGIN_OPEN_SQUARE_BRACKET = /\A\[/.freeze # "/[/1, 2, 3]\n"
|
|
12
|
+
CLOSE_SQUARE_BRACKET_END_LINE = /\]\z/.freeze # "[1, 2, 3/]\n/"
|
|
13
|
+
LINE_BEGIN_QUOTE = /\A[\'|\"]/.freeze # "/'/idddqd',"
|
|
14
|
+
QUOTE_COMMA_END_LINE = /[\'|\"]\,?\z/.freeze # "'iddqd/',/"
|
|
15
15
|
|
|
16
16
|
def initialize(*)
|
|
17
17
|
super
|
data/lib/ducalis/git_access.rb
CHANGED
|
@@ -9,7 +9,7 @@ class GitAccess
|
|
|
9
9
|
|
|
10
10
|
MODES = {
|
|
11
11
|
branch: ->(git) { git.diff('origin/master') },
|
|
12
|
-
index:
|
|
12
|
+
index: ->(git) { git.diff('HEAD') }
|
|
13
13
|
}.freeze
|
|
14
14
|
|
|
15
15
|
include Diffs
|
|
@@ -29,6 +29,7 @@ class GitAccess
|
|
|
29
29
|
|
|
30
30
|
def for(path)
|
|
31
31
|
return find(path) unless path.include?(Dir.pwd)
|
|
32
|
+
|
|
32
33
|
find(Pathname.new(path).relative_path_from(Pathname.new(Dir.pwd)).to_s)
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -40,6 +41,7 @@ class GitAccess
|
|
|
40
41
|
|
|
41
42
|
def changes
|
|
42
43
|
return default_value if flag.nil? || !under_git?
|
|
44
|
+
|
|
43
45
|
@changes ||= patch_diffs
|
|
44
46
|
end
|
|
45
47
|
|
|
@@ -53,6 +55,7 @@ class GitAccess
|
|
|
53
55
|
|
|
54
56
|
def default_value
|
|
55
57
|
raise Ducalis::MissingGit unless flag.nil?
|
|
58
|
+
|
|
56
59
|
[]
|
|
57
60
|
end
|
|
58
61
|
|
data/lib/ducalis/patch.rb
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module Ducalis
|
|
4
4
|
class Patch
|
|
5
|
-
RANGE_LINE = /^@@ .+\+(?<line_number>\d+)
|
|
6
|
-
MODIFIED_LINE = /^\+(?!\+|\+)
|
|
7
|
-
NOT_REMOVED_LINE = /^[^-]
|
|
8
|
-
ANY_LINE =
|
|
5
|
+
RANGE_LINE = /^@@ .+\+(?<line_number>\d+),/.freeze
|
|
6
|
+
MODIFIED_LINE = /^\+(?!\+|\+)/.freeze
|
|
7
|
+
NOT_REMOVED_LINE = /^[^-]/.freeze
|
|
8
|
+
ANY_LINE = /.*/.freeze
|
|
9
9
|
|
|
10
10
|
DIFF_LINES = {
|
|
11
11
|
RANGE_LINE => lambda do |lines, _line_number, line, _position|
|
data/lib/ducalis/rubo_cop.rb
CHANGED
data/lib/ducalis/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ducalis
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ignat Zakrevsky
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-
|
|
11
|
+
date: 2018-11-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: git
|
|
@@ -64,6 +64,20 @@ dependencies:
|
|
|
64
64
|
- - ">="
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
66
|
version: 1.3.2
|
|
67
|
+
- !ruby/object:Gem::Dependency
|
|
68
|
+
name: regexp_parser
|
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: 0.5.0
|
|
74
|
+
type: :runtime
|
|
75
|
+
prerelease: false
|
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: 0.5.0
|
|
67
81
|
- !ruby/object:Gem::Dependency
|
|
68
82
|
name: rubocop
|
|
69
83
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -111,6 +125,8 @@ files:
|
|
|
111
125
|
- lib/ducalis/cops/black_list_suffix.rb
|
|
112
126
|
- lib/ducalis/cops/callbacks_activerecord.rb
|
|
113
127
|
- lib/ducalis/cops/case_mapping.rb
|
|
128
|
+
- lib/ducalis/cops/complex_cases/smart_delete_check.rb
|
|
129
|
+
- lib/ducalis/cops/complex_regex.rb
|
|
114
130
|
- lib/ducalis/cops/controllers_except.rb
|
|
115
131
|
- lib/ducalis/cops/data_access_objects.rb
|
|
116
132
|
- lib/ducalis/cops/descriptive_block_names.rb
|