reek 1.2.7.3 → 1.2.8

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 (76) hide show
  1. data/History.txt +17 -0
  2. data/README.md +32 -48
  3. data/config/defaults.reek +7 -1
  4. data/features/api.feature +20 -0
  5. data/features/masking_smells.feature +41 -0
  6. data/features/options.feature +4 -0
  7. data/features/rake_task.feature +14 -0
  8. data/features/yaml.feature +8 -8
  9. data/lib/reek.rb +1 -1
  10. data/lib/reek/cli/command_line.rb +9 -2
  11. data/lib/reek/cli/reek_command.rb +5 -4
  12. data/lib/reek/cli/yaml_command.rb +2 -2
  13. data/lib/reek/core/code_context.rb +10 -1
  14. data/lib/reek/core/method_context.rb +2 -2
  15. data/lib/reek/core/sniffer.rb +3 -1
  16. data/lib/reek/core/stop_context.rb +4 -0
  17. data/lib/reek/examiner.rb +2 -2
  18. data/lib/reek/rake/task.rb +16 -0
  19. data/lib/reek/smell_warning.rb +3 -2
  20. data/lib/reek/smells/attribute.rb +13 -9
  21. data/lib/reek/smells/boolean_parameter.rb +14 -9
  22. data/lib/reek/smells/class_variable.rb +16 -5
  23. data/lib/reek/smells/control_couple.rb +11 -6
  24. data/lib/reek/smells/data_clump.rb +33 -30
  25. data/lib/reek/smells/duplication.rb +39 -8
  26. data/lib/reek/smells/feature_envy.rb +7 -8
  27. data/lib/reek/smells/irresponsible_module.rb +12 -3
  28. data/lib/reek/smells/large_class.rb +31 -15
  29. data/lib/reek/smells/long_method.rb +15 -5
  30. data/lib/reek/smells/long_parameter_list.rb +14 -7
  31. data/lib/reek/smells/long_yield_list.rb +12 -9
  32. data/lib/reek/smells/nested_iterators.rb +46 -11
  33. data/lib/reek/smells/simulated_polymorphism.rb +16 -8
  34. data/lib/reek/smells/smell_detector.rb +13 -13
  35. data/lib/reek/smells/uncommunicative_method_name.rb +12 -20
  36. data/lib/reek/smells/uncommunicative_module_name.rb +17 -19
  37. data/lib/reek/smells/uncommunicative_parameter_name.rb +22 -15
  38. data/lib/reek/smells/uncommunicative_variable_name.rb +24 -18
  39. data/lib/reek/smells/utility_function.rb +6 -6
  40. data/lib/reek/source/code_comment.rb +19 -1
  41. data/lib/reek/source/tree_dresser.rb +40 -22
  42. data/reek.gemspec +6 -4
  43. data/spec/matchers/smell_of_matcher.rb +58 -0
  44. data/spec/reek/core/code_context_spec.rb +4 -2
  45. data/spec/reek/core/code_parser_spec.rb +2 -1
  46. data/spec/reek/core/method_context_spec.rb +5 -5
  47. data/spec/reek/smells/attribute_spec.rb +2 -4
  48. data/spec/reek/smells/boolean_parameter_spec.rb +32 -42
  49. data/spec/reek/smells/class_variable_spec.rb +22 -6
  50. data/spec/reek/smells/control_couple_spec.rb +15 -14
  51. data/spec/reek/smells/data_clump_spec.rb +29 -111
  52. data/spec/reek/smells/duplication_spec.rb +79 -49
  53. data/spec/reek/smells/feature_envy_spec.rb +1 -2
  54. data/spec/reek/smells/irresponsible_module_spec.rb +43 -22
  55. data/spec/reek/smells/large_class_spec.rb +34 -59
  56. data/spec/reek/smells/long_method_spec.rb +15 -10
  57. data/spec/reek/smells/long_parameter_list_spec.rb +24 -24
  58. data/spec/reek/smells/long_yield_list_spec.rb +13 -14
  59. data/spec/reek/smells/nested_iterators_spec.rb +93 -76
  60. data/spec/reek/smells/smell_detector_shared.rb +4 -2
  61. data/spec/reek/smells/uncommunicative_method_name_spec.rb +10 -27
  62. data/spec/reek/smells/uncommunicative_module_name_spec.rb +22 -23
  63. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +36 -26
  64. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +45 -48
  65. data/spec/reek/smells/utility_function_spec.rb +14 -13
  66. data/spec/reek/source/code_comment_spec.rb +61 -3
  67. data/spec/reek/source/tree_dresser_spec.rb +96 -1
  68. data/spec/samples/config/allow_duplication.reek +3 -0
  69. data/spec/samples/config/deeper_nested_iterators.reek +3 -0
  70. data/spec/samples/demo/demo.rb +8 -0
  71. data/spec/samples/inline_config/dirty.rb +16 -0
  72. data/spec/samples/inline_config/masked.reek +7 -0
  73. data/spec/samples/mask_some/dirty.rb +8 -0
  74. data/spec/samples/mask_some/some.reek +8 -0
  75. data/spec/spec_helper.rb +2 -0
  76. metadata +15 -5
