reek 3.4.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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