ducalis 0.11.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -1
  3. data/.gitignore +1 -0
  4. data/.travis.yml +4 -4
  5. data/Gemfile.lock +11 -9
  6. data/README.md +5 -5
  7. data/config/.ducalis.yml +5 -1
  8. data/ducalis.gemspec +1 -0
  9. data/lib/ducalis.rb +3 -33
  10. data/lib/ducalis/cli_arguments.rb +1 -0
  11. data/lib/ducalis/cops/black_list_suffix.rb +2 -0
  12. data/lib/ducalis/cops/callbacks_activerecord.rb +1 -0
  13. data/lib/ducalis/cops/complex_cases/smart_delete_check.rb +38 -0
  14. data/lib/ducalis/cops/complex_regex.rb +50 -0
  15. data/lib/ducalis/cops/controllers_except.rb +2 -0
  16. data/lib/ducalis/cops/data_access_objects.rb +1 -0
  17. data/lib/ducalis/cops/enforce_namespace.rb +2 -0
  18. data/lib/ducalis/cops/evlis_overusing.rb +2 -0
  19. data/lib/ducalis/cops/extensions/type_resolving.rb +1 -0
  20. data/lib/ducalis/cops/facade_pattern.rb +5 -2
  21. data/lib/ducalis/cops/fetch_expression.rb +2 -1
  22. data/lib/ducalis/cops/keyword_defaults.rb +5 -0
  23. data/lib/ducalis/cops/module_like_class.rb +4 -0
  24. data/lib/ducalis/cops/multiple_times.rb +4 -4
  25. data/lib/ducalis/cops/only_defs.rb +1 -0
  26. data/lib/ducalis/cops/options_argument.rb +1 -0
  27. data/lib/ducalis/cops/params_passing.rb +2 -0
  28. data/lib/ducalis/cops/possible_tap.rb +7 -3
  29. data/lib/ducalis/cops/preferable_methods.rb +4 -8
  30. data/lib/ducalis/cops/private_instance_assign.rb +2 -0
  31. data/lib/ducalis/cops/protected_scope_cop.rb +1 -0
  32. data/lib/ducalis/cops/public_send.rb +1 -0
  33. data/lib/ducalis/cops/raise_without_error_class.rb +1 -0
  34. data/lib/ducalis/cops/recursion.rb +1 -0
  35. data/lib/ducalis/cops/regex_cop.rb +1 -0
  36. data/lib/ducalis/cops/rest_only_cop.rb +2 -0
  37. data/lib/ducalis/cops/rubocop_disable.rb +2 -0
  38. data/lib/ducalis/cops/standard_methods.rb +1 -0
  39. data/lib/ducalis/cops/strings_in_activerecords.rb +3 -0
  40. data/lib/ducalis/cops/too_long_workers.rb +2 -0
  41. data/lib/ducalis/cops/uncommented_gem.rb +3 -0
  42. data/lib/ducalis/cops/unlocked_gem.rb +1 -0
  43. data/lib/ducalis/cops/useless_only.rb +2 -0
  44. data/lib/ducalis/documentation.rb +4 -4
  45. data/lib/ducalis/git_access.rb +4 -1
  46. data/lib/ducalis/patch.rb +4 -4
  47. data/lib/ducalis/patched_rubocop/git_turget_finder.rb +1 -0
  48. data/lib/ducalis/rubo_cop.rb +1 -0
  49. data/lib/ducalis/version.rb +1 -1
  50. metadata +18 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f37cdd7057ceecfbfee6683af5b297e384c02c1aa31392cfd676d15839b3ef8b
4
- data.tar.gz: 0d6581dd6029ed5e29b0d7bda5e37062e30378d67c0910cf2f8ae186674af6c1
3
+ metadata.gz: 9aadaf2bffe42d747bfec8a03418ca1ba12fff39018e1b32305ad71c4108f5b3
4
+ data.tar.gz: 42115ba6b4ba38977f7cf65eeb8684b90dea5faaeaf4a7bea17e03b0768c79a2
5
5
  SHA512:
