reek 4.0.3 → 4.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -4
  3. data/CHANGELOG.md +4 -0
  4. data/Gemfile +3 -3
  5. data/Rakefile +3 -3
  6. data/docs/Class-Variable.md +1 -1
  7. data/docs/Control-Parameter.md +1 -1
  8. data/docs/Duplicate-Method-Call.md +8 -4
  9. data/features/samples.feature +3 -2
  10. data/lib/reek/smells/data_clump.rb +2 -2
  11. data/lib/reek/smells/duplicate_method_call.rb +2 -2
  12. data/lib/reek/smells/long_parameter_list.rb +1 -1
  13. data/lib/reek/smells/long_yield_list.rb +1 -3
  14. data/lib/reek/smells/nested_iterators.rb +54 -39
  15. data/lib/reek/smells/repeated_conditional.rb +1 -1
  16. data/lib/reek/smells/smell_configuration.rb +5 -4
  17. data/lib/reek/smells/smell_detector.rb +3 -3
  18. data/lib/reek/smells/too_many_instance_variables.rb +1 -1
  19. data/lib/reek/smells/too_many_methods.rb +1 -1
  20. data/lib/reek/smells/too_many_statements.rb +1 -3
  21. data/lib/reek/smells/uncommunicative_method_name.rb +2 -2
  22. data/lib/reek/smells/uncommunicative_module_name.rb +2 -2
  23. data/lib/reek/smells/uncommunicative_parameter_name.rb +2 -2
  24. data/lib/reek/smells/uncommunicative_variable_name.rb +2 -2
  25. data/lib/reek/smells/unused_private_method.rb +1 -1
  26. data/lib/reek/smells/utility_function.rb +5 -1
  27. data/lib/reek/source/source_code.rb +3 -0
  28. data/lib/reek/version.rb +1 -1
  29. data/reek.gemspec +1 -1
  30. data/spec/reek/smells/nested_iterators_spec.rb +20 -20
  31. data/spec/reek/smells/smell_configuration_spec.rb +0 -5
  32. data/spec/reek/smells/utility_function_spec.rb +22 -0
  33. data/spec/spec_helper.rb +5 -0
  34. data/tasks/ataru.rake +4 -0
  35. data/tasks/mutant.rake +5 -0
  36. metadata +8 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 71c1d52a08d1ce16364812f297d6a072109b406b
4
- data.tar.gz: f0ea4129036dde1687ad6656c603d63e8a6ddaf2
3
+ metadata.gz: 7c09dc83cbd7c49a57a5e0568479ccf87a8c284d
4
+ data.tar.gz: 7a9643ce33812488821abaabaf157eee3ba1f886
5
5
  SHA512:
6
- metadata.gz: 1228e4b9dc1f5673e7f474af1c6f5eb16d30eac8df72d4aecb4684b2ff2aa7796e62defe53a903ff9853444fe112187e3d7de7f40bd8c619c75d459d11273d7d
7
- data.tar.gz: fb720491aa22c267bdccdfdb7296f9f89a216ab7da89d8e66f903c63bf401ee8beff14256a0b052bf284df93f81d27f9c050f1db1af3b15714f6b7a23565c425
6
+ metadata.gz: e85233445ea0535f360876d6f45398ff364aa895883b0175a60097839fbb5182b399496010c675253a9b79efdd15b1ca759db9253ea7febfc916efe2a3e3013f
7
+ data.tar.gz: cdc14223fc488191e56d3642c1bff7affeb9f4e97fc9f6030d1bd7988f7c75f133ad824724da58785c44556ab0a2483f7893f35c219eb666f22dfcb21f8b6082
data/.travis.yml CHANGED
@@ -2,10 +2,7 @@ sudo: false
2
2
  cache: bundler
3
3
  language: ruby
4
4
  bundler_args: --without debugging
5
- script:
6
- - bundle exec rake
7
- - bundle exec ataru check
8
- - bundle exec mutant --include lib --require reek --use rspec --since master --jobs 4 "Reek*"
5
+ script: bundle exec rake ci
9
6
  rvm:
10
7
  - 2.1
11
8
  - 2.2
@@ -30,3 +27,6 @@ notifications:
30
27
  - matijs@matijs.net
31
28
  - chastell@chastell.net
32
29
  irc: irc.freenode.org#reek
30
+ branches:
31
+ only:
32
+ - master
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Change log
2
2
 
3
+ ## 4.0.4 (2016-06-11)
4
+
5
+ * (troessner) Bump parser
6
+
3
7
  ## 4.0.3 (2016-05-23)
