reek 1.2.7.3 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -19,12 +19,16 @@ module Reek
19
19
  #
20
20
  class UncommunicativeParameterName < SmellDetector
21
21
 
22
+ SMELL_CLASS = 'UncommunicativeName'
23
+ SMELL_SUBCLASS = self.name.split(/::/)[-1]
24
+ PARAMETER_NAME_KEY = 'parameter_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'
25
29
 
26
30
  DEFAULT_REJECT_SET = [/^.$/, /[0-9]$/, /[A-Z]/]
27
-
31
+
28
32
  # The name of the config field that lists the specific names that are
29
33
  # to be treated as exceptions; these names will not be reported as
30
34
  # uncommunicative.
@@ -34,8 +38,8 @@ module Reek
34
38
 
35
39
  def self.default_config
36
40
  super.adopt(
37
- REJECT_KEY => DEFAULT_REJECT_SET,
38
- ACCEPT_KEY => DEFAULT_ACCEPT_SET
41
+ REJECT_KEY => DEFAULT_REJECT_SET,
42
+ ACCEPT_KEY => DEFAULT_ACCEPT_SET
39
43
  )
40
44
  end
41
45
 
@@ -49,23 +53,26 @@ 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(context)
55
- context.exp.parameter_names.each do |name|
56
- next unless is_bad_name?(name, context)
57
- smell = SmellWarning.new('UncommunicativeName', context.full_name, [context.exp.line],
58
- "has the parameter name '#{name}'",
59
- @source, 'UncommunicativeParameterName', {'parameter_name' => name.to_s})
60
- @smells_found << smell
61
- #SMELL: serious duplication
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
+ ctx.exp.parameter_names.select do |name|
63
+ is_bad_name?(name, ctx)
64
+ end.map do |name|
65
+ smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
66
+ "has the parameter name '#{name}'",
67
+ @source, SMELL_SUBCLASS, {PARAMETER_NAME_KEY => name.to_s})
68
+ smell
62
69
  end
63
70
  end
64
71
 
65
- def is_bad_name?(name, context) # :nodoc:
72
+ def is_bad_name?(name, ctx)
66
73
  var = name.to_s.gsub(/^[@\*\&]*/, '')
67
- return false if var == '*' or value(ACCEPT_KEY, context, DEFAULT_ACCEPT_SET).include?(var)
68
- value(REJECT_KEY, context, DEFAULT_REJECT_SET).detect {|patt| patt === var}
74
+ return false if var == '*' or @accept_names.include?(var)
75
+ @reject_names.detect {|patt| patt === var}
69
76
  end
70
77
  end
71
78
  end
@@ -19,12 +19,16 @@ module Reek
19
19
  #
20
20
  class UncommunicativeVariableName < SmellDetector
21
21
 
22
+ SMELL_CLASS = 'UncommunicativeName'
23
+ SMELL_SUBCLASS = self.name.split(/::/)[-1]
24
+ VARIABLE_NAME_KEY = 'variable_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'
25
29
 
26
30
  DEFAULT_REJECT_SET = [/^.$/, /[0-9]$/, /[A-Z]/]
27
-
31
+
28
32
  # The name of the config field that lists the specific names that are
29
33
  # to be treated as exceptions; these names will not be reported as
30
34
  # uncommunicative.
@@ -34,8 +38,8 @@ module Reek
34
38
 
35
39
  def self.default_config
36
40
  super.adopt(
37
- REJECT_KEY => DEFAULT_REJECT_SET,
38
- ACCEPT_KEY => DEFAULT_ACCEPT_SET
41
+ REJECT_KEY => DEFAULT_REJECT_SET,
42
+ ACCEPT_KEY => DEFAULT_ACCEPT_SET
39
43
  )
40
44
  end
41
45
 