6
- metadata.gz: b831a202e63c4abb94b3721b8bd95cf25afacc7322910f45498abe0895e0c6495f8ba8ea61267df20b0d5b21c9cada5963b43b6893b0613c16a858bcec071fac
7
- data.tar.gz: 205701a52658e6f888d3bc93668367d1a71302c4f134613e004069c2592c891df892a2c0f726bb0ce7166401844eb39cee64830d45d0dbf5415397ef4be77276
6
+ metadata.gz: 384403e2c35a226966cbe8e3102fbed36bff5bcba77cc963f3d2dd5c670a66ac7e766dd3f0231a7005bc97091bcb23f55180a6eda7de2b3553072c55a2a926c9
7
+ data.tar.gz: f267d508e2b72e8f70acbf842703c8563f8b16893bf534c8abf8b02d16b682e0fde539e976969791b27d3578dee9db9d3746302cf9d4875f643d35ba8fc34ef3
@@ -1,6 +1,6 @@
1
1
  engines:
2
2
  rubocop:
3
3
  enabled: true
4
- channel: rubocop-0-52
4
+ channel: rubocop-0-60
5
5
  config:
6
6
  file: .rubocop.yml
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ /.idea/
1
2
  /.bundle/
2
3
  /.yardoc
3
4
  /_yardoc/
@@ -1,8 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.2.9
4
- - 2.3.6
5
- - 2.4.3
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.0
16
- env: IGNORE_LEGACY=true
15
+ - rvm: 2.5.3
16
+ env: IGNORE_LEGACY=true WITH_DOCS=true
@@ -1,10 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ducalis (0.11.1)
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.2)
19
+ faraday (0.15.3)
19
20
  multipart-post (>= 1.2, < 3)
20
- git (1.4.0)
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.9.0)
25
+ octokit (4.13.0)
25
26
  sawyer (~> 0.8.0, >= 0.5.3)
26
27
  parallel (1.12.1)
27
- parser (2.5.1.2)
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.2)
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.58.2)
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.0, >= 1.0.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
@@ -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: false
55
+ Enabled: true
52
56
 
53
57
  Ducalis/FetchExpression:
54
58
  Enabled: true
@@ -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
@@ -30,36 +30,6 @@ require 'ducalis/cli_arguments'
30
30
  require 'ducalis/patch'
31
31
  require 'ducalis/git_access'
32
32
 
33
- require 'ducalis/cops/black_list_suffix'
34
- require 'ducalis/cops/callbacks_activerecord'
35
- require 'ducalis/cops/case_mapping'
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
@@ -40,6 +40,7 @@ module Ducalis
40
40
  ARGV.include?(to_key(mode))
41
41
  end
42
42
  return unless git_mode
43
+
43
44
  ARGV.delete(to_key(git_mode))
44
45
  GitAccess.instance.flag = git_mode
45
46
  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
@@ -40,6 +40,7 @@ module Ducalis
40
40
  def on_send(node)
41
41
  return unless in_model?
42
42
  return unless METHODS_BLACK_LIST.include?(node.method_name)
43
+
43
44
  add_offense(node, :selector, OFFENSE)
44
45
  end
45
46
  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
 
@@ -22,6 +22,7 @@ module Ducalis
22
22
  def on_send(node)
23
23
  return unless in_controller?
24
24
  return unless NODE_EXPRESSIONS.include?(node.to_a.first)
25
+
25
26
  add_offense(node, :expression, OFFENSE)
26
27
  end
27
28
  end
@@ -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
 
@@ -40,6 +40,7 @@ module TypeResolving
40
40
 
41
41
  def in_controller?
42
42
  return false if @superclass_name.nil?
43
+
43
44
  @superclass_name.end_with?(*CONTROLLER_SUFFIXES)
44
45
  end
45
46
 
@@ -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
- return if instance_variables_matches(node).count < max_instance_variables
23
- add_offense(node, :expression, OFFENSE)
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 = /\:\[\]/ # params[:key]
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
- date_current(body),
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
@@ -19,6 +19,7 @@ module Ducalis
19
19
  return if !inheritance.nil? || body.nil?
20
20
  return unless !instance_methods_definitions?(body) &&
21
21
  class_methods_defintions?(body)
22
+
22
23
  add_offense(node, :expression, OFFENSE)
23
24
  end
24
25
 
@@ -39,6 +39,7 @@ module Ducalis
39
39
 
40
40
  def on_def(node)
41
41
  return unless options_like_arg?(node)
42
+
42
43
  add_offense(node, :expression, OFFENSE)
43
44
  end
44
45
 
@@ -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 unwrap_asign(node)
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
- unwrap_asign(subnode).type == PAIRS[var_node.type] &&
41
- unwrap_asign(subnode).to_a.first == var_node.to_a.first
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
- DELETE_CHECK
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
@@ -22,6 +22,7 @@ module Ducalis
22
22
  def on_send(node)