@@ -1,7 +1,6 @@
1
1
  require File.join( File.dirname( File.expand_path(__FILE__)), 'smell_detector')
2
2
  require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'source')
3
3
 
4
- # Part of Reek's core
5
4
  module Reek
6
5
  module Smells
7
6
 
@@ -11,17 +10,27 @@ module Reek
11
10
  #
12
11
  class IrresponsibleModule < SmellDetector
13
12
 
13
+ SMELL_CLASS = self.name.split(/::/)[-1]
14
+ SMELL_SUBCLASS = SMELL_CLASS
15
+
16
+ MODULE_NAME_KEY = 'module_name'
17
+
14
18
  def self.contexts # :nodoc:
15
19
  [:class]
16
20
  end
17
21
 
18
22
  #
19
23
  # Checks the given class or module for a descriptive comment.
20
- # Remembers any smells found.
24
+ #
25
+ # @return [Array<SmellWarning>]
21
26
  #
22
27
  def examine_context(ctx)
23
28
  comment = Source::CodeComment.new(ctx.exp.comments)
24
- found(ctx, "has no descriptive comment") unless comment.is_descriptive?
29
+ return [] if comment.is_descriptive?
30
+ smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
31
+ 'has no descriptive comment',
32
+ @source, SMELL_SUBCLASS, {MODULE_NAME_KEY => ctx.exp.text_name})
33
+ [smell]
25
34
  end
26
35
  end
27
36
  end
@@ -15,8 +15,12 @@ module Reek
15
15
  # included modules.
16
16
  #
17
17
  class LargeClass < SmellDetector
18
+
19
+ SMELL_CLASS = self.name.split(/::/)[-1]
18
20
  SUBCLASS_TOO_MANY_METHODS = 'TooManyMethods'
19
21
  SUBCLASS_TOO_MANY_IVARS = 'TooManyInstanceVariables'
22
+ METHOD_COUNT_KEY = 'method_count'
23
+ IVAR_COUNT_KEY = 'ivar_count'
20
24
 
21
25
  # The name of the config field that sets the maximum number of methods
22
26
  # permitted in a class.
@@ -46,25 +50,37 @@ module Reek
46
50
  super(source, config)
47
51
  end
48
52
 
49
- def check_num_methods(klass) # :nodoc:
50
- actual = klass.local_nodes(:defn).length
51
- return if actual <= value(MAX_ALLOWED_METHODS_KEY, klass, DEFAULT_MAX_METHODS)
52
- found(klass, "has at least #{actual} methods", SUBCLASS_TOO_MANY_METHODS, {'method_count' => actual})
53
+ #
54
+ # Checks +klass+ for too many methods or too many instance variables.
55
+ #
56
+ # @return [Array<SmellWarning>]
57
+ #
58
+ def examine_context(ctx)
59
+ @max_allowed_ivars = value(MAX_ALLOWED_IVARS_KEY, ctx, DEFAULT_MAX_IVARS)
60
+ @max_allowed_methods = value(MAX_ALLOWED_METHODS_KEY, ctx, DEFAULT_MAX_METHODS)
61
+ check_num_methods(ctx) + check_num_ivars(ctx)
53
62
  end