@@ -49,32 +53,34 @@ 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(context)
55
- variable_names(context.exp).each do |name, lines|
56
- next unless is_bad_name?(name, context)
57
- smell = SmellWarning.new('UncommunicativeName', context.full_name, lines,
58
- "has the variable name '#{name}'",
59
- @source, 'UncommunicativeVariableName', {'variable_name' => name.to_s})
60
- @smells_found << smell
61
- #SMELL: serious duplication
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
+ variable_names(ctx.exp).select do |name, lines|
63
+ is_bad_name?(name, ctx)
64
+ end.map do |name, lines|
65
+ SmellWarning.new(SMELL_CLASS, ctx.full_name, lines,
66
+ "has the variable name '#{name}'",
67
+ @source, SMELL_SUBCLASS, {VARIABLE_NAME_KEY => name.to_s})
62
68
  end
63
69
  end
64
70
 
65
- def is_bad_name?(name, context) # :nodoc:
71
+ def is_bad_name?(name, ctx)
66
72
  var = name.to_s.gsub(/^[@\*\&]*/, '')
67
- return false if value(ACCEPT_KEY, context, DEFAULT_ACCEPT_SET).include?(var)
68
- value(REJECT_KEY, context, DEFAULT_REJECT_SET).detect {|patt| patt === var}
73
+ return false if @accept_names.include?(var)
74
+ @reject_names.detect {|patt| patt === var}
69
75
  end
70
76
 
71
77
  def variable_names(exp)
72
78
  assignment_nodes = exp.each_node(:lasgn, [:class, :module, :defs, :defn])
73
79
  case exp.first
74
- when :class, :module
75
- assignment_nodes += exp.each_node(:iasgn, [:class, :module])
80
+ when :class, :module
81
+ assignment_nodes += exp.each_node(:iasgn, [:class, :module])
76
82
  end
77
- result = Hash.new {|hash,key| hash[key] = []}
83
+ result = Hash.new {|hash, key| hash[key] = []}
78
84
  assignment_nodes.each {|asgn| result[asgn[1]].push(asgn.line) }
79
85
  result
80
86
  end
@@ -61,18 +61,18 @@ module Reek
61
61
 
62
62
  #
63
63
  # Checks whether the given +method+ is a utility function.
64
- # Remembers any smells found.
64
+ #
65
+ # @return [Array<SmellWarning>]
65
66
  #
66
67
  def examine_context(method_ctx)
67
- return false if method_ctx.num_statements == 0
68
- return false if depends_on_instance?(method_ctx.exp)
69
- return false if num_helper_methods(method_ctx) <= value(HELPER_CALLS_LIMIT_KEY, method_ctx, DEFAULT_HELPER_CALLS_LIMIT)
68
+ return [] if method_ctx.num_statements == 0
69
+ return [] if depends_on_instance?(method_ctx.exp)
70
+ return [] if num_helper_methods(method_ctx) <= value(HELPER_CALLS_LIMIT_KEY, method_ctx, DEFAULT_HELPER_CALLS_LIMIT)
70
71
  # SMELL: loads of calls to value{} with the above pattern
71
72
  smell = SmellWarning.new(SMELL_CLASS, method_ctx.full_name, [method_ctx.exp.line],
72
73
  "doesn't depend on instance state",
73
74
  @source, SMELL_SUBCLASS)
74
- @smells_found << smell
75
- #SMELL: serious duplication
75
+ [smell]
76
76
  end
77
77
 
78
78
  private
@@ -7,13 +7,31 @@ module Reek
7
7
  # module, class and method definitions.
8
8
  #
9
9
  class CodeComment
10
+ CONFIG_REGEX = /:reek:(\w+)(:\s*\{.*?\})?/
10
11
 
11
12
  def initialize(text)