4
8
 
5
9
  * (mvz) Include default exclusions in generated TODO file
data/Gemfile CHANGED
@@ -7,12 +7,12 @@ group :development do
7
7
  gem 'ataru', '~> 0.2.0'
8
8
  gem 'cucumber', '~> 2.0'
9
9
  gem 'factory_girl', '~> 4.0'
10
+ gem 'mutant-rspec', '~> 0.8.8'
10
11
  gem 'rake', '~> 11.1'
11
12
  gem 'rspec', '~> 3.0'
12
13
  gem 'rubocop', '~> 0.39.0'
13
- gem 'yard', '~> 0.8.7'
14
14
  gem 'simplecov', '~> 0.11.1'
15
- gem 'mutant-rspec', '~> 0.8.8'
15
+ gem 'yard', '~> 0.8.7'
16
16
 
17
17
  platforms :mri do
18
18
  gem 'redcarpet', '~> 3.3.1'
@@ -21,8 +21,8 @@ end
21
21
 
22
22
  group :debugging do
23
23
  # Fixing https://github.com/guard/guard/wiki/Add-Readline-support-to-Ruby-on-Mac-OS-X#option-4-using-a-pure-ruby-readline-implementation
24
- gem 'rb-readline', '~> 0.5.3'
25
24
  gem 'pry'
25
+ gem 'rb-readline', '~> 0.5.3'
26
26
  platforms :mri do
27
27
  gem 'pry-byebug'
28
28
  gem 'pry-stack_explorer'
data/Rakefile CHANGED
@@ -3,6 +3,6 @@ require 'rake/clean'
3
3
 
4
4
  Dir['tasks/**/*.rake'].each { |t| load t }
5
5
 
6
- task default: :test
7
- task default: :rubocop unless RUBY_ENGINE == 'rbx'
8
- task default: 'test:quality'
6
+ task local_test_run: [:test, :rubocop, 'test:quality']
7
+ task ci: [:test, :rubocop, 'test:quality', :ataru, :mutant]
8
+ task default: :local_test_run
@@ -4,7 +4,7 @@
4
4
 
5
5
  Class variables form part of the global runtime state, and as such make it easy for one part of the system to accidentally or inadvertently depend on another part of the system. So the system becomes more prone to problems where changing something over here breaks something over there. In particular, class variables can make it hard to set up tests (because the context of the test includes all global state).
6
6
 