54
63
 
55
- def check_num_ivars(klass) # :nodoc:
56
- count = klass.local_nodes(:iasgn).map {|iasgn| iasgn[1]}.uniq.length
57
- return if count <= value(MAX_ALLOWED_IVARS_KEY, klass, DEFAULT_MAX_IVARS)
58
- found(klass, "has at least #{count} instance variables", SUBCLASS_TOO_MANY_IVARS, {'ivar_count' => count})
64
+ private
65
+
66
+ def check_num_methods(ctx) # :nodoc:
67
+ actual = ctx.local_nodes(:defn).length
68
+ return [] if actual <= @max_allowed_methods
69
+ smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
70
+ "has at least #{actual} methods",
71
+ @source, SUBCLASS_TOO_MANY_METHODS,
72
+ {METHOD_COUNT_KEY => actual})
73
+ [smell]
59
74
  end
60
75
 
61
- #
62
- # Checks +klass+ for too many methods or too many instance variables.
63
- # Remembers any smells found.
64
- #
65
- def examine_context(klass)
66
- check_num_methods(klass)
67
- check_num_ivars(klass)
76
+ def check_num_ivars(ctx) # :nodoc:
77
+ count = ctx.local_nodes(:iasgn).map {|iasgn| iasgn[1]}.uniq.length
78
+ return [] if count <= @max_allowed_ivars
79
+ smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
80
+ "has at least #{count} instance variables",
81
+ @source, SUBCLASS_TOO_MANY_IVARS,
82
+ {IVAR_COUNT_KEY => count})
83
+ [smell]
68
84
  end
69
85
  end
70
86
  end
@@ -11,8 +11,12 @@ module Reek
11
11
  # 5 statements.
12
12
  #
13
13
  class LongMethod < SmellDetector
14
+
15
+ SMELL_CLASS = self.name.split(/::/)[-1]
14
16
  SUBCLASS_TOO_MANY_STATEMENTS = 'TooManyStatements'
15
17
 
18
+ STATEMENT_COUNT_KEY = 'statement_count'
19
+
16
20
  # The name of the config field that sets the maximum number of
17
21
  # statements permitted in any method.
18
22
  MAX_ALLOWED_STATEMENTS_KEY = 'max_statements'
@@ -32,12 +36,18 @@ module Reek
32
36
 
33
37
  #
34
38
  # Checks the length of the given +method+.
35
- # Remembers any smells found.
36
39
  #
37
- def examine_context(method)
38
- num = method.num_statements
39
- return false if num <= value(MAX_ALLOWED_STATEMENTS_KEY, method, DEFAULT_MAX_STATEMENTS)
40
- found(method, "has approx #{num} statements", SUBCLASS_TOO_MANY_STATEMENTS, {'statement_count' => num})
40
+ # @return [Array<SmellWarning>]
41
+ #
42
+ def examine_context(ctx)
43
+ @max_allowed_statements = value(MAX_ALLOWED_STATEMENTS_KEY, ctx, DEFAULT_MAX_STATEMENTS)
44
+ num = ctx.num_statements
45
+ return [] if num <= @max_allowed_statements
46
+ smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
47
+ "has approx #{num} statements",
48
+ @source, SUBCLASS_TOO_MANY_STATEMENTS,
49
+ {STATEMENT_COUNT_KEY => num})
50
+ [smell]
41
51
  end
42
52
  end
43
53
  end
@@ -18,6 +18,8 @@ module Reek
18
18
  SMELL_CLASS = self.name.split(/::/)[-1]
19
19
  SMELL_SUBCLASS = 'LongParameterList'
20
20
 
21
+ PARAMETER_COUNT_KEY = 'parameter_count'
22
+
21
23
  # The name of the config field that sets the maximum number of
22
24
  # parameters permitted in any method or block.