12
- @text = text.gsub(/#/, '').gsub(/\n/, '').strip
13
+ @config = Hash.new { |hash,key| hash[key] = {} }
14
+ @text = text.gsub(CONFIG_REGEX) do |m|
15
+ add_to_config($1, $2)
16
+ ''
17
+ end.gsub(/#/, '').gsub(/\n/, '').strip
13
18
  end
19
+
20
+ def config
21
+ @config
22
+ end
23
+
14
24
  def is_descriptive?
15
25
  @text.split(/\s+/).length >= 2
16
26
  end
27
+
28
+ protected
29
+ def add_to_config(smell, options)
30
+ options ||= ': { enabled: false }'
31
+ @config.merge! YAML.load(smell.gsub(/(?:^|_)(.)/) { $1.upcase } + options)
32
+ # extend this to all configs --------------------------^
33
+ # extend to allow configuration of whole smell class, not just subclass
34
+ end
17
35
  end
18
36
  end
19
37
  end
@@ -6,6 +6,17 @@ module Reek
6
6
  # syntax tree more easily.
7
7
  #
8
8
  module SexpNode
9
+ def self.format(expr)
10
+ case expr
11
+ when Sexp then expr.format_ruby
12
+ else expr.to_s
13
+ end
14
+ end
15
+
16
+ def hash
17
+ self.inspect.hash
18
+ end
19
+
9
20
  def is_language_node?
10
21
  first.class == Symbol
11
22
  end
@@ -37,7 +48,7 @@ module Reek
37
48
  end
38
49
  blk.call(self) if first == target_type
39
50
  end
40
- def format
51
+ def format_ruby
41
52
  return self[0].to_s unless Array === self
42
53
  Ruby2Ruby.new.process(deep_copy)
43
54
  end
@@ -64,15 +75,6 @@ module Reek
64
75
  end
65
76
  end
66
77
 
67
- module ClassNode
68
- def name() self[1] end
69
- def superclass() self[2] end
70
- def full_name(outer)
71
- prefix = outer == '' ? '' : "#{outer}::"
72
- "#{prefix}#{name}"
73
- end
74
- end
75
-
76
78
  module CvarNode
77
79
  def name() self[1] end
78
80
  end
@@ -80,24 +82,29 @@ module Reek
80
82
  CvasgnNode = CvarNode
81
83
  CvdeclNode = CvarNode
82
84
 
83
- module DefnNode
84
- def name() self[1] end
85
+ module MethodNode
85
86
  def arg_names
86
87
  unless @args
87
- @args = self[2][1..-1].reject {|param| Sexp === param or param.to_s =~ /^&/}
88
+ @args = argslist[1..-1].reject {|param| Sexp === param or param.to_s =~ /^&/}
88
89
  end
89
90
  @args
90
91
  end
91
92
  def parameters()
92
93
  unless @params
93
- @params = self[2].reject {|param| Sexp === param}
94
+ @params = argslist.reject {|param| Sexp === param}
94
95
  end
95
96
  @params
96
97
  end
97
98
  def parameter_names
98
99
  parameters[1..-1]
99
100
  end
101
+ end
102
+
103
+ module DefnNode
104
+ def name() self[1] end
105
+ def argslist() self[2] end
100
106
  def body() self[3] end
107
+ include MethodNode
101
108
  def full_name(outer)
102
109
  prefix = outer == '' ? '' : "#{outer}#"
103
110
  "#{prefix}#{name}"
@@ -107,16 +114,12 @@ module Reek
107
114
  module DefsNode
108
115
  def receiver() self[1] end
109
116
  def name() self[2] end
110
- def parameters
111
- self[3].reject {|param| Sexp === param}
112
- end
113
- def parameter_names
114
- parameters[1..-1]
115
- end
117
+ def argslist() self[3] end
116
118
  def body() self[4] end
119
+ include MethodNode
117
120
  def full_name(outer)
118
121
  prefix = outer == '' ? '' : "#{outer}#"
119
- "#{prefix}#{receiver.format}.#{name}"
122
+ "#{prefix}#{SexpNode.format(receiver)}.#{name}"
120
123
  end
121
124
  end
122
125
 
@@ -148,12 +151,27 @@ module Reek
148
151
 
149
152
  module ModuleNode
150
153
  def name() self[1] end
154
+ def simple_name
155
+ expr = name
156
+ while Sexp === expr and expr[0] == :colon2
157
+ expr = expr[2]
158
+ end
159
+ expr
160
+ end
151
161
  def full_name(outer)
152
162
  prefix = outer == '' ? '' : "#{outer}::"
153
- "#{prefix}#{name}"
163
+ "#{prefix}#{text_name}"
164
+ end
165
+ def text_name
166
+ SexpNode.format(name)
154
167
  end
155
168
  end
156
169
 
170
+ module ClassNode
171
+ include ModuleNode
172
+ def superclass() self[2] end
173
+ end
174
+
157
175
  module YieldNode
158
176
  def args() self[1..-1] end
159
177
  def arg_names
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{reek}
5
- s.version = "1.2.7.3"
5
+ s.version = "1.2.8"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Kevin Rutherford"]
9
- s.date = %q{2010-03-29}
9
+ s.date = %q{2010-04-26}
10
10
  s.default_executable = %q{reek}
11
11
  s.description = %q{Reek is a tool that examines Ruby classes, modules and methods
12
12
  and reports any code smells it finds.
@@ -14,10 +14,12 @@ and reports any code smells it finds.
14
14
  s.email = ["kevin@rutherford-software.com"]
15
15
  s.executables = ["reek"]
16
16
  s.extra_rdoc_files = ["History.txt", "License.txt"]
17
- s.files = [".yardopts", "History.txt", "License.txt", "README.md", "Rakefile", "bin/reek", "config/defaults.reek", "features/masking_smells.feature", "features/options.feature", "features/rake_task.feature", "features/reports.feature", "features/samples.feature", "features/stdin.feature", "features/step_definitions/reek_steps.rb", "features/support/env.rb", "features/yaml.feature", "lib/reek.rb", "lib/reek/cli/application.rb", "lib/reek/cli/command_line.rb", "lib/reek/cli/help_command.rb", "lib/reek/cli/reek_command.rb", "lib/reek/cli/report.rb", "lib/reek/cli/version_command.rb", "lib/reek/cli/yaml_command.rb", "lib/reek/core/code_context.rb", "lib/reek/core/code_parser.rb", "lib/reek/core/method_context.rb", "lib/reek/core/module_context.rb", "lib/reek/core/object_refs.rb", "lib/reek/core/singleton_method_context.rb", "lib/reek/core/smell_configuration.rb", "lib/reek/core/sniffer.rb", "lib/reek/core/stop_context.rb", "lib/reek/core/warning_collector.rb", "lib/reek/examiner.rb", "lib/reek/rake/task.rb", "lib/reek/smell_warning.rb", "lib/reek/smells.rb", "lib/reek/smells/attribute.rb", "lib/reek/smells/boolean_parameter.rb", "lib/reek/smells/class_variable.rb", "lib/reek/smells/control_couple.rb", "lib/reek/smells/data_clump.rb", "lib/reek/smells/duplication.rb", "lib/reek/smells/feature_envy.rb", "lib/reek/smells/irresponsible_module.rb", "lib/reek/smells/large_class.rb", "lib/reek/smells/long_method.rb", "lib/reek/smells/long_parameter_list.rb", "lib/reek/smells/long_yield_list.rb", "lib/reek/smells/nested_iterators.rb", "lib/reek/smells/simulated_polymorphism.rb", "lib/reek/smells/smell_detector.rb", "lib/reek/smells/uncommunicative_method_name.rb", "lib/reek/smells/uncommunicative_module_name.rb", "lib/reek/smells/uncommunicative_parameter_name.rb", "lib/reek/smells/uncommunicative_variable_name.rb", "lib/reek/smells/utility_function.rb", "lib/reek/source.rb", "lib/reek/source/code_comment.rb", "lib/reek/source/config_file.rb", "lib/reek/source/core_extras.rb", "lib/reek/source/reference_collector.rb", "lib/reek/source/sexp_formatter.rb", "lib/reek/source/source_code.rb", "lib/reek/source/source_file.rb", "lib/reek/source/source_locator.rb", "lib/reek/source/tree_dresser.rb", "lib/reek/spec.rb", "lib/reek/spec/should_reek.rb", "lib/reek/spec/should_reek_of.rb", "lib/reek/spec/should_reek_only_of.rb", "reek.gemspec", "spec/reek/cli/help_command_spec.rb", "spec/reek/cli/reek_command_spec.rb", "spec/reek/cli/report_spec.rb", "spec/reek/cli/version_command_spec.rb", "spec/reek/cli/yaml_command_spec.rb", "spec/reek/core/code_context_spec.rb", "spec/reek/core/code_parser_spec.rb", "spec/reek/core/config_spec.rb", "spec/reek/core/method_context_spec.rb", "spec/reek/core/module_context_spec.rb", "spec/reek/core/object_refs_spec.rb", "spec/reek/core/singleton_method_context_spec.rb", "spec/reek/core/smell_configuration_spec.rb", "spec/reek/core/stop_context_spec.rb", "spec/reek/core/warning_collector_spec.rb", "spec/reek/examiner_spec.rb", "spec/reek/smell_warning_spec.rb", "spec/reek/smells/attribute_spec.rb", "spec/reek/smells/behaves_like_variable_detector.rb", "spec/reek/smells/boolean_parameter_spec.rb", "spec/reek/smells/class_variable_spec.rb", "spec/reek/smells/control_couple_spec.rb", "spec/reek/smells/data_clump_spec.rb", "spec/reek/smells/duplication_spec.rb", "spec/reek/smells/feature_envy_spec.rb", "spec/reek/smells/irresponsible_module_spec.rb", "spec/reek/smells/large_class_spec.rb", "spec/reek/smells/long_method_spec.rb", "spec/reek/smells/long_parameter_list_spec.rb", "spec/reek/smells/long_yield_list_spec.rb", "spec/reek/smells/nested_iterators_spec.rb", "spec/reek/smells/simulated_polymorphism_spec.rb", "spec/reek/smells/smell_detector_shared.rb", "spec/reek/smells/uncommunicative_method_name_spec.rb", "spec/reek/smells/uncommunicative_module_name_spec.rb", "spec/reek/smells/uncommunicative_parameter_name_spec.rb", "spec/reek/smells/uncommunicative_variable_name_spec.rb", "spec/reek/smells/utility_function_spec.rb", "spec/reek/source/code_comment_spec.rb", "spec/reek/source/object_source_spec.rb", "spec/reek/source/reference_collector_spec.rb", "spec/reek/source/source_code_spec.rb", "spec/reek/source/tree_dresser_spec.rb", "spec/reek/spec/should_reek_of_spec.rb", "spec/reek/spec/should_reek_only_of_spec.rb", "spec/reek/spec/should_reek_spec.rb", "spec/samples/all_but_one_masked/clean_one.rb", "spec/samples/all_but_one_masked/dirty.rb", "spec/samples/all_but_one_masked/masked.reek", "spec/samples/clean_due_to_masking/clean_one.rb", "spec/samples/clean_due_to_masking/clean_three.rb", "spec/samples/clean_due_to_masking/clean_two.rb", "spec/samples/clean_due_to_masking/dirty_one.rb", "spec/samples/clean_due_to_masking/dirty_two.rb", "spec/samples/clean_due_to_masking/masked.reek", "spec/samples/corrupt_config_file/corrupt.reek", "spec/samples/corrupt_config_file/dirty.rb", "spec/samples/empty_config_file/dirty.rb", "spec/samples/empty_config_file/empty.reek", "spec/samples/exceptions.reek", "spec/samples/inline.rb", "spec/samples/masked/dirty.rb", "spec/samples/masked/masked.reek", "spec/samples/mixed_results/clean_one.rb", "spec/samples/mixed_results/clean_three.rb", "spec/samples/mixed_results/clean_two.rb", "spec/samples/mixed_results/dirty_one.rb", "spec/samples/mixed_results/dirty_two.rb", "spec/samples/not_quite_masked/dirty.rb", "spec/samples/not_quite_masked/masked.reek", "spec/samples/optparse.rb", "spec/samples/overrides/masked/dirty.rb", "spec/samples/overrides/masked/lower.reek", "spec/samples/overrides/upper.reek", "spec/samples/redcloth.rb", "spec/samples/three_clean_files/clean_one.rb", "spec/samples/three_clean_files/clean_three.rb", "spec/samples/three_clean_files/clean_two.rb", "spec/samples/two_smelly_files/dirty_one.rb", "spec/samples/two_smelly_files/dirty_two.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/reek.rake", "tasks/test.rake"]
17
+ s.files = [".yardopts", "History.txt", "License.txt", "README.md", "Rakefile", "bin/reek", "config/defaults.reek", "features/api.feature", "features/masking_smells.feature", "features/options.feature", "features/rake_task.feature", "features/reports.feature", "features/samples.feature", "features/stdin.feature", "features/step_definitions/reek_steps.rb", "features/support/env.rb", "features/yaml.feature", "lib/reek.rb", "lib/reek/cli/application.rb", "lib/reek/cli/command_line.rb", "lib/reek/cli/help_command.rb", "lib/reek/cli/reek_command.rb", "lib/reek/cli/report.rb", "lib/reek/cli/version_command.rb", "lib/reek/cli/yaml_command.rb", "lib/reek/core/code_context.rb", "lib/reek/core/code_parser.rb", "lib/reek/core/method_context.rb", "lib/reek/core/module_context.rb", "lib/reek/core/object_refs.rb", "lib/reek/core/singleton_method_context.rb", "lib/reek/core/smell_configuration.rb", "lib/reek/core/sniffer.rb", "lib/reek/core/stop_context.rb", "lib/reek/core/warning_collector.rb", "lib/reek/examiner.rb", "lib/reek/rake/task.rb", "lib/reek/smell_warning.rb", "lib/reek/smells.rb", "lib/reek/smells/attribute.rb", "lib/reek/smells/boolean_parameter.rb", "lib/reek/smells/class_variable.rb", "lib/reek/smells/control_couple.rb", "lib/reek/smells/data_clump.rb", "lib/reek/smells/duplication.rb", "lib/reek/smells/feature_envy.rb", "lib/reek/smells/irresponsible_module.rb", "lib/reek/smells/large_class.rb", "lib/reek/smells/long_method.rb", "lib/reek/smells/long_parameter_list.rb", "lib/reek/smells/long_yield_list.rb", "lib/reek/smells/nested_iterators.rb", "lib/reek/smells/simulated_polymorphism.rb", "lib/reek/smells/smell_detector.rb", "lib/reek/smells/uncommunicative_method_name.rb", "lib/reek/smells/uncommunicative_module_name.rb", "lib/reek/smells/uncommunicative_parameter_name.rb", "lib/reek/smells/uncommunicative_variable_name.rb", "lib/reek/smells/utility_function.rb", "lib/reek/source.rb", "lib/reek/source/code_comment.rb", "lib/reek/source/config_file.rb", "lib/reek/source/core_extras.rb", "lib/reek/source/reference_collector.rb", "lib/reek/source/sexp_formatter.rb", "lib/reek/source/source_code.rb", "lib/reek/source/source_file.rb", "lib/reek/source/source_locator.rb", "lib/reek/source/tree_dresser.rb", "lib/reek/spec.rb", "lib/reek/spec/should_reek.rb", "lib/reek/spec/should_reek_of.rb", "lib/reek/spec/should_reek_only_of.rb", "reek.gemspec", "spec/matchers/smell_of_matcher.rb", "spec/reek/cli/help_command_spec.rb", "spec/reek/cli/reek_command_spec.rb", "spec/reek/cli/report_spec.rb", "spec/reek/cli/version_command_spec.rb", "spec/reek/cli/yaml_command_spec.rb", "spec/reek/core/code_context_spec.rb", "spec/reek/core/code_parser_spec.rb", "spec/reek/core/config_spec.rb", "spec/reek/core/method_context_spec.rb", "spec/reek/core/module_context_spec.rb", "spec/reek/core/object_refs_spec.rb", "spec/reek/core/singleton_method_context_spec.rb", "spec/reek/core/smell_configuration_spec.rb", "spec/reek/core/stop_context_spec.rb", "spec/reek/core/warning_collector_spec.rb", "spec/reek/examiner_spec.rb", "spec/reek/smell_warning_spec.rb", "spec/reek/smells/attribute_spec.rb", "spec/reek/smells/behaves_like_variable_detector.rb", "spec/reek/smells/boolean_parameter_spec.rb", "spec/reek/smells/class_variable_spec.rb", "spec/reek/smells/control_couple_spec.rb", "spec/reek/smells/data_clump_spec.rb", "spec/reek/smells/duplication_spec.rb", "spec/reek/smells/feature_envy_spec.rb", "spec/reek/smells/irresponsible_module_spec.rb", "spec/reek/smells/large_class_spec.rb", "spec/reek/smells/long_method_spec.rb", "spec/reek/smells/long_parameter_list_spec.rb", "spec/reek/smells/long_yield_list_spec.rb", "spec/reek/smells/nested_iterators_spec.rb", "spec/reek/smells/simulated_polymorphism_spec.rb", "spec/reek/smells/smell_detector_shared.rb", "spec/reek/smells/uncommunicative_method_name_spec.rb", "spec/reek/smells/uncommunicative_module_name_spec.rb", "spec/reek/smells/uncommunicative_parameter_name_spec.rb", "spec/reek/smells/uncommunicative_variable_name_spec.rb", "spec/reek/smells/utility_function_spec.rb", "spec/reek/source/code_comment_spec.rb", "spec/reek/source/object_source_spec.rb", "spec/reek/source/reference_collector_spec.rb", "spec/reek/source/source_code_spec.rb", "spec/reek/source/tree_dresser_spec.rb", "spec/reek/spec/should_reek_of_spec.rb", "spec/reek/spec/should_reek_only_of_spec.rb", "spec/reek/spec/should_reek_spec.rb", "spec/samples/all_but_one_masked/clean_one.rb", "spec/samples/all_but_one_masked/dirty.rb", "spec/samples/all_but_one_masked/masked.reek", "spec/samples/clean_due_to_masking/clean_one.rb", "spec/samples/clean_due_to_masking/clean_three.rb", "spec/samples/clean_due_to_masking/clean_two.rb", "spec/samples/clean_due_to_masking/dirty_one.rb", "spec/samples/clean_due_to_masking/dirty_two.rb", "spec/samples/clean_due_to_masking/masked.reek", "spec/samples/config/allow_duplication.reek", "spec/samples/config/deeper_nested_iterators.reek", "spec/samples/corrupt_config_file/corrupt.reek", "spec/samples/corrupt_config_file/dirty.rb", "spec/samples/demo/demo.rb", "spec/samples/empty_config_file/dirty.rb", "spec/samples/empty_config_file/empty.reek", "spec/samples/exceptions.reek", "spec/samples/inline_config/dirty.rb", "spec/samples/inline_config/masked.reek", "spec/samples/inline.rb", "spec/samples/mask_some/dirty.rb", "spec/samples/mask_some/some.reek", "spec/samples/masked/dirty.rb", "spec/samples/masked/masked.reek", "spec/samples/mixed_results/clean_one.rb", "spec/samples/mixed_results/clean_three.rb", "spec/samples/mixed_results/clean_two.rb", "spec/samples/mixed_results/dirty_one.rb", "spec/samples/mixed_results/dirty_two.rb", "spec/samples/not_quite_masked/dirty.rb", "spec/samples/not_quite_masked/masked.reek", "spec/samples/optparse.rb", "spec/samples/overrides/masked/dirty.rb", "spec/samples/overrides/masked/lower.reek", "spec/samples/overrides/upper.reek", "spec/samples/redcloth.rb", "spec/samples/three_clean_files/clean_one.rb", "spec/samples/three_clean_files/clean_three.rb", "spec/samples/three_clean_files/clean_two.rb", "spec/samples/two_smelly_files/dirty_one.rb", "spec/samples/two_smelly_files/dirty_two.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/reek.rake", "tasks/test.rake"]
18
18
  s.homepage = %q{http://wiki.github.com/kevinrutherford/reek}
19
19
  s.post_install_message = %q{
20
- For more information on reek, see http://wiki.github.com/kevinrutherford/reek
20
+ Thank you for downloading Reek. For info:
21
+ - see the reek wiki http://wiki.github.com/kevinrutherford/reek
22
+ - follow @rubyreek on twitter
21
23
  }
22
24
  s.rdoc_options = ["--main", "README.md"]
23
25
  s.require_paths = ["lib"]
@@ -0,0 +1,58 @@
1
+ module SmellOfMatcher
2
+ class SmellOf
3
+ def initialize(klass, *expected_smells)
4
+ @klass = klass
5
+ @expected_smells = expected_smells
6
+ @config = {}
7
+ end
8
+
9
+ def failure_message_for_should
10
+ "Expected #{@source.desc} to smell of #{@klass}, but it didn't: #{@reason}"
11
+ end
12
+
13
+ def failure_message_for_should_not
14
+ "Expected #{@source.desc} not to smell of #{@klass}, but it did"
15
+ end
16
+
17
+ def matches?(src)
18
+ @source = src.to_reek_source
19
+ ctx = MethodContext.new(nil, @source.syntax_tree)
20
+ detector = @klass.new(@source.desc, @klass.default_config.merge(@config))
21
+ detector.examine(ctx)
22
+ actual_smells = detector.smells_found.to_a
23
+ if actual_smells.empty?
24
+ @reason = 'no smells found by detector'
25
+ return false
26
+ end
27
+ return false if actual_smells.any? do |expected_smell|
28
+ @reason = "Found #{expected_smell.smell_class}/#{expected_smell.subclass}" &&
29
+ expected_smell.smell_class != @klass::SMELL_CLASS &&
30
+ expected_smell.subclass != @klass::SMELL_SUBCLASS
31
+ end
32
+ return actual_smells.length == 1 if @expected_smells.empty?
33
+ return false unless @expected_smells.length == actual_smells.length
34
+ @expected_smells.each_with_index do |expected_smell,index|
35
+ expected_smell.each do |(key,value)|
36
+ if actual_smells[index].smell[key] != value
37
+ @reason = "#{key} != #{value}"
38
+ return false
39
+ end
40
+ end
41
+ end
42
+ true
43
+ end
44
+
45
+ def with_config(options)
46
+ @config = options
47
+ self
48
+ end
49
+ end
50
+
51
+ def smell_of(klass, *smells)
52
+ SmellOf.new(klass, *smells)
53
+ end
54
+ end
55
+
56
+ Spec::Runner.configure do |config|
57
+ config.include(SmellOfMatcher)
58
+ end
@@ -13,6 +13,7 @@ describe CodeContext do
13
13
  @exp = mock('exp')
14
14
  @exp.should_receive(:name).any_number_of_times.and_return(@exp_name)
15
15
  @exp.should_receive(:full_name).any_number_of_times.and_return(@full_name)
16
+ @exp.should_receive(:comments).any_number_of_times.and_return('')
16
17
  @ctx = CodeContext.new(nil, @exp)
17
18
  end
18
19
  it 'gets its short name from the exp' do
@@ -36,6 +37,7 @@ describe CodeContext do
36
37
  @outer_name = 'another_random sting'
37
38
  outer = mock('outer')
38
39
  outer.should_receive(:full_name).at_least(:once).and_return(@outer_name)
40
+ outer.should_receive(:config).and_return({})
39
41
  @ctx = CodeContext.new(outer, @exp)
40
42
  end
41
43
  it 'creates the correct full name' do
@@ -54,8 +56,8 @@ describe CodeContext do
54
56
  it 'should pass unknown method calls down the stack' do
55
57
  stop = StopContext.new
56
58
  def stop.bananas(arg1, arg2) arg1 + arg2 + 43 end
57
- element = ModuleContext.new(stop, 'mod', s(:module, :mod, nil))
58
- element = MethodContext.new(element, [0, :bad])
59
+ element = ModuleContext.new(stop, 'mod', ast(:module, :mod, nil))
60
+ element = MethodContext.new(element, ast(:defn, :bad))
59
61
  element.bananas(17, -5).should == 55
60
62
  end
61
63
  end
@@ -15,7 +15,8 @@ end
15
15
 
16
16
  describe CodeParser, 'with a global method definition' do
17
17
  it 'reports no problems for simple method' do
18
- 'def Outermost::fred() true; end'.should_not reek
18
+ src = 'def Outermost::fred() true; end'
19
+ src.should_not reek
19
20
  end
20
21
  end
21
22