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.
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