23
25
  MAX_ALLOWED_PARAMS_KEY = 'max_params'
@@ -40,14 +42,19 @@ module Reek
40
42
  end
41
43
 
42
44
  #
43
- # Checks the number of parameters in the given scope.
44
- # Remembers any smells found.
45
+ # Checks the number of parameters in the given method.
46
+ #
47
+ # @return [Array<SmellWarning>]
45
48
  #
46
- def examine_context(method_ctx)
47
- num_params = method_ctx.parameters.length
48
- return false if num_params <= value(MAX_ALLOWED_PARAMS_KEY, method_ctx, DEFAULT_MAX_ALLOWED_PARAMS)
49
- found(method_ctx, "has #{num_params} parameters",
50
- SMELL_SUBCLASS, {'parameter_count' => num_params})
49
+ def examine_context(ctx)
50
+ @max_allowed_params = value(MAX_ALLOWED_PARAMS_KEY, ctx, DEFAULT_MAX_ALLOWED_PARAMS)
51
+ num_params = ctx.exp.arg_names.length
52
+ return [] if num_params <= @max_allowed_params
53
+ smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
54
+ "has #{num_params} parameters",
55
+ @source, SMELL_SUBCLASS,
56
+ {PARAMETER_COUNT_KEY => num_params})
57
+ [smell]
51
58
  end
52
59
  end
53
60
  end
@@ -21,9 +21,11 @@ module Reek
21
21
  # value.
22
22
  DEFAULT_MAX_ALLOWED_PARAMS = 3
23
23
 
24
+ PARAMETER_COUNT_KEY = 'parameter_count'
25
+
24
26
  def self.default_config
25
27
  super.adopt(
26
- MAX_ALLOWED_PARAMS_KEY => DEFAULT_MAX_ALLOWED_PARAMS
28
+ MAX_ALLOWED_PARAMS_KEY => DEFAULT_MAX_ALLOWED_PARAMS
27
29
  )
28
30
  end
29
31
 
@@ -33,17 +35,18 @@ module Reek
33
35
 
34
36
  #
35
37
  # Checks the number of parameters in the given scope.
36
- # Remembers any smells found.
38
+ #
39
+ # @return [Array<SmellWarning>]
37
40
  #
38
41
  def examine_context(method_ctx)
39
- method_ctx.local_nodes(:yield).each do |yield_node|
42
+ @max_allowed_params = value(MAX_ALLOWED_PARAMS_KEY, method_ctx, DEFAULT_MAX_ALLOWED_PARAMS)
43
+ method_ctx.local_nodes(:yield).select do |yield_node|
44
+ yield_node.args.length > @max_allowed_params
45
+ end.map do |yield_node|
40
46
  num_params = yield_node.args.length
41
- next if num_params <= value(MAX_ALLOWED_PARAMS_KEY, method_ctx, DEFAULT_MAX_ALLOWED_PARAMS)
42
- smell = SmellWarning.new(SMELL_CLASS, method_ctx.full_name, [yield_node.line],
43
- "yields #{num_params} parameters",
44
- @source, SMELL_SUBCLASS, {'parameter_count' => num_params})
45
- @smells_found << smell
46
- #SMELL: serious duplication
47
+ SmellWarning.new(SMELL_CLASS, method_ctx.full_name, [yield_node.line],
48
+ "yields #{num_params} parameters",
49
+ @source, SMELL_SUBCLASS, {PARAMETER_COUNT_KEY => num_params})
47
50
  end
48
51
  end
49
52
  end
@@ -10,26 +10,59 @@ module Reek
10
10
  # +NestedIterators+ reports failing methods only once.
11
11
  #
12
12
  class NestedIterators < SmellDetector
13
+
14
+ SMELL_CLASS = self.name.split(/::/)[-1]
15
+ SMELL_SUBCLASS = SMELL_CLASS
13
16
  # SMELL: should be a subclass of UnnecessaryComplexity
