reek 3.4.1 → 3.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 846eb328f05e66b28b63cb9103b710d75a61a98b
4
- data.tar.gz: f870a86b912aca22b1720902098afeb575de4a03
3
+ metadata.gz: 777f1ba21321886d04fe6cb9be80e175250f8490
4
+ data.tar.gz: 574babd716780ea8013ceb2b485dda1e3eaa521d
5
5
  SHA512:
6
- metadata.gz: b2ee82efb561b2270381528407b1488b695c24f954e78688999df29b3dc17246faa473b523454a7a1c0e65e8aea4201270786a3df2e251ef57f2760ea924228e
7
- data.tar.gz: a80ad9e5f23f1e5db3d5e8df22d55e8cf17656a028582ce25af3d2c00c9bde74002a13f97724ef09c7ea12205e3b0ab0c3ee9a2612aad8fb346ef12f2d4c918f
6
+ metadata.gz: 4d5a1ea491b81408ae7c6ee5dc8699b21e7df7ecdfa888d3826556acb677dabf6a2a145677c89e783f5aaaf58b914ab3138ce444e393a19e476e3c6940ff6bbe
7
+ data.tar.gz: cd1ab7b709b02c9d2756fc716aee8418295bebf48757fdaff1dab3fca689bb84a8d5b359fc6026e4f3d8450d80128b36e00ba12138f904596a9752ad200f69e4
data/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 3.5.0 (2015-09-28)
6
+
7
+ * (troessner) Ignore iterators without block arguments for NestedIterators
8
+
5
9
  ## 3.4.1 (2015-09-24)
6
10
 
7
11
  * (chastell) Fix parsing `Foo = bar.new(new)`
data/docs/API.md CHANGED
@@ -76,7 +76,7 @@ IrresponsibleModule:
76
76
  You can now use either
77
77
 
78
78
  ```Ruby
79
- Reek::Configuration::AppConfiguration.from_path Pathname.new('config.reek`)
79
+ Reek::Configuration::AppConfiguration.from_path Pathname.new('config.reek')
80
80
  ```
81
81
 
82
82
  but you can also pass a hash via `Reek::Configuration::AppConfiguration.from_map`.
@@ -33,6 +33,21 @@ test.rb -- 1 warning:
33
33
 
34
34
  Nested Iterators reports failing methods only once.
35
35
  `Object#tap` is ignored by default and thus does not count as iterator.