23
23
  return unless [find_method?(node), find_by_id?(node)].any?
24
24
  return unless const_like?(node)
25
+
25
26
  add_offense(node, :expression, OFFENSE)
26
27
  end
27
28
 
@@ -10,6 +10,7 @@ module Ducalis
10
10
 
11
11
  def on_send(node)
12
12
  return unless send_call?(node)
13
+
13
14
  add_offense(node, :expression, OFFENSE)
14
15
  end
15
16
 
@@ -12,6 +12,7 @@ module Ducalis
12
12
  _who, what, *args = *node
13
13
  return if what != :raise
14
14
  return if args.first && args.first.type != :str
15
+
15
16
  add_offense(node, :expression, OFFENSE)
16
17
  end
17
18
  end
@@ -12,6 +12,7 @@ module Ducalis
12
12
  @method_name, _args, body = *node
13
13
  return unless body
14
14
  return unless send_call?(body) || send_self_call?(body)
15
+
15
16
  add_offense(node, :expression, OFFENSE)
16
17
  end
17
18
 
@@ -44,6 +44,7 @@ module Ducalis
44
44
  def on_begin(node)
45
45
  not_defined_regexes(node).each do |regex|
46
46
  next if SELF_DESCRIPTIVE.include?(regex.source) || const_dynamic?(regex)
47
+
47
48
  add_offense(regex, :expression, format(OFFENSE, present_node(regex)))
48
49
  end
49
50
  end
@@ -22,8 +22,10 @@ module Ducalis
22
22
  def on_def(node)
23
23
  return unless in_controller?
24
24
  return if non_public?(node)
25
+
25
26
  method_name, = *node
26
27
  return if WHITELIST.include?(method_name)
28
+
27
29
  add_offense(node, :expression, OFFENSE)
28
30
  end
29
31
  alias on_defs on_def
@@ -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
@@ -14,6 +14,7 @@ module Ducalis
14
14
  def on_def(node)
15
15
  name, _args, _body = *node
16
16
  return unless BLACK_LIST.include?(name)
17
+
17
18
  add_offense(node, :expression, OFFENSE)
18
19
  end
19
20
  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,8 +16,10 @@ module Ducalis
16
16
 
17
17
  def on_class(node)
18
18
  return unless in_worker?
19
+
19
20
  length = code_length(node)
20
21
  return unless length > max_length
22
+
21
23
  add_offense(node, :expression, "#{OFFENSE} [#{length}/#{max_length}]")
22
24
  end
23
25
  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
@@ -10,6 +10,7 @@ module Ducalis
10
10
 
11
11
  def investigate(processed_source)
12
12
  return unless processed_source.ast
13
+
13
14
  gem_declarations(processed_source.ast).select do |node|
14
15
  _, _, gemname, _args = *node
15
16
  add_offense(node, :selector,
@@ -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\[/ # "/[/1, 2, 3]\n"
12
- CLOSE_SQUARE_BRACKET_END_LINE = /\]\z/ # "[1, 2, 3/]\n/"
13
- LINE_BEGIN_QUOTE = /\A[\'|\"]/ # "/'/idddqd',"
14
- QUOTE_COMMA_END_LINE = /[\'|\"]\,?\z/ # "'iddqd/',/"
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
@@ -9,7 +9,7 @@ class GitAccess
9
9
 
10
10
  MODES = {
11
11
  branch: ->(git) { git.diff('origin/master') },
12
- index: ->(git) { git.diff('HEAD') }
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
 
@@ -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|
@@ -5,6 +5,7 @@ module PatchedRubocop
5
5
  def find_files(base_dir, flags)
6
6
  replacement = GitAccess.instance.changed_files
7
7
  return replacement unless replacement.empty?
8
+
8
9
  super
9
10
  end
10
11
  end
@@ -3,6 +3,7 @@
3
3
  module PatchedRubocop
4
4
  CURRENT_VERSION = Gem::Version.new(RuboCop::Version.version)
5
5
  ADAPTED_VERSION = Gem::Version.new('0.46.0')
6
+ SPEC_CHANGES_VERSION = Gem::Version.new('0.59.0')
6
7
  end
7
8
 
8
9
  module RuboCop
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ducalis
4
- VERSION = '0.11.1'.freeze
4
+ VERSION = '0.12.0'.freeze
5
5
  end
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.11.1
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-08-07 00:00:00.000000000 Z
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