17
+ NESTING_DEPTH_KEY = 'depth'
18
+
19
+ # The name of the config field that sets the maximum depth
20
+ # of nested iterators to be permitted within any single method.
21
+ MAX_ALLOWED_NESTING_KEY = 'max_allowed_nesting'
22
+
23
+ DEFAULT_MAX_ALLOWED_NESTING = 1
24
+
25
+ # The name of the config field that sets the names of any
26
+ # methods for which nesting should not be considered
27
+ IGNORE_ITERATORS_KEY = 'ignore_iterators'
28
+
29
+ DEFAULT_IGNORE_ITERATORS = []
30
+
31
+ def self.default_config
32
+ super.adopt(
33
+ MAX_ALLOWED_NESTING_KEY => DEFAULT_MAX_ALLOWED_NESTING,
34
+ IGNORE_ITERATORS_KEY => DEFAULT_IGNORE_ITERATORS
35
+ )
36
+ end
37
+
38
+ def initialize(source, config = NestedIterators.default_config)
39
+ super(source, config)
40
+ end
14
41
 
15
42
  #
16
43
  # Checks whether the given +block+ is inside another.
17
- # Remembers any smells found.
18
44
  #
19
- def examine_context(method_ctx)
20
- find_deepest_iterators(method_ctx).each do |iter|
45
+ # @return [Array<SmellWarning>]
46
+ #
47
+ def examine_context(ctx)
48
+ @ignore_iterators = value(IGNORE_ITERATORS_KEY, ctx, DEFAULT_IGNORE_ITERATORS)
49
+ @max_allowed_nesting = value(MAX_ALLOWED_NESTING_KEY, ctx, DEFAULT_MAX_ALLOWED_NESTING)
50
+ find_deepest_iterators(ctx).map do |iter|
21
51
  depth = iter[1]
22
- found(method_ctx, "contains iterators nested #{depth} deep", '',
23
- {'depth' => depth}, [iter[0].line])
52
+ SmellWarning.new(SMELL_CLASS, ctx.full_name, [iter[0].line],
53
+ "contains iterators nested #{depth} deep",
54
+ @source, SMELL_SUBCLASS,
55
+ {NESTING_DEPTH_KEY => depth})
24
56
  end
25
- # TODO: report the nesting depth and the innermost line
26
57
  # BUG: no longer reports nesting outside methods (eg. in Optparse)
27
58
  end
28
59
 
29
- def find_deepest_iterators(method_ctx)
60
+ private
61
+
62
+ def find_deepest_iterators(ctx)
30
63
  result = []
31
- find_iters(method_ctx.exp, 1, result)
32
- result.select {|item| item[1] >= 2}
64
+ find_iters(ctx.exp, 1, result)
65
+ result.select {|item| item[1] > @max_allowed_nesting}
33
66
  end
34
67
 
35
68
  def find_iters(exp, depth, result)
@@ -39,8 +72,10 @@ module Reek
39
72
  when :iter
40
73
  find_iters([elem.call], depth, result)
41
74
  current = result.length