7
- For a detailed explanation, check out [this article](http://4thmouse.com/index.php/2011/03/20/why-class-variables-in-ruby-are-a-bad-idea/)
7
+ For a detailed explanation, check out [this article](http://4thmouse.com/index.php/2011/03/20/why-class-variables-in-ruby-are-a-bad-idea/).
8
8
 
9
9
  ## Example
10
10
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- _Control Parameter_ is a case of [Control Couple](Control-Couple.md)
5
+ _Control Parameter_ is a case of [Control Couple](Control-Couple.md).
6
6
 
7
7
  ## Example
8
8
 
@@ -10,7 +10,7 @@ Reek implements a check for _Duplicate Method Call_.
10
10
  Here's a very much simplified and contrived example. The following method will report a warning:
11
11
 
12
12
  ```Ruby
13
- def double_thing()
13
+ def double_thing
14
14
  @other.thing + @other.thing
15
15
  end
16
16
  ```
@@ -18,19 +18,23 @@ end
18
18
  One quick approach to silence Reek would be to refactor the code thus:
19
19
 
20
20
  ```Ruby
21
- def double_thing()
21
+ def double_thing
22
22
  thing = @other.thing
23
23
  thing + thing
24
24
  end
25
25
  ```
26
26
 
27
- A slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:
27
+ A slightly different approach would be to replace all calls in `double_thing` by calls to `thing`:
28
28
 
29
29
  ```Ruby
30
30
  class Other
31
- def double_thing()
31
+ def double_thing
32
32
  thing + thing
33
33
  end
34
+
35
+ def thing
36
+ @other.thing
37
+ end
34
38
  end
35
39
  ```
36
40
 
@@ -177,7 +177,7 @@ Feature: Basic smell detection
177
177
  UnusedParameters: OptionParser::Completion#convert has unused parameter 'opt' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
178
178
  UnusedParameters: OptionParser::Switch::NoArgument#parse has unused parameter 'argv' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
179
179
  UnusedParameters: OptionParser::Switch::OptionalArgument#parse has unused parameter 'argv' [https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md]
180
- redcloth.rb -- 101 warnings:
180
+ redcloth.rb -- 102 warnings:
181
181
  Attribute: RedCloth#filter_html is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
182
182
  Attribute: RedCloth#filter_styles is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
183
183
  Attribute: RedCloth#hard_breaks is a writable attribute [https://github.com/troessner/reek/blob/master/docs/Attribute.md]
@@ -216,6 +216,7 @@ Feature: Basic smell detection
216
216
  LongParameterList: RedCloth#textile_fn_ has 5 parameters [https://github.com/troessner/reek/blob/master/docs/Long-Parameter-List.md]
217
217
  LongParameterList: RedCloth#textile_p has 4 parameters [https://github.com/troessner/reek/blob/master/docs/Long-Parameter-List.md]
218
218
  NestedIterators: RedCloth#block_textile_lists contains iterators nested 3 deep [https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md]
219
+ NestedIterators: RedCloth#block_textile_table contains iterators nested 2 deep [https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md]
219
220
  NestedIterators: RedCloth#block_textile_table contains iterators nested 3 deep [https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md]
220
221
  NestedIterators: RedCloth#blocks contains iterators nested 2 deep [https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md]
221
222
  NestedIterators: RedCloth#clean_html contains iterators nested 2 deep [https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md]
@@ -279,5 +280,5 @@ Feature: Basic smell detection
279
280
  UtilityFunction: RedCloth#lT doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
280
281
  UtilityFunction: RedCloth#no_textile doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
281
282
  UtilityFunction: RedCloth#v_align doesn't depend on instance state (maybe move it to another class?) [https://github.com/troessner/reek/blob/master/docs/Utility-Function.md]
282
- 265 total warnings
283
+ 266 total warnings
283
284
  """
@@ -53,8 +53,8 @@ module Reek
53
53
  #
54
54
  # :reek:FeatureEnvy
55
55
  def inspect(ctx)
56
- max_copies = value(MAX_COPIES_KEY, ctx, DEFAULT_MAX_COPIES)
57
- min_clump_size = value(MIN_CLUMP_SIZE_KEY, ctx, DEFAULT_MIN_CLUMP_SIZE)
56
+ max_copies = value(MAX_COPIES_KEY, ctx)
57
+ min_clump_size = value(MIN_CLUMP_SIZE_KEY, ctx)
58
58
  MethodGroup.new(ctx, min_clump_size, max_copies).clumps.map do |clump, methods|
59
59
  methods_length = methods.length
60
60
  smell_warning(
@@ -45,8 +45,8 @@ module Reek
45
45
  # :reek:FeatureEnvy
46
46
  # :reek:DuplicateMethodCall: { max_calls: 2 }
47
47
  def inspect(ctx)
48
- max_allowed_calls = value(MAX_ALLOWED_CALLS_KEY, ctx, DEFAULT_MAX_CALLS)
49
- allow_calls = value(ALLOW_CALLS_KEY, ctx, DEFAULT_ALLOW_CALLS)
48
+ max_allowed_calls = value(MAX_ALLOWED_CALLS_KEY, ctx)
49
+ allow_calls = value(ALLOW_CALLS_KEY, ctx)
50
50
 
51
51
  collector = CallCollector.new(ctx, max_allowed_calls, allow_calls)
52
52
  collector.smelly_calls.map do |found_call|
@@ -35,7 +35,7 @@ module Reek
35
35
  # @return [Array<SmellWarning>]
36
36
  #
37
37
  def inspect(ctx)
38
- max_allowed_params = value(MAX_ALLOWED_PARAMS_KEY, ctx, DEFAULT_MAX_ALLOWED_PARAMS)
38
+ max_allowed_params = value(MAX_ALLOWED_PARAMS_KEY, ctx)
39
39
  exp = ctx.exp
40
40
  count = exp.arg_names.length
41
41
  return [] if count <= max_allowed_params
@@ -27,9 +27,7 @@ module Reek
27
27
  # :reek:FeatureEnvy
28
28
  # :reek:DuplicateMethodCall: { max_calls: 2 }
29
29
  def inspect(ctx)
30
- max_allowed_params = value(MAX_ALLOWED_PARAMS_KEY,
31
- ctx,
32
- DEFAULT_MAX_ALLOWED_PARAMS)
30
+ max_allowed_params = value(MAX_ALLOWED_PARAMS_KEY, ctx)
33
31
  ctx.local_nodes(:yield).select do |yield_node|
34
32
  yield_node.args.length > max_allowed_params
35
33
  end.map do |yield_node|
@@ -13,9 +13,8 @@ module Reek
13
13
  class NestedIterators < SmellDetector
14
14
  # Struct for conveniently associating iterators with their depth (that is, their nesting).
15
15
  Iterator = Struct.new :exp, :depth do
16
- include Comparable
17
- def <=>(other)
18
- depth <=> other.depth
16
+ def line
17
+ exp.line
19
18
  end
20
19
  end
21
20
 
@@ -36,41 +35,55 @@ module Reek
36
35
  )
37
36
  end
38
37
 
39
- #
40
- # Attempts to find the deepest nested iterator and warns if it's depth
41
- # is bigger than our allowed maximum.
38
+ # Generates a smell warning for each independent deepest nesting depth
39
+ # that is greater than our allowed maximum. This means if two iterators
40
+ # with the same depth were found, we combine them into one warning and
41
+ # merge the line information.
42
42
  #
43
43
  # @return [Array<SmellWarning>]
44
44
  #
45
- # :reek:TooManyStatements: { max_statements: 6 }
46
45
  def inspect(ctx)
47
- configure_ignore_iterators(ctx)
48
- deepest_iterator = find_deepest_iterator ctx
49
- return [] unless deepest_iterator
50
- depth = deepest_iterator.depth
51
- return [] unless depth > max_nesting(ctx)
46
+ configure_ignore_iterators ctx
47
+ violations = find_violations ctx
52
48
 
53
- [smell_warning(
54
- context: ctx,
55
- lines: [deepest_iterator.exp.line],
56
- message: "contains iterators nested #{depth} deep",
57
- parameters: { name: ctx.full_name, count: depth })]
49
+ violations.group_by(&:depth).map do |depth, group|
50
+ lines = group.map(&:line)
51
+ smell_warning(
52
+ context: ctx,
53
+ lines: lines,
54
+ message: "contains iterators nested #{depth} deep",
55
+ parameters: { name: ctx.full_name, count: depth })
56
+ end
58
57
  end
59
58
 
60
59
  private
61
60
 
62
61
  attr_accessor :ignore_iterators
63
62
 
63
+ # Finds the set of independent most deeply nested iterators that are
64
+ # nested more deeply than allowed.
65
+ #
66
+ # Here, independent means that if iterator A is contained within iterator
67
+ # B, we only include A. But if iterators A and B are both contained in
68
+ # iterator C, but A is not contained in B, nor B in A, both A and B are
69
+ # included.
70
+ #
71
+ # @return [Array<Iterator>]
72
+ #
73
+ def find_violations(ctx)
74
+ candidates = find_candidates ctx
75
+ max_allowed_nesting = max_nesting(ctx)
76
+ candidates.select { |it| it.depth > max_allowed_nesting }
77
+ end
78
+
79
+ # Finds the set of independent most deeply nested iterators regardless of
80
+ # nesting depth.
64
81
  #
65
- # @return [Iterator|nil]
82
+ # @return [Array<Iterator>]
66
83
  #
67
- def find_deepest_iterator(ctx)
84
+ def find_candidates(ctx)
68
85
  exp = ctx.exp
69
- return nil unless exp.find_nodes([:block])
70
- scout(parent: exp, exp: exp, depth: 0).
71
- flatten.
72
- sort.
73
- last
86
+ scout(exp: exp, depth: 0)
74
87
  end
75
88
 
76
89
  # A little digression into parser's sexp is necessary here:
@@ -79,36 +92,38 @@ module Reek
79
92
  # foo.each() do ... end
80
93
  # this will end up as:
81
94
  #
82
- # "foo.each() do ... end" -> the iterator below
83
- # "each()" -> the "call" below
84
- # "do ... end" -> the "block" below
85
- #
86
- # @param parent [AST::Node] The parent iterator
95
+ # "foo.each() do ... end" -> one of the :block nodes
96
+ # "each()" -> the node's "call"
97
+ # "do ... end" -> the node's "block"
87
98
  #
88
99
  # @param exp [AST::Node]
89
100
  # The given expression to analyze.
90
- # Will be nil on empty blocks so we'll return just the parent iterator
91
101
  #
92
102
  # @param depth [Integer]
93
103
  #
94
104
  # @return [Array<Iterator>]
95
105
  #
96
- def scout(parent:, exp:, depth:)
97
- return [Iterator.new(parent, depth)] unless exp
98
- iterators = exp.find_nodes([:block])
99
- return [Iterator.new(parent, depth)] if iterators.empty?
100
- iterators.map do |iterator|
106
+ # :reek:TooManyStatements: { max_statements: 6 }
107
+ def scout(exp:, depth:)
108
+ return [] unless exp
109
+ exp.find_nodes([:block]).flat_map do |iterator|
110
+ new_depth = increment_depth(iterator, depth)
101
111
  # 1st case: we recurse down the given block of the iterator. In this case
102
112
  # we need to check if we should increment the depth.
103
113
  # 2nd case: we recurse down the associated call of the iterator. In this case
104
114
  # the depth stays the same.
105
- scout(parent: iterator, exp: iterator.block, depth: increment_depth(iterator, depth)) +
106
- scout(parent: iterator, exp: iterator.call, depth: depth)
115
+ nested_iterators = scout(exp: iterator.block, depth: new_depth) +
116
+ scout(exp: iterator.call, depth: depth)
117
+ if nested_iterators.empty?
118
+ Iterator.new(iterator, new_depth)
119
+ else
120
+ nested_iterators
121
+ end
107
122
  end
108
123
  end
109
124
 
110
125
  def configure_ignore_iterators(ctx)
111
- self.ignore_iterators = value(IGNORE_ITERATORS_KEY, ctx, DEFAULT_IGNORE_ITERATORS)
126
+ self.ignore_iterators = value(IGNORE_ITERATORS_KEY, ctx)
112
127
  end
113
128
 
114
129
  def increment_depth(iterator, depth)
@@ -116,7 +131,7 @@ module Reek
116
131
  end
117
132
 
118
133
  def max_nesting(ctx)
119
- value(MAX_ALLOWED_NESTING_KEY, ctx, DEFAULT_MAX_ALLOWED_NESTING)
134
+ value(MAX_ALLOWED_NESTING_KEY, ctx)
120
135
  end
121
136
 
122
137
  # :reek:FeatureEnvy
@@ -49,7 +49,7 @@ module Reek
49
49
  # :reek:TooManyStatements: { max_statements: 6 }
50
50
  # :reek:DuplicateMethodCall: { max_calls: 2 }
51
51
  def inspect(ctx)
52
- max_identical_ifs = value(MAX_IDENTICAL_IFS_KEY, ctx, DEFAULT_MAX_IFS)
52
+ max_identical_ifs = value(MAX_IDENTICAL_IFS_KEY, ctx)
53
53
  conditional_counts(ctx).select do |_key, lines|
54
54
  lines.length > max_identical_ifs
55
55
  end.map do |key, lines|
@@ -29,13 +29,14 @@ module Reek
29
29
  Overrides.new(options.fetch(OVERRIDES_KEY, {})).for_context(context)
30
30
  end
31
31
 
32
- # Retrieves the value, if any, for the given +key+.
32
+ # Retrieves the value, if any, for the given +key+ in the given +context+.
33
33
  #
34
- # Returns +fall_back+ if this config has no value for the key.
34
+ # Raises an error if neither the context nor this config have a value for
35
+ # the key.
35
36
  #
36
- def value(key, context, fall_back)
37
+ def value(key, context)
37
38
  overrides_for(context).each { |conf| return conf[key] if conf.key?(key) }
38
- options.fetch(key, fall_back)
39
+ options.fetch(key)
39
40
  end
40
41
 
41
42
  private
@@ -52,7 +52,7 @@ module Reek
52
52
  end
53
53
 
54
54
  def exception?(context)
55
- context.matches?(value(EXCLUDE_KEY, context, DEFAULT_EXCLUDE_SET))
55
+ context.matches?(value(EXCLUDE_KEY, context))
56
56
  end
57
57
 
58
58
  def self.todo_configuration_for(smells)
@@ -69,8 +69,8 @@ module Reek
69
69
  config.enabled? && config_for(context)[SmellConfiguration::ENABLED_KEY] != false
70
70
  end
71
71
 
72
- def value(key, ctx, fall_back)
73
- config_for(ctx)[key] || config.value(key, ctx, fall_back)
72
+ def value(key, ctx)
73
+ config_for(ctx)[key] || config.value(key, ctx)
74
74
  end
75
75
 
76
76
  def config_for(ctx)
@@ -35,7 +35,7 @@ module Reek
35
35
  # @return [Array<SmellWarning>]
36
36
  #
37
37
  def inspect(ctx)
38
- max_allowed_ivars = value(MAX_ALLOWED_IVARS_KEY, ctx, DEFAULT_MAX_IVARS)
38
+ max_allowed_ivars = value(MAX_ALLOWED_IVARS_KEY, ctx)
39
39
  count = ctx.local_nodes(:ivasgn).map { |ivasgn| ivasgn.children.first }.uniq.length
40
40
  return [] if count <= max_allowed_ivars
41
41
  [smell_warning(
@@ -37,7 +37,7 @@ module Reek
37
37
  # @return [Array<SmellWarning>]
38
38
  #
39
39
  def inspect(ctx)
40
- max_allowed_methods = value(MAX_ALLOWED_METHODS_KEY, ctx, DEFAULT_MAX_METHODS)
40
+ max_allowed_methods = value(MAX_ALLOWED_METHODS_KEY, ctx)
41
41
  # TODO: Only checks instance methods!
42
42
  actual = ctx.node_instance_methods.length
43
43
  return [] if actual <= max_allowed_methods
@@ -29,9 +29,7 @@ module Reek
29
29
  # @return [Array<SmellWarning>]
30
30
  #
31
31
  def inspect(ctx)
32
- max_allowed_statements = value(MAX_ALLOWED_STATEMENTS_KEY,
33
- ctx,
34
- DEFAULT_MAX_STATEMENTS)
32
+ max_allowed_statements = value(MAX_ALLOWED_STATEMENTS_KEY, ctx)
35
33
  count = ctx.number_of_statements
36
34
  return [] if count <= max_allowed_statements
37
35
  [smell_warning(
@@ -56,11 +56,11 @@ module Reek
56
56
  end
57
57
 
58
58
  def reject_patterns(context)
59
- Array value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
59
+ Array value(REJECT_KEY, context)
60
60
  end
61
61
 
62
62
  def accept_patterns(context)
63
- Array value(ACCEPT_KEY, context, DEFAULT_ACCEPT_PATTERNS)
63
+ Array value(ACCEPT_KEY, context)
64
64
  end
65
65
  end
66
66
  end
@@ -71,11 +71,11 @@ module Reek
71
71
  end
72
72
 
73
73
  def reject_patterns(context)
74
- Array value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
74
+ Array value(REJECT_KEY, context)
75
75
  end
76
76
 
77
77
  def accept_patterns(context)
78
- Array value(ACCEPT_KEY, context, DEFAULT_ACCEPT_PATTERNS)
78
+ Array value(ACCEPT_KEY, context)
79
79
  end
80
80
  end
81
81
  end
@@ -65,11 +65,11 @@ module Reek
65
65
  end
66
66
 
67
67
  def reject_patterns(context)
68
- Array value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
68
+ Array value(REJECT_KEY, context)
69
69
  end
70
70
 
71
71
  def accept_patterns(context)
72
- Array value(ACCEPT_KEY, context, DEFAULT_ACCEPT_PATTERNS)
72
+ Array value(ACCEPT_KEY, context)
73
73
  end
74
74
 
75
75
  # :reek:UtilityFunction
@@ -49,8 +49,8 @@ module Reek
49
49
  # @return [Array<SmellWarning>]
50
50
  #
51
51
  def inspect(ctx)
52
- self.reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
53
- self.accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
52
+ self.reject_names = value(REJECT_KEY, ctx)
53
+ self.accept_names = value(ACCEPT_KEY, ctx)
54
54
  variable_names(ctx.exp).select do |name, _lines|
55
55
  bad_name?(name, ctx)
56
56
  end.map do |name, lines|
@@ -81,7 +81,7 @@ module Reek
81
81
  # :reek:FeatureEnvy
82
82
  def ignore_method?(ctx, method)
83
83
  # ignore_contexts will be e.g. ["Foo::Smelly#my_method", "..."]
84
- ignore_contexts = value(EXCLUDE_KEY, ctx, DEFAULT_EXCLUDE_SET)
84
+ ignore_contexts = value(EXCLUDE_KEY, ctx)
85
85
  ignore_contexts.any? do |ignore_context|
86
86
  full_name = "#{method.parent.full_name}##{method.name}"
87
87
  full_name[ignore_context]
@@ -41,6 +41,10 @@ module Reek
41
41
  PUBLIC_METHODS_ONLY_KEY = 'public_methods_only'.freeze
42
42
  PUBLIC_METHODS_ONLY_DEFAULT = false
43
43
 
44
+ def self.default_config
45
+ super.merge(PUBLIC_METHODS_ONLY_KEY => PUBLIC_METHODS_ONLY_DEFAULT)
46
+ end
47
+
44
48
  class << self
45
49
  def contexts # :nodoc:
46
50
  [:def]
@@ -76,7 +80,7 @@ module Reek
76
80
 
77
81
  def ignore_method?(method_ctx)
78
82
  method_ctx.non_public_visibility? &&
79
- value(PUBLIC_METHODS_ONLY_KEY, method_ctx, PUBLIC_METHODS_ONLY_DEFAULT)
83
+ value(PUBLIC_METHODS_ONLY_KEY, method_ctx)
80
84
  end
81
85
  end
82
86
  end
@@ -6,6 +6,9 @@ end
6
6
  require_relative '../tree_dresser'
7
7
  require_relative '../ast/node'
8
8
 
9
+ # Opt in to new way of representing lambdas
10
+ Parser::Builders::Default.emit_lambda = true
11
+
9
12
  module Reek
10
13
  module Source
11
14
  #
data/lib/reek/version.rb CHANGED
@@ -7,6 +7,6 @@ module Reek
7
7
  # @public
8
8
  module Version
9
9
  # @public
10
- STRING = '4.0.3'.freeze
10
+ STRING = '4.0.4'.freeze
11
11
  end
12
12
  end
data/reek.gemspec CHANGED
@@ -22,6 +22,6 @@ Gem::Specification.new do |s|
22
22
  s.summary = 'Code smell detector for Ruby'
23
23
 
24
24
  s.add_runtime_dependency 'codeclimate-engine-rb', '~> 0.3.1'
25
- s.add_runtime_dependency 'parser', '~> 2.3', '>= 2.3.0.6'
25
+ s.add_runtime_dependency 'parser', '~> 2.3.1', '>= 2.3.1.2'
26
26
  s.add_runtime_dependency 'rainbow', '~> 2.0'
27
27
  end
@@ -82,6 +82,7 @@ RSpec.describe Reek::Smells::NestedIterators do
82
82
  }
83
83
  end
84
84
  EOS
85
+ expect(src).not_to reek_of(:NestedIterators, count: 2)
85
86
  expect(src).to reek_of(:NestedIterators, count: 3)
86
87
  end
87
88
 
@@ -112,7 +113,7 @@ RSpec.describe Reek::Smells::NestedIterators do
112
113
  describe 'inspect / warnings' do
113
114
  let(:detector) { build(:smell_detector, smell_type: :NestedIterators) }
114
115
 
115
- it 'reports correctly' do
116
+ it 'reports a sensible warning message' do
116
117
  source = <<-EOS
117
118
  def foo
118
119
  bar do |bar|
@@ -120,41 +121,40 @@ RSpec.describe Reek::Smells::NestedIterators do
120
121
  end
121
122
  end
122
123
  EOS
123
- warnings = detector.inspect(build(:method_context, source: source))
124
- warning = warnings.first
124
+ expect(source).to reek_of(:NestedIterators, message: 'contains iterators nested 2 deep')
125
+ end
125
126
 
126
- expect(warning.smell_type).to eq(Reek::Smells::NestedIterators.smell_type)
127
- expect(warning.parameters[:name]).to eq('foo')
128
- expect(warning.lines).to eq([3])
127
+ it 'reports the name of the method and line of the deepest iterator' do
128
+ source = <<-EOS
129
+ def foo
130
+ bar do |bar|
131
+ baz {|baz| }
132
+ end
133
+ end
134
+ EOS
135
+ expect(source).to reek_of(:NestedIterators, name: 'foo', lines: [3])
129
136
  end
130
137
 
131
- it 'should report nested iterators only once per method' do
138
+ it 'reports all lines on which nested iterators occur' do
132
139
  source = <<-EOS
133
- def bad(fred)
140
+ def bad
134
141
  @fred.each {|item| item.each {|part| @joe.send} }
135
142
  @jim.each {|ting| ting.each {|piece| @hal.send} }
136
143
  end
137
144
  EOS
138
145
 
139
- warnings = detector.inspect(build(:method_context, source: source))
140
- expect(warnings.size).to eq(1)
141
- warning = warnings.first
142
- expect(warning.parameters[:name]).to eq('bad')
146
+ expect(source).to reek_of(:NestedIterators, name: 'bad', lines: [2, 3])
143
147
  end
144
148
 
145
- it 'reports nested iterators only once per method even if levels are different' do
149
+ it 'reports separete cases of nested iterators if levels are different' do
146
150
  source = <<-EOS
147
- def bad(fred)
151
+ def bad
148
152
  @fred.each {|item| item.each {|part| part.foo} }
149
153
  @jim.each {|ting| ting.each {|piece| piece.each {|atom| atom.foo } } }
150
154
  end
151
155
  EOS
152
- warnings = detector.inspect(build(:method_context, source: source))
153
- expect(warnings.size).to eq(1)
154
- warning = warnings.first
155
-
156
- expect(warning.parameters[:name]).to eq('bad')
157
- expect(warning.lines).to eq([3])
156
+ expect(source).to reek_of(:NestedIterators, name: 'bad', lines: [2], count: 2)
157
+ expect(source).to reek_of(:NestedIterators, name: 'bad', lines: [3], count: 3)
158
158
  end
159
159
  end
160
160
 
@@ -2,11 +2,6 @@ require_relative '../../spec_helper'
2
2
  require_lib 'reek/smells/smell_configuration'
3
3
 
4
4
  RSpec.describe Reek::Smells::SmellConfiguration do
5
- it 'returns the default value when key not found' do
6
- cf = described_class.new({})
7
- expect(cf.value('fred', nil, 27)).to eq(27)
8
- end
9
-
10
5
  context 'when overriding default configs' do
11
6
  let(:base_config) do
12
7
  {
@@ -217,6 +217,28 @@ RSpec.describe Reek::Smells::UtilityFunction do
217
217
  end
218
218
  end
219
219
 
220
+ context 'with the default configuration' do
221
+ it 'reports private methods' do
222
+ src = <<-EOS
223
+ class C
224
+ private
225
+ def m1(a) a.to_s; end
226
+ end
227
+ EOS
228
+ expect(src).to reek_of(:UtilityFunction)
229
+ end
230
+
231
+ it 'reports protected methods' do
232
+ src = <<-EOS
233
+ class C
234
+ protected
235
+ def m1(a) a.to_s; end
236
+ end
237
+ EOS
238
+ expect(src).to reek_of(:UtilityFunction)
239
+ end
240
+ end
241
+
220
242
  describe 'disabling UtilityFunction via configuration for non-public methods' do
221
243
  let(:config) do
222
244
  { Reek::Smells::UtilityFunction::PUBLIC_METHODS_ONLY_KEY => true }
data/spec/spec_helper.rb CHANGED
@@ -82,6 +82,11 @@ RSpec.configure do |config|
82
82
  config.mock_with :rspec do |mocks|
83
83
  mocks.verify_partial_doubles = true
84
84
  end
85
+
86
+ # Avoid infinitely running tests. This is mainly useful when running mutant.
87
+ config.around(:each) do |example|
88
+ Timeout.timeout(5, &example)
89
+ end
85
90
  end
86
91
 
87
92
  private
data/tasks/ataru.rake ADDED
@@ -0,0 +1,4 @@
1
+ desc 'Runs ataru to check if our docs are still in sync with our code'
2
+ task :ataru do
3
+ system 'bundle exec ataru check'
4
+ end
data/tasks/mutant.rake ADDED
@@ -0,0 +1,5 @@
1
+ desc 'Runs mutant'
2
+ task :mutant do
3
+ system 'RUBY_THREAD_VM_STACK_SIZE=64000 bundle exec mutant --include lib '\
4
+ "--require reek --use rspec --since master --jobs 4 'Reek*'"
5
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reek
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.3
4
+ version: 4.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Rutherford
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2016-05-23 00:00:00.000000000 Z
14
+ date: 2016-06-12 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: codeclimate-engine-rb
@@ -33,20 +33,20 @@ dependencies:
33
33
  requirements:
34
34
  - - "~>"
35
35
  - !ruby/object:Gem::Version
36
- version: '2.3'
36
+ version: 2.3.1
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 2.3.0.6
39
+ version: 2.3.1.2
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '2.3'
46
+ version: 2.3.1
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: 2.3.0.6
49
+ version: 2.3.1.2
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: rainbow
52
52
  requirement: !ruby/object:Gem::Requirement
@@ -372,8 +372,10 @@ files:
372
372
  - spec/samples/two_smelly_files/dirty_one.rb
373
373
  - spec/samples/two_smelly_files/dirty_two.rb
374
374
  - spec/spec_helper.rb
375
+ - tasks/ataru.rake
375
376
  - tasks/configuration.rake
376
377
  - tasks/console.rake
378
+ - tasks/mutant.rake
377
379
  - tasks/reek.rake
378
380
  - tasks/rubocop.rake
379
381
  - tasks/test.rake