36
+ Furthermore iterators without block arguments are not counted, e.g.:
37
+
38
+
39
+ ```Ruby
40
+ def foo
41
+ before do
42
+ item.each do |part|
43
+ puts part
44
+ end
45
+ end
46
+ end
47
+ ```
48
+
49
+ would not smell of NestedIterators (given a maximum allowed nesting of 1) since the
50
+ `before` would not be counted (because it doesn't pass any arguments to the block).
36
51
 
37
52
  ## Configuration
38
53
 
@@ -24,8 +24,8 @@ Feature: Directory directives
24
24
  class UsersController < ApplicationController
25
25
  def show
26
26
  respond_with do |format|
27
- format.json { @user.to_custom_json }
28
- format.xlm { @user.to_fancy_xml }
27
+ format.json { |json| @user.to_custom_json }
28
+ format.xml { |xml| @user.to_fancy_xml }
29
29
  end
30
30
  end
31
31
  end
@@ -94,7 +94,7 @@ Feature: Directory directives
94
94
  class UsersController < ApplicationController
95
95
  def show
96
96
  respond_with do |format|
97
- format.json { @user.to_custom_json }
97
+ format.json { |json| @user.to_custom_json }
98
98
  end
99
99
  end
100
100
  end
@@ -145,7 +145,7 @@ Feature: Directory directives
145
145
  class UsersController < ApplicationController
146
146
  def show
147
147
  respond_with do |format|
148
- format.json { @user.to_custom_json }
148
+ format.json { |json| @user.to_custom_json }
149
149
  end
150
150
  end
151
151
  end
@@ -155,7 +155,7 @@ Feature: Directory directives
155
155
  class ProjectController < ApplicationController
156
156
  def show
157
157
  respond_with do |format|
158
- format.json { @project.to_custom_json }
158
+ format.json { |json| @project.to_custom_json }
159
159
  end
160
160
  end
161
161
  end
@@ -251,8 +251,8 @@ Feature: Directory directives
251
251
  """
252
252
  class Klass
253
253
  def meth
254
- respond_to do
255
- answer_to do
254
+ respond_to do |arg|
255
+ answer_to do |arg|
256
256
  end
257
257
  end
258
258
  end
@@ -262,8 +262,8 @@ Feature: Directory directives
262
262
  """
263
263
  class Klazz
264
264
  def meth
265
- respond_to do
266
- answer_to do
265
+ respond_to do |arg|
266
+ answer_to do |arg|
267
267
  end
268
268
  end
269
269
  end
@@ -59,7 +59,7 @@ Feature: Basic smell detection
59
59
  Module#inline calls Inline.const_get(lang) 2 times (DuplicateMethodCall)
60
60
  Module#inline calls options[:testing] 2 times (DuplicateMethodCall)
61
61
  Module#inline has approx 12 statements (TooManyStatements)
62
- optparse.rb -- 121 warnings:
62
+ optparse.rb -- 117 warnings:
63
63
  OptionParser has at least 42 methods (TooManyMethods)
64
64
  OptionParser has at least 6 instance variables (TooManyInstanceVariables)
65
65
  OptionParser has the variable name 'f' (UncommunicativeVariableName)
@@ -73,7 +73,6 @@ Feature: Basic smell detection
73
73
  OptionParser tests not_style at least 3 times (RepeatedConditional)
74
74
  OptionParser tests s at least 7 times (RepeatedConditional)
75
75
  OptionParser#banner is a writable attribute (Attribute)
76
- OptionParser#complete contains iterators nested 2 deep (NestedIterators)
77
76
  OptionParser#complete has 4 parameters (LongParameterList)
78
77
  OptionParser#complete has approx 6 statements (TooManyStatements)
79
78
  OptionParser#complete has boolean parameter 'icase' (BooleanParameter)
@@ -93,7 +92,7 @@ Feature: Basic smell detection
93
92
  OptionParser#make_switch calls sdesc << "-#{q}" 2 times (DuplicateMethodCall)
94
93
  OptionParser#make_switch calls search(:atype, FalseClass) 2 times (DuplicateMethodCall)
95
94
  OptionParser#make_switch calls search(:atype, o) 6 times (DuplicateMethodCall)
96
- OptionParser#make_switch contains iterators nested 3 deep (NestedIterators)
95
+ OptionParser#make_switch contains iterators nested 2 deep (NestedIterators)
97
96
  OptionParser#make_switch has approx 72 statements (TooManyStatements)
98
97
  OptionParser#make_switch has the variable name 'a' (UncommunicativeVariableName)
99
98
  OptionParser#make_switch has the variable name 'c' (UncommunicativeVariableName)
@@ -114,7 +113,6 @@ Feature: Basic smell detection
114
113
  OptionParser#parse_in_order calls setter.call(sw.switch_name, val) 2 times (DuplicateMethodCall)
115
114
  OptionParser#parse_in_order calls sw.block 2 times (DuplicateMethodCall)
116
115
  OptionParser#parse_in_order calls sw.switch_name 2 times (DuplicateMethodCall)
117
- OptionParser#parse_in_order contains iterators nested 3 deep (NestedIterators)
118
116
  OptionParser#parse_in_order has approx 35 statements (TooManyStatements)
119
117
  OptionParser#permute calls argv[0] 2 times (DuplicateMethodCall)
120
118
  OptionParser#permute refers to argv more than self (maybe move it to another class?) (FeatureEnvy)
@@ -133,7 +131,6 @@ Feature: Basic smell detection
133
131
  OptionParser::Arguable has initialize method (ModuleInitialize)
134
132
  OptionParser::Arguable#options has approx 6 statements (TooManyStatements)
135
133
  OptionParser::Arguable#options= is controlled by argument opt (ControlParameter)
136
- OptionParser::CompletingHash#match contains iterators nested 2 deep (NestedIterators)
137
134
  OptionParser::Completion#complete calls candidates.size 2 times (DuplicateMethodCall)
138
135
  OptionParser::Completion#complete calls k.id2name 2 times (DuplicateMethodCall)
139
136
  OptionParser::Completion#complete has approx 23 statements (TooManyStatements)
@@ -172,7 +169,6 @@ Feature: Basic smell detection
172
169
  OptionParser::Switch#summarize calls left.shift 2 times (DuplicateMethodCall)
173
170
  OptionParser::Switch#summarize calls left[(-1)] 3 times (DuplicateMethodCall)
174
171
  OptionParser::Switch#summarize calls s.length 3 times (DuplicateMethodCall)
175
- OptionParser::Switch#summarize contains iterators nested 2 deep (NestedIterators)
176
172
  OptionParser::Switch#summarize has 5 parameters (LongParameterList)
177
173
  OptionParser::Switch#summarize has approx 28 statements (TooManyStatements)
178
174
  OptionParser::Switch#summarize has the variable name 'l' (UncommunicativeVariableName)
@@ -215,7 +211,7 @@ Feature: Basic smell detection
215
211
  RedCloth#blocks is controlled by argument deep_code (ControlParameter)
216
212
  RedCloth#blocks refers to blk more than self (maybe move it to another class?) (FeatureEnvy)
217
213
  RedCloth#clean_html calls tags[tag] 2 times (DuplicateMethodCall)
218
- RedCloth#clean_html contains iterators nested 3 deep (NestedIterators)
214
+ RedCloth#clean_html contains iterators nested 2 deep (NestedIterators)
219
215
  RedCloth#clean_html doesn't depend on instance state (maybe move it to another class?) (UtilityFunction)
220
216
  RedCloth#clean_html has approx 15 statements (TooManyStatements)
221
217
  RedCloth#clean_html has the variable name 'q' (UncommunicativeVariableName)
@@ -283,5 +279,5 @@ Feature: Basic smell detection
283
279
  RedCloth#textile_popup_help has the parameter name 'windowW' (UncommunicativeParameterName)
284
280
  RedCloth#to_html has approx 26 statements (TooManyStatements)
285
281
  RedCloth#v_align doesn't depend on instance state (maybe move it to another class?) (UtilityFunction)
286
- 269 total warnings
282
+ 265 total warnings
287
283
  """
@@ -37,5 +37,7 @@ World do
37
37
  end
38
38
 
39
39
  Before do
40
- @aruba_timeout_seconds = 30
40
+ Aruba.configure do |config|
41
+ config.exit_timeout = 30
42
+ end
41
43
  end
data/lib/reek/ast/node.rb CHANGED
@@ -31,20 +31,10 @@ module Reek
31
31
  comment_lines.map(&:text).join("\n")
32
32
  end
33
33
 
34
- # @deprecated
35
- def [](index)
36
- elements[index]
37
- end
38
-
39
34
  def line
40
35
  loc && loc.line
41
36
  end
42
37
 
43
- # @deprecated
44
- def first
45
- type
46
- end
47
-
48
38
  #
49
39
  # Carries out a depth-first traversal of this syntax tree, yielding
50
40
  # every Sexp of type `target_type`. The traversal ignores any node
@@ -129,10 +119,6 @@ module Reek
129
119
  def each_sexp
130
120
  children.each { |elem| yield elem if elem.is_a? ::Parser::AST::Node }
131
121
  end
132
-
133
- def elements
134
- [type, *children]
135
- end
136
122
  end
137
123
  end
138
124
  end
@@ -120,10 +120,10 @@ module Reek
120
120
 
121
121
  # Base module for utility methods for :and and :or nodes.
122
122
  module LogicOperatorBase
123
- def condition() self[1] end
123
+ def condition() children.first end
124
124
 
125
125
  def body_nodes(type, ignoring = [])
126
- self[2].find_nodes type, ignoring
126
+ children[1].find_nodes type, ignoring
127
127
  end
128
128
  end
129
129
 
@@ -139,12 +139,12 @@ module Reek
139
139
 
140
140
  # Utility methods for :attrasgn nodes.
141
141
  module AttrasgnNode
142
- def args() self[3] end
142
+ def args() children[2] end
143
143
  end
144
144
 
145
145
  # Utility methods for :case nodes.
146
146
  module CaseNode
147
- def condition() self[1] end
147
+ def condition() children.first end
148
148
 
149
149
  def body_nodes(type, ignoring = [])
150
150
  children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
@@ -177,7 +177,7 @@ module Reek
177
177
  end
178
178
 
179
179
  def arg_names
180
- args.map { |arg| arg[1] }
180
+ args.map { |arg| arg.children.first }
181
181
  end
182
182
 
183
183
  def module_creation_call?
@@ -215,7 +215,7 @@ module Reek
215
215
 
216
216
  # Base module for utility methods for nodes representing variables.
217
217
  module VariableBase
218
- def name() self[1] end
218
+ def name() children.first end
219
219
  end
220
220
 
221
221
  # Utility methods for :cvar nodes.
@@ -301,11 +301,11 @@ module Reek
301
301
 
302
302
  # Utility methods for :def nodes.
303
303
  module DefNode
304
- def name() self[1] end
305
- def argslist() self[2] end
304
+ def name() children.first end
305
+ def argslist() children[1] end
306
306
 
307
307
  def body
308
- self[3]
308
+ children[2]
309
309
  end
310
310
 
311
311
  def full_name(outer)
@@ -323,12 +323,12 @@ module Reek
323
323
 
324
324
  # Utility methods for :defs nodes.
325
325
  module DefsNode
326
- def receiver() self[1] end
327
- def name() self[2] end
328
- def argslist() self[3] end
326
+ def receiver() children.first end
327
+ def name() children[1] end
328
+ def argslist() children[2] end
329
329
 
330
330
  def body
331
- self[4]
331
+ children[3]
332
332
  end
333
333
 
334
334
  include MethodNodeBase
@@ -345,7 +345,7 @@ module Reek
345
345
 
346
346
  # Utility methods for :if nodes.
347
347
  module IfNode
348
- def condition() self[1] end
348
+ def condition() children.first end
349
349
 
350
350
  def body_nodes(type, ignoring = [])
351
351
  children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
@@ -354,23 +354,27 @@ module Reek
354
354
 
355
355
  # Utility methods for :block nodes.
356
356
  module BlockNode
357
- def call() self[1] end
358
- def args() self[2] end
359
- def block() self[3] end
360
- def parameters() self[2] || [] end
357
+ def call() children.first end
358
+ def args() children[1] end
359
+ def block() children[2] end
360
+ def parameters() children[1] || [] end
361
361
 
362
362
  def parameter_names
363
- parameters[1..-1].to_a
363
+ parameters.children
364
364
  end
365
365
 
366
366
  def simple_name
367
367
  :block
368
368
  end
369
+
370
+ def without_block_arguments?
371
+ args.components.empty?
372
+ end
369
373
  end
370
374
 
371
375
  # Utility methods for :lit nodes.
372
376
  module LitNode
373
- def value() self[1] end
377
+ def value() children.first end
374
378
  end
375
379
 
376
380
  # Utility methods for :const nodes.
@@ -425,7 +429,7 @@ module Reek
425
429
  # Utility methods for :class nodes.
426
430
  module ClassNode
427
431
  include ModuleNode
428
- def superclass() self[2] end
432
+ def superclass() children[1] end
429
433
  end
430
434
 
431
435
  # Utility methods for :casgn nodes.
@@ -464,7 +468,7 @@ module Reek
464
468
 
465
469
  # Utility methods for :yield nodes.
466
470
  module YieldNode
467
- def args() self[1..-1] end
471
+ def args() children end
468
472
 
469
473
  def arg_names
470
474
  args.map { |arg| arg[1] }
@@ -42,10 +42,9 @@ module Reek
42
42
  def self.from_map(map = {})
43
43
  allocate.tap do |instance|
44
44
  instance.instance_eval do
45
- self.directory_directives = map.fetch(:directory_directives, {}).
46
- extend(DirectoryDirectives)
47
- self.default_directive = map.fetch(:default_directive, {}).extend(DefaultDirective)
48
- self.excluded_paths = map.fetch(:excluded_paths, []).extend(ExcludedPaths)
45
+ load_values map.fetch(:directory_directives, {})
46
+ load_values map.fetch(:default_directive, {})
47
+ load_values EXCLUDE_PATHS_KEY => map.fetch(:excluded_paths, [])
49
48
  end
50
49
  end
51
50
  end
@@ -90,6 +89,10 @@ module Reek
90
89
  def find_and_load(path: nil)
91
90
  configuration_file = ConfigurationFileFinder.find_and_load(path: path)
92
91
 
92
+ load_values(configuration_file)
93
+ end
94
+
95
+ def load_values(configuration_file)
93
96
  configuration_file.each do |key, value|
94
97
  case
95
98
  when key == EXCLUDE_PATHS_KEY
@@ -8,9 +8,26 @@ module Reek
8
8
 
9
9
  # :reek:UtilityFunction
10
10
  def smell_type?(key)
11
- Reek::Smells.const_get key
12
- rescue NameError
13
- false
11
+ case key
12
+ when Class
13
+ true
14
+ when String
15
+ begin
16
+ Reek::Smells.const_defined? key
17
+ rescue NameError
18
+ false
19
+ end
20
+ end
21
+ end
22
+
23
+ # :reek:UtilityFunction
24
+ def key_to_smell_detector(key)
25
+ case key
26
+ when Class
27
+ key
28
+ else
29
+ Reek::Smells.const_get key
30
+ end
14
31
  end
15
32
 
16
33
  def error_message_for_missing_directory(pathname)
@@ -4,8 +4,10 @@ module Reek
4
4
  # Hash extension for the default directive.
5
5
  #
6
6
  module DefaultDirective
7
+ include ConfigurationValidator
8
+
7
9
  def add(key, config)
8
- self[Reek::Smells.const_get(key)] = config
10
+ self[key_to_smell_detector(key)] = config
9
11
  end
10
12
  end
11
13
  end
@@ -32,7 +32,7 @@ module Reek
32
32
  with_valid_directory(path) do |directory|
33
33
  self[directory] = config.each_with_object({}) do |(key, value), hash|
34
34
  abort(error_message_for_invalid_smell_type(key)) unless smell_type?(key)
35
- hash[Reek::Smells.const_get(key)] = value
35
+ hash[key_to_smell_detector(key)] = value
36
36
  end
37
37
  end
38
38
  self
@@ -25,7 +25,7 @@ module Reek
25
25
  # :reek:FeatureEnvy
26
26
  def examine_context(ctx)
27
27
  ctx.default_assignments.select do |_param, value|
28
- [:true, :false].include?(value[0])
28
+ [:true, :false].include?(value.type)
29
29
  end.map do |parameter, _value|
30
30
  smell_warning(
31
31
  context: ctx,
@@ -44,7 +44,6 @@ module Reek
44
44
  else
45
45
  []
46
46
  end
47
- # BUG: no longer reports nesting outside methods (eg. in Optparse)
48
47
  end
49
48
 
50
49
  private
@@ -72,9 +71,10 @@ module Reek
72
71
  result
73
72
  end
74
73
 
74
+ # :reek:FeatureEnvy
75
75
  def ignored_iterator?(exp)
76
- name = exp.call.method_name.to_s
77
- ignore_iterators.any? { |pattern| /#{pattern}/ =~ name }
76
+ ignore_iterators.any? { |pattern| /#{pattern}/ =~ exp.call.method_name } ||
77
+ exp.without_block_arguments?
78
78
  end
79
79
  end
80
80
  end
@@ -39,7 +39,7 @@ module Reek
39
39
  #
40
40
  def examine_context(ctx)
41
41
  max_allowed_ivars = value(MAX_ALLOWED_IVARS_KEY, ctx, DEFAULT_MAX_IVARS)
42
- count = ctx.local_nodes(:ivasgn).map { |ivasgn| ivasgn[1] }.uniq.length
42
+ count = ctx.local_nodes(:ivasgn).map { |ivasgn| ivasgn.children.first }.uniq.length
43
43
  return [] if count <= max_allowed_ivars
44
44
  [smell_warning(
45
45
  context: ctx,
@@ -83,18 +83,18 @@ module Reek
83
83
  def find_assignment_variable_names(exp, accumulator)
84
84
  assignment_nodes = exp.each_node(:lvasgn, [:class, :module, :defs, :def])
85
85
 
86
- case exp.first
86
+ case exp.type
87
87
  when :class, :module
88
88
  assignment_nodes += exp.each_node(:ivasgn, [:class, :module])
89
89
  end
90
90
 
91
- assignment_nodes.each { |asgn| accumulator[asgn[1]].push(asgn.line) }
91
+ assignment_nodes.each { |asgn| accumulator[asgn.children.first].push(asgn.line) }
92
92
  end
93
93
 
94
94
  # :reek:FeatureEnvy
95
95
  # :reek:TooManyStatements: { max_statements: 6 }
96
96
  def find_block_argument_variable_names(exp, accumulator)
97
- arg_search_exp = case exp.first
97
+ arg_search_exp = case exp.type
98
98
  when :class, :module
99
99
  exp
100
100
  when :defs, :def
@@ -140,14 +140,15 @@ module Reek
140
140
  alias_method :process_kwbegin, :process_begin
141
141
 
142
142
  def process_if(exp)
143
- count_clause(exp[2])
144
- count_clause(exp[3])
143
+ children = exp.children
144
+ count_clause(children[1])
145
+ count_clause(children[2])
145
146
  element.count_statements(-1)
146
147
  process_default(exp)
147
148
  end
148
149
 
149
150
  def process_while(exp)
150
- count_clause(exp[2])
151
+ count_clause(exp.children[1])
151
152
  element.count_statements(-1)
152
153
  process_default(exp)
153
154
  end
@@ -155,19 +156,19 @@ module Reek
155
156
  alias_method :process_until, :process_while
156
157
 
157
158
  def process_for(exp)
158
- count_clause(exp[3])
159
+ count_clause(exp.children[2])
159
160
  element.count_statements(-1)
160
161
  process_default(exp)
161
162
  end
162
163
 
163
164
  def process_rescue(exp)
164
- count_clause(exp[1])
165
+ count_clause(exp.children.first)
165
166
  element.count_statements(-1)
166
167
  process_default(exp)
167
168
  end
168
169
 
169
170
  def process_resbody(exp)
170
- count_statement_list(exp[2..-1].compact)
171
+ count_statement_list(exp.children[1..-1].compact)
171
172
  process_default(exp)
172
173
  end
173
174
 
data/lib/reek/version.rb CHANGED
@@ -6,6 +6,6 @@ module Reek
6
6
  # @public
7
7
  module Version
8
8
  # @public
9
- STRING = '3.4.1'
9
+ STRING = '3.5.0'
10
10
  end
11
11
  end
@@ -11,6 +11,17 @@ FactoryGirl.define do
11
11
  end
12
12
  end
13
13
 
14
+ factory :method_context, class: Reek::Context::MethodContext do
15
+ skip_create
16
+ transient do
17
+ source 'def foo; end'
18
+ end
19
+
20
+ initialize_with do
21
+ new(nil, Reek::Source::SourceCode.from(source).syntax_tree)
22
+ end
23
+ end
24
+
14
25
  factory :smell_detector, class: Reek::Smells::SmellDetector do
15
26
  skip_create
16
27
  transient do
@@ -38,7 +38,31 @@ RSpec.describe Reek::Configuration::AppConfiguration do
38
38
  end
39
39
 
40
40
  describe '#from_map' do
41
- it 'properly sets the configuration' do
41
+ let(:default_directive_value) do
42
+ { 'IrresponsibleModule' => { 'enabled' => false } }
43
+ end
44
+
45
+ let(:directory_directives_value) do
46
+ { 'spec/samples/three_clean_files' =>
47
+ { 'UtilityFunction' => { 'enabled' => false } } }
48
+ end
49
+
50
+ let(:exclude_paths_value) do
51
+ ['spec/samples/two_smelly_files',
52
+ 'spec/samples/source_with_non_ruby_files']
53
+ end
54
+
55
+ it 'properly sets the configuration from simple data structures' do
56
+ config = described_class.from_map(directory_directives: directory_directives_value,
57
+ default_directive: default_directive_value,
58
+ excluded_paths: exclude_paths_value)
59
+
60
+ expect(config.send(:excluded_paths)).to eq(expected_excluded_paths)
61
+ expect(config.send(:default_directive)).to eq(expected_default_directive)
62
+ expect(config.send(:directory_directives)).to eq(expected_directory_directives)
63
+ end
64
+
65
+ it 'properly sets the configuration from native structures' do
42
66
  config = described_class.from_map(directory_directives: expected_directory_directives,
43
67
  default_directive: expected_default_directive,
44
68
  excluded_paths: expected_excluded_paths)
@@ -51,17 +75,18 @@ RSpec.describe Reek::Configuration::AppConfiguration do
51
75
  end
52
76
 
53
77
  describe '#directive_for' do
78
+ let(:source_via) { 'spec/samples/three_clean_files/dummy.rb' }
79
+
54
80
  context 'our source is in a directory for which we have a directive' do
55
81
  let(:baz_config) { { Reek::Smells::IrresponsibleModule => { enabled: false } } }
56
82
  let(:bang_config) { { Reek::Smells::Attribute => { enabled: true } } }
57
83
 
58
84
  let(:directory_directives) do
59
85
  {
60
- Pathname.new('foo/bar/baz') => baz_config,
61
- Pathname.new('foo/bar/bang') => bang_config
86
+ 'spec/samples/two_smelly_files' => baz_config,
87
+ 'spec/samples/three_clean_files' => bang_config
62
88
  }
63
89
  end
64
- let(:source_via) { 'foo/bar/bang/dummy.rb' }
65
90
 
66
91
  it 'returns the corresponding directive' do
67
92
  configuration = described_class.from_map directory_directives: directory_directives
@@ -70,22 +95,16 @@ RSpec.describe Reek::Configuration::AppConfiguration do
70
95
  end
71
96
 
72
97
  context 'our source is not in a directory for which we have a directive' do
73
- let(:irresponsible_module_config) do
74
- { Reek::Smells::IrresponsibleModule => { enabled: false } }
75
- end
98
+ let(:default_directive) { { Reek::Smells::IrresponsibleModule => { enabled: false } } }
76
99
  let(:attribute_config) { { Reek::Smells::Attribute => { enabled: false } } }
77
- let(:default_directive) do
78
- irresponsible_module_config
79
- end
80
100
  let(:directory_directives) do
81
- { Pathname.new('foo/bar/baz') => attribute_config }
101
+ { 'spec/samples/two_smelly_files' => attribute_config }
82
102
  end
83
- let(:source_via) { 'foo/bar/bang/dummy.rb' }
84
103
 
85
104
  it 'returns the default directive' do
86
105
  configuration = described_class.from_map directory_directives: directory_directives,
87
106
  default_directive: default_directive
88
- expect(configuration.directive_for(source_via)).to eq(irresponsible_module_config)
107
+ expect(configuration.directive_for(source_via)).to eq(default_directive)
89
108
  end
90
109
  end
91
110
  end
@@ -118,7 +118,7 @@ RSpec.describe Reek::Context::CodeContext do
118
118
  end
119
119
 
120
120
  it "yields the method's full AST" do
121
- ctx.each_node(:def, []) { |exp| expect(exp[1]).to eq(:calloo) }
121
+ ctx.each_node(:def, []) { |exp| expect(exp.children.first).to eq(:calloo) }
122
122
  end
123
123
 
124
124
  context 'pruning the traversal' do
@@ -49,27 +49,9 @@ RSpec.describe Reek::Smells::NestedIterators do
49
49
  it 'detects an iterator with an empty block' do
50
50
  src = <<-EOS
51
51
  def foo
52
- bar { baz { } }
53
- end
54
- EOS
55
- expect(src).to reek_of(:NestedIterators)
56
- end
57
-
58
- it 'should report nested iterators only once per method' do
59
- src = <<-EOS
60
- def bad(fred)
61
- @fred.each {|item| item.each {|part| @joe.send} }
62
- @jim.each {|ting| ting.each {|piece| @hal.send} }
63
- end
64
- EOS
65
- expect(src).to reek_of(:NestedIterators)
66
- end
67
-
68
- it 'reports nested iterators only once per method even if levels are different' do
69
- src = <<-EOS
70
- def bad(fred)
71
- @fred.each {|item| item.each {|part| part.foo} }
72
- @jim.each {|ting| ting.each {|piece| piece.each {|atom| atom.foo } } }
52
+ bar do |bar|
53
+ baz {|baz| }
54
+ end
73
55
  end
74
56
  EOS
75
57
  expect(src).to reek_of(:NestedIterators)
@@ -103,7 +85,7 @@ RSpec.describe Reek::Smells::NestedIterators do
103
85
  expect(src).to reek_of(:NestedIterators, count: 3)
104
86
  end
105
87
 
106
- it 'handles the case where super recieves a block' do
88
+ it 'handles the case where super receives a block' do
107
89
  src = <<-EOS
108
90
  def super_call_with_block
109
91
  super do |k|
@@ -115,7 +97,7 @@ RSpec.describe Reek::Smells::NestedIterators do
115
97
  expect(src).to reek_of(:NestedIterators)
116
98
  end
117
99
 
118
- it 'handles the case where super recieves a block and arguments' do
100
+ it 'handles the case where super receives a block and arguments' do
119
101
  src = <<-EOS
120
102
  def super_call_with_block
121
103
  super(foo) do |k|
@@ -127,6 +109,71 @@ RSpec.describe Reek::Smells::NestedIterators do
127
109
  expect(src).to reek_of(:NestedIterators)
128
110
  end
129
111
 
112
+ describe 'examine_context / warnings' do
113
+ let(:detector) { build(:smell_detector, smell_type: :NestedIterators) }
114
+
115
+ it 'reports correctly' do
116
+ source = <<-EOS
117
+ def foo
118
+ bar do |bar|
119
+ baz {|baz| }
120
+ end
121
+ end
122
+ EOS
123
+ warnings = detector.examine_context(build(:method_context, source: source))
124
+ warning = warnings.first
125
+
126
+ expect(warning.smell_category).to eq(Reek::Smells::NestedIterators.smell_category)
127
+ expect(warning.smell_type).to eq(Reek::Smells::NestedIterators.smell_type)
128
+ expect(warning.parameters[:name]).to eq('foo')
129
+ expect(warning.lines).to eq([3])
130
+ end
131
+
132
+ it 'should report nested iterators only once per method' do
133
+ source = <<-EOS
134
+ def bad(fred)
135
+ @fred.each {|item| item.each {|part| @joe.send} }
136
+ @jim.each {|ting| ting.each {|piece| @hal.send} }
137
+ end
138
+ EOS
139
+
140
+ warnings = detector.examine_context(build(:method_context, source: source))
141
+ expect(warnings.size).to eq(1)
142
+ warning = warnings.first
143
+ expect(warning.parameters[:name]).to eq('bad')
144
+ end
145
+
146
+ it 'reports nested iterators only once per method even if levels are different' do
147
+ source = <<-EOS
148
+ def bad(fred)
149
+ @fred.each {|item| item.each {|part| part.foo} }
150
+ @jim.each {|ting| ting.each {|piece| piece.each {|atom| atom.foo } } }
151
+ end
152
+ EOS
153
+ warnings = detector.examine_context(build(:method_context, source: source))
154
+ expect(warnings.size).to eq(1)
155
+ warning = warnings.first
156
+
157
+ expect(warning.parameters[:name]).to eq('bad')
158
+ expect(warning.lines).to eq([3])
159
+ end
160
+ end
161
+
162
+ describe 'iterators without block arguments' do
163
+ it 'does not count those iterators' do
164
+ source = <<-EOS
165
+ def foo
166
+ before do
167
+ item.each do |part|
168
+ puts part
169
+ end
170
+ end
171
+ end
172
+ EOS
173
+ expect(source).not_to reek_of(:NestedIterators)
174
+ end
175
+ end
176
+
130
177
  context 'when the allowed nesting depth is 3' do
131
178
  let(:configuration) do
132
179
  config = { Reek::Smells::NestedIterators =>
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: 3.4.1
4
+ version: 3.5.0
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: 2015-09-24 00:00:00.000000000 Z
14
+ date: 2015-09-28 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: parser