42
- find_iters([elem.block], depth+1, result)
43
- result << [elem, depth] if result.length == current
75
+ call = Source::SexpFormatter.format(elem.call)
76
+ ignored = @ignore_iterators.any? { |ignore| /#{ignore}/ === call }
77
+ find_iters([elem.block], depth + (ignored ? 0 : 1), result)
78
+ result << [elem, depth] if result.length == current unless ignored
44
79
  when :class, :defn, :defs, :module
45
80
  next
46
81
  else
@@ -23,6 +23,9 @@ module Reek
23
23
  #
24
24
  class SimulatedPolymorphism < SmellDetector
25
25
 
26
+ SMELL_CLASS = self.name.split(/::/)[-1]
27
+ SMELL_SUBCLASS = 'RepeatedConditional'
28
+
26
29
  def self.contexts # :nodoc:
27
30
  [:class]
28
31
  end
@@ -43,15 +46,20 @@ module Reek
43
46
 
44
47
  #
45
48
  # Checks the given class for multiple identical conditional tests.
46
- # Remembers any smells found.
47
49
  #
48
- def examine_context(klass)
49
- conditional_counts(klass).each do |key, lines|
50
+ # @return [Array<SmellWarning>]
51
+ #
52
+ def examine_context(ctx)
53
+ @max_identical_ifs = value(MAX_IDENTICAL_IFS_KEY, ctx, DEFAULT_MAX_IFS)
54
+ conditional_counts(ctx).select do |key, lines|
55
+ lines.length > @max_identical_ifs
56
+ end.map do |key, lines|
50
57
  occurs = lines.length
51
- next unless occurs > value(MAX_IDENTICAL_IFS_KEY, klass, DEFAULT_MAX_IFS)
52
- expr = key.format
53
- found(klass, "tests #{expr} at least #{occurs} times",
54
- 'RepeatedConditional', {'expression' => expr, 'occurrences' => occurs}, lines)
58
+ expr = key.format_ruby
59
+ SmellWarning.new(SMELL_CLASS, ctx.full_name, lines,
60
+ "tests #{expr} at least #{occurs} times",
61
+ @source, SMELL_SUBCLASS,
62
+ {'expression' => expr, 'occurrences' => occurs})
55
63
  end
56
64
  end
57
65
 
@@ -61,7 +69,7 @@ module Reek
61
69
  # occurs. Ignores nested classes and modules.
62
70
  #
63
71
  def conditional_counts(sexp)
64
- result = Hash.new {|hash,key| hash[key] = []}
72
+ result = Hash.new {|hash, key| hash[key] = []}
65
73
  collector = proc { |node|
66
74
  condition = node.condition
67
75
  next if condition.nil? or condition == s(:call, nil, :block_given?, s(:arglist))
@@ -29,7 +29,7 @@ module Reek
29
29
  DEFAULT_EXCLUDE_SET = []
30
30
 
31
31
  class << self
32
- def contexts # :nodoc:
32
+ def contexts
33
33
  [:defn, :defs]
34
34
  end
35
35
 
@@ -46,7 +46,7 @@ module Reek
46
46
  def initialize(source, config = self.class.default_config)
47
47
  @source = source
48
48
  @config = Core::SmellConfiguration.new(config)
49
- @smells_found = Set.new
49
+ @smells_found = []
50
50
  end
51
51
 
52
52
  def register(hooks)
@@ -64,7 +64,11 @@ module Reek
64
64
  end
65
65
 
66
66
  def examine(context)
67
- examine_context(context) if @config.enabled? and !exception?(context)
67
+ enabled = @config.enabled? && config_for(context)[Core::SmellConfiguration::ENABLED_KEY] != false
68
+ if enabled && !exception?(context)
69
+ sm = examine_context(context)
70
+ @smells_found += sm
71
+ end
68
72
  end
69
73
 
70
74
  def examine_context(context)
@@ -74,21 +78,17 @@ module Reek
74
78
  context.matches?(value(EXCLUDE_KEY, context, DEFAULT_EXCLUDE_SET))
75
79
  end
76
80
 
77
- def found(context, message, subclass = '', parameters = {}, lines = nil)
78
- lines ||= [context.exp.line] # SMELL: nil?!?!?! Yuk
79
- smell = SmellWarning.new(self.class.name.split(/::/)[-1], context.full_name,
80
- lines, message,
81
- @source, subclass, parameters)
82
- @smells_found << smell
83
- smell
84
- end
85
-
86
81
  def report_on(report)
87
82
  @smells_found.each { |smell| smell.report_on(report) }
88
83
  end
89
84
 
90
85
  def value(key, ctx, fall_back)
91
- @config.value(key, ctx, fall_back)
86
+ config_for(ctx)[key] || @config.value(key, ctx, fall_back)
87
+ end
88
+
89
+ def config_for(ctx)
90
+ ctx.config[self.class.name.split(/::/)[-1]] || {}
91
+ # BUG: needs to consider smell class AND subclass
92
92
  end
93
93
  end
94
94
  end
@@ -53,29 +53,21 @@ module Reek
53
53
 
54
54
  #
55
55
  # Checks the given +context+ for uncommunicative names.
56
- # Remembers any smells found.
57
56
  #
58
- def examine_context(method_ctx)
59
- name = method_ctx.name
60
- return false if accept?(method_ctx)
61
- return false unless is_bad_name?(name, method_ctx)
62
- smell = SmellWarning.new('UncommunicativeName', method_ctx.full_name, [method_ctx.exp.line],
57
+ # @return [Array<SmellWarning>]
58
+ #
59
+ def examine_context(ctx)
60
+ @reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
61
+ @accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
62
+ name = ctx.name
63
+ return [] if @accept_names.include?(ctx.full_name)
64
+ var = name.to_s.gsub(/^[@\*\&]*/, '')
65
+ return [] if @accept_names.include?(var)
66
+ return [] unless @reject_names.detect {|patt| patt === var}
67
+ smell = SmellWarning.new('UncommunicativeName', ctx.full_name, [ctx.exp.line],
63
68
  "has the name '#{name}'",
64
69
  @source, 'UncommunicativeMethodName', {METHOD_NAME_KEY => name.to_s})
65
- @smells_found << smell
66
- #SMELL: serious duplication
67
- end
68
-
69
- private
70
-
71
- def accept?(context)
72
- value(ACCEPT_KEY, context, DEFAULT_ACCEPT_SET).include?(context.full_name)
73
- end
74
-
75
- def is_bad_name?(name, context) # :nodoc:
76
- var = name.to_s.gsub(/^[@\*\&]*/, '')
77
- return false if value(ACCEPT_KEY, context, DEFAULT_ACCEPT_SET).include?(var)
78
- value(REJECT_KEY, context, DEFAULT_REJECT_SET).detect {|patt| patt === var}
70
+ [smell]
79
71
  end
80
72
  end
81
73
  end
@@ -19,6 +19,10 @@ module Reek
19
19
  #
20
20
  class UncommunicativeModuleName < SmellDetector
21
21
 
22
+ SMELL_CLASS = 'UncommunicativeName'
23
+ SMELL_SUBCLASS = self.name.split(/::/)[-1]
24
+ MODULE_NAME_KEY = 'module_name'
25
+
22
26
  # The name of the config field that lists the regexps of
23
27
  # smelly names to be reported.
24
28
  REJECT_KEY = 'reject'
@@ -49,27 +53,21 @@ module Reek
49
53
 
50
54
  #
51
55
  # Checks the given +context+ for uncommunicative names.
52
- # Remembers any smells found.
53
56
  #
54
- def examine_context(module_ctx)
55
- name = module_ctx.name
56
- return false if accept?(module_ctx)
57
- return false unless is_bad_name?(name, module_ctx)
58
- smell = SmellWarning.new('UncommunicativeName', module_ctx.full_name, [module_ctx.exp.line],
59
- "has the name '#{name}'",
60
- @source, 'UncommunicativeModuleName', {'module_name' => name.to_s})
61
- @smells_found << smell
62
- #SMELL: serious duplication
63
- end
64
-
65
- def accept?(context)
66
- value(ACCEPT_KEY, context, DEFAULT_ACCEPT_SET).include?(context.full_name)
67
- end
68
-
69
- def is_bad_name?(name, context) # :nodoc:
57
+ # @return [Array<SmellWarning>]
58
+ #
59
+ def examine_context(ctx)
60
+ @reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
61
+ @accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
62
+ name = ctx.exp.simple_name
63
+ return [] if @accept_names.include?(ctx.full_name)
70
64
  var = name.to_s.gsub(/^[@\*\&]*/, '')
71
- return false if value(ACCEPT_KEY, context, DEFAULT_ACCEPT_SET).include?(var)
72
- value(REJECT_KEY, context, DEFAULT_REJECT_SET).detect {|patt| patt === var}
65
+ return [] if @accept_names.include?(var)
66
+ return [] unless @reject_names.detect {|patt| patt === var}
67
+ smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
68
+ "has the name '#{name}'",
69
+ @source, SMELL_SUBCLASS, {MODULE_NAME_KEY => name.to_s})
70
+ [smell]
73
71
  end
74
72
  end
75
73
  end