reek 1.2.6 → 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 (209) hide show
  1. data/.yardopts +10 -0
  2. data/History.txt +62 -0
  3. data/README.md +74 -0
  4. data/bin/reek +2 -2
  5. data/config/defaults.reek +44 -5
  6. data/features/api.feature +20 -0
  7. data/features/masking_smells.feature +60 -42
  8. data/features/options.feature +7 -2
  9. data/features/rake_task.feature +29 -22
  10. data/features/reports.feature +9 -41
  11. data/features/samples.feature +184 -196
  12. data/features/stdin.feature +5 -8
  13. data/features/step_definitions/reek_steps.rb +8 -4
  14. data/features/support/env.rb +2 -3
  15. data/features/yaml.feature +85 -0
  16. data/lib/reek/cli/application.rb +46 -0
  17. data/lib/reek/cli/command_line.rb +108 -0
  18. data/lib/reek/cli/help_command.rb +18 -0
  19. data/lib/reek/cli/reek_command.rb +37 -0
  20. data/lib/reek/cli/report.rb +58 -0
  21. data/lib/reek/cli/version_command.rb +19 -0
  22. data/lib/reek/cli/yaml_command.rb +32 -0
  23. data/lib/reek/core/code_context.rb +64 -0
  24. data/lib/reek/core/code_parser.rb +166 -0
  25. data/lib/reek/core/method_context.rb +84 -0
  26. data/lib/reek/core/module_context.rb +20 -0
  27. data/lib/reek/core/object_refs.rb +51 -0
  28. data/lib/reek/core/singleton_method_context.rb +20 -0
  29. data/lib/reek/core/smell_configuration.rb +62 -0
  30. data/lib/reek/core/sniffer.rb +105 -0
  31. data/lib/reek/core/stop_context.rb +30 -0
  32. data/lib/reek/core/warning_collector.rb +27 -0
  33. data/lib/reek/examiner.rb +104 -0
  34. data/lib/reek/rake/task.rb +142 -0
  35. data/lib/reek/smell_warning.rb +73 -23
  36. data/lib/reek/smells/attribute.rb +26 -21
  37. data/lib/reek/smells/boolean_parameter.rb +38 -0
  38. data/lib/reek/smells/class_variable.rb +22 -9
  39. data/lib/reek/smells/control_couple.rb +40 -16
  40. data/lib/reek/smells/data_clump.rb +114 -23
  41. data/lib/reek/smells/duplication.rb +55 -15
  42. data/lib/reek/smells/feature_envy.rb +16 -7
  43. data/lib/reek/smells/irresponsible_module.rb +37 -0
  44. data/lib/reek/smells/large_class.rb +37 -19
  45. data/lib/reek/smells/long_method.rb +20 -9
  46. data/lib/reek/smells/long_parameter_list.rb +22 -10
  47. data/lib/reek/smells/long_yield_list.rb +43 -7
  48. data/lib/reek/smells/nested_iterators.rb +68 -8
  49. data/lib/reek/smells/simulated_polymorphism.rb +26 -15
  50. data/lib/reek/smells/smell_detector.rb +29 -51
  51. data/lib/reek/smells/uncommunicative_method_name.rb +74 -0
  52. data/lib/reek/smells/uncommunicative_module_name.rb +74 -0
  53. data/lib/reek/smells/uncommunicative_parameter_name.rb +79 -0
  54. data/lib/reek/smells/uncommunicative_variable_name.rb +89 -0
  55. data/lib/reek/smells/utility_function.rb +51 -12
  56. data/lib/reek/smells.rb +29 -0
  57. data/lib/reek/source/code_comment.rb +37 -0
  58. data/lib/reek/source/config_file.rb +72 -0
  59. data/lib/reek/source/core_extras.rb +46 -0
  60. data/lib/reek/source/reference_collector.rb +28 -0
  61. data/lib/reek/source/sexp_formatter.rb +17 -0
  62. data/lib/reek/source/source_code.rb +44 -0
  63. data/lib/reek/source/source_file.rb +32 -0
  64. data/lib/reek/source/source_locator.rb +42 -0
  65. data/lib/reek/source/tree_dresser.rb +204 -0
  66. data/lib/reek/source.rb +18 -0
  67. data/lib/reek/spec/should_reek.rb +31 -0
  68. data/lib/reek/spec/should_reek_of.rb +37 -0
  69. data/lib/reek/spec/should_reek_only_of.rb +37 -0
  70. data/lib/reek/spec.rb +51 -0
  71. data/lib/reek.rb +8 -4
  72. data/reek.gemspec +9 -7
  73. data/spec/matchers/smell_of_matcher.rb +58 -0
  74. data/spec/reek/{help_command_spec.rb → cli/help_command_spec.rb} +3 -4
  75. data/spec/reek/cli/reek_command_spec.rb +46 -0
  76. data/spec/reek/cli/report_spec.rb +30 -0
  77. data/spec/reek/{version_command_spec.rb → cli/version_command_spec.rb} +3 -3
  78. data/spec/reek/cli/yaml_command_spec.rb +47 -0
  79. data/spec/reek/core/code_context_spec.rb +145 -0
  80. data/spec/reek/{code_parser_spec.rb → core/code_parser_spec.rb} +7 -6
  81. data/spec/reek/{config_spec.rb → core/config_spec.rb} +2 -6
  82. data/spec/reek/{method_context_spec.rb → core/method_context_spec.rb} +16 -36
  83. data/spec/reek/core/module_context_spec.rb +27 -0
  84. data/spec/reek/{object_refs_spec.rb → core/object_refs_spec.rb} +5 -6
  85. data/spec/reek/core/singleton_method_context_spec.rb +9 -0
  86. data/spec/reek/core/smell_configuration_spec.rb +11 -0
  87. data/spec/reek/core/stop_context_spec.rb +17 -0
  88. data/spec/reek/core/warning_collector_spec.rb +27 -0
  89. data/spec/reek/examiner_spec.rb +103 -0
  90. data/spec/reek/smell_warning_spec.rb +74 -82
  91. data/spec/reek/smells/attribute_spec.rb +42 -23
  92. data/spec/reek/smells/behaves_like_variable_detector.rb +2 -2
  93. data/spec/reek/smells/boolean_parameter_spec.rb +66 -0
  94. data/spec/reek/smells/class_variable_spec.rb +60 -75
  95. data/spec/reek/smells/control_couple_spec.rb +37 -32
  96. data/spec/reek/smells/data_clump_spec.rb +63 -24
  97. data/spec/reek/smells/duplication_spec.rb +106 -57
  98. data/spec/reek/smells/feature_envy_spec.rb +113 -92
  99. data/spec/reek/smells/irresponsible_module_spec.rb +58 -0
  100. data/spec/reek/smells/large_class_spec.rb +67 -57
  101. data/spec/reek/smells/long_method_spec.rb +40 -10
  102. data/spec/reek/smells/long_parameter_list_spec.rb +50 -28
  103. data/spec/reek/smells/long_yield_list_spec.rb +57 -0
  104. data/spec/reek/smells/nested_iterators_spec.rb +115 -7
  105. data/spec/reek/smells/simulated_polymorphism_spec.rb +56 -20
  106. data/spec/reek/smells/smell_detector_shared.rb +42 -0
  107. data/spec/reek/smells/uncommunicative_method_name_spec.rb +42 -0
  108. data/spec/reek/smells/uncommunicative_module_name_spec.rb +66 -0
  109. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +71 -0
  110. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +120 -0
  111. data/spec/reek/smells/utility_function_spec.rb +50 -4
  112. data/spec/reek/source/code_comment_spec.rb +82 -0
  113. data/spec/reek/source/object_source_spec.rb +20 -0
  114. data/spec/reek/source/reference_collector_spec.rb +53 -0
  115. data/spec/reek/{adapters/source_spec.rb → source/source_code_spec.rb} +7 -8
  116. data/spec/reek/source/tree_dresser_spec.rb +270 -0
  117. data/spec/reek/spec/should_reek_of_spec.rb +76 -0
  118. data/spec/reek/spec/should_reek_only_of_spec.rb +89 -0
  119. data/spec/reek/{adapters → spec}/should_reek_spec.rb +8 -32
  120. data/spec/samples/all_but_one_masked/clean_one.rb +1 -0
  121. data/spec/samples/all_but_one_masked/dirty.rb +1 -0
  122. data/spec/samples/all_but_one_masked/masked.reek +5 -1
  123. data/spec/samples/clean_due_to_masking/clean_one.rb +1 -0
  124. data/spec/samples/clean_due_to_masking/clean_three.rb +1 -0
  125. data/spec/samples/clean_due_to_masking/clean_two.rb +1 -0
  126. data/spec/samples/clean_due_to_masking/dirty_one.rb +1 -1
  127. data/spec/samples/clean_due_to_masking/dirty_two.rb +1 -1
  128. data/spec/samples/clean_due_to_masking/masked.reek +5 -1
  129. data/spec/samples/config/allow_duplication.reek +3 -0
  130. data/spec/samples/config/deeper_nested_iterators.reek +3 -0
  131. data/spec/samples/corrupt_config_file/dirty.rb +1 -1
  132. data/spec/samples/demo/demo.rb +8 -0
  133. data/spec/samples/empty_config_file/dirty.rb +2 -1
  134. data/spec/samples/exceptions.reek +1 -1
  135. data/spec/samples/inline_config/dirty.rb +16 -0
  136. data/spec/samples/inline_config/masked.reek +7 -0
  137. data/spec/samples/mask_some/dirty.rb +8 -0
  138. data/spec/samples/mask_some/some.reek +8 -0
  139. data/spec/samples/masked/dirty.rb +2 -1
  140. data/spec/samples/masked/masked.reek +3 -1
  141. data/spec/samples/mixed_results/clean_one.rb +1 -0
  142. data/spec/samples/mixed_results/clean_three.rb +1 -0
  143. data/spec/samples/mixed_results/clean_two.rb +1 -0
  144. data/spec/samples/mixed_results/dirty_one.rb +1 -0
  145. data/spec/samples/mixed_results/dirty_two.rb +1 -0
  146. data/spec/samples/not_quite_masked/dirty.rb +2 -1
  147. data/spec/samples/not_quite_masked/masked.reek +1 -1
  148. data/spec/samples/overrides/masked/dirty.rb +2 -1
  149. data/spec/samples/overrides/masked/lower.reek +3 -1
  150. data/spec/samples/three_clean_files/clean_one.rb +1 -0
  151. data/spec/samples/three_clean_files/clean_three.rb +1 -0
  152. data/spec/samples/three_clean_files/clean_two.rb +1 -0
  153. data/spec/samples/two_smelly_files/dirty_one.rb +2 -1
  154. data/spec/samples/two_smelly_files/dirty_two.rb +2 -1
  155. data/spec/spec_helper.rb +10 -2
  156. data/tasks/reek.rake +2 -2
  157. data/tasks/test.rake +12 -3
  158. metadata +121 -79
  159. data/README.rdoc +0 -84
  160. data/features/profile.feature +0 -34
  161. data/lib/reek/adapters/application.rb +0 -46
  162. data/lib/reek/adapters/command_line.rb +0 -77
  163. data/lib/reek/adapters/config_file.rb +0 -31
  164. data/lib/reek/adapters/core_extras.rb +0 -64
  165. data/lib/reek/adapters/rake_task.rb +0 -121
  166. data/lib/reek/adapters/report.rb +0 -86
  167. data/lib/reek/adapters/source.rb +0 -72
  168. data/lib/reek/adapters/spec.rb +0 -133
  169. data/lib/reek/block_context.rb +0 -62
  170. data/lib/reek/class_context.rb +0 -41
  171. data/lib/reek/code_context.rb +0 -68
  172. data/lib/reek/code_parser.rb +0 -203
  173. data/lib/reek/configuration.rb +0 -57
  174. data/lib/reek/detector_stack.rb +0 -37
  175. data/lib/reek/help_command.rb +0 -14
  176. data/lib/reek/if_context.rb +0 -18
  177. data/lib/reek/masking_collection.rb +0 -33
  178. data/lib/reek/method_context.rb +0 -138
  179. data/lib/reek/module_context.rb +0 -49
  180. data/lib/reek/name.rb +0 -57
  181. data/lib/reek/object_refs.rb +0 -49
  182. data/lib/reek/reek_command.rb +0 -28
  183. data/lib/reek/sexp_formatter.rb +0 -10
  184. data/lib/reek/singleton_method_context.rb +0 -26
  185. data/lib/reek/smells/uncommunicative_name.rb +0 -84
  186. data/lib/reek/sniffer.rb +0 -177
  187. data/lib/reek/stop_context.rb +0 -35
  188. data/lib/reek/tree_dresser.rb +0 -82
  189. data/lib/reek/version_command.rb +0 -14
  190. data/lib/reek/yield_call_context.rb +0 -12
  191. data/spec/reek/adapters/report_spec.rb +0 -31
  192. data/spec/reek/adapters/should_reek_of_spec.rb +0 -138
  193. data/spec/reek/adapters/should_reek_only_of_spec.rb +0 -87
  194. data/spec/reek/block_context_spec.rb +0 -65
  195. data/spec/reek/class_context_spec.rb +0 -161
  196. data/spec/reek/code_context_spec.rb +0 -182
  197. data/spec/reek/configuration_spec.rb +0 -12
  198. data/spec/reek/if_context_spec.rb +0 -17
  199. data/spec/reek/masking_collection_spec.rb +0 -236
  200. data/spec/reek/module_context_spec.rb +0 -46
  201. data/spec/reek/name_spec.rb +0 -37
  202. data/spec/reek/object_source_spec.rb +0 -23
  203. data/spec/reek/reek_command_spec.rb +0 -45
  204. data/spec/reek/singleton_method_context_spec.rb +0 -16
  205. data/spec/reek/smells/smell_detector_spec.rb +0 -36
  206. data/spec/reek/smells/uncommunicative_name_spec.rb +0 -146
  207. data/spec/reek/sniffer_spec.rb +0 -11
  208. data/spec/reek/stop_context_spec.rb +0 -33
  209. data/spec/reek/tree_dresser_spec.rb +0 -20
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source_code')
2
+
3
+ module Reek
4
+ module Source
5
+
6
+ #
7
+ # Represents a file of Ruby source, whose contents will be examined
8
+ # for code smells.
9
+ #
10
+ class SourceFile < SourceCode
11
+
12
+ def initialize(path)
13
+ @path = path
14
+ super(IO.readlines(@path).join, @path)
15
+ end
16
+
17
+ def configure(sniffer)
18
+ path = File.expand_path(File.dirname(@path))
19
+ all_config_files(path).each { |cf| ConfigFile.new(cf).configure(sniffer) }
20
+ end
21
+
22
+ private
23
+
24
+ def all_config_files(path)
25
+ return [] unless File.exist?(path)
26
+ parent = File.dirname(path)
27
+ return [] if path == parent
28
+ all_config_files(parent) + Dir["#{path}/*.reek"]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,42 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'core_extras')
2
+
3
+ module Reek
4
+ module Source
5
+
6
+ #
7
+ # Finds Ruby source files in a filesystem.
8
+ #
9
+ class SourceLocator
10
+ def initialize(paths)
11
+ @paths = paths
12
+ end
13
+
14
+ def all_sources
15
+ valid_paths.map {|path| File.new(path).to_reek_source }
16
+ end
17
+
18
+ private
19
+
20
+ def all_ruby_source_files(paths)
21
+ paths.map do |path|
22
+ if test 'd', path
23
+ all_ruby_source_files(Dir["#{path}/**/*.rb"])
24
+ else
25
+ path
26
+ end
27
+ end.flatten.sort
28
+ end
29
+
30
+ def valid_paths
31
+ all_ruby_source_files(@paths).select do |path|
32
+ if test 'f', path
33
+ true
34
+ else
35
+ $stderr.puts "Error: No such file - #{path}"
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,204 @@
1
+ module Reek
2
+ module Source
3
+
4
+ #
5
+ # Extensions to +Sexp+ to allow +CodeParser+ to navigate the abstract
6
+ # syntax tree more easily.
7
+ #
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
+
20
+ def is_language_node?
21
+ first.class == Symbol
22
+ end
23
+
24
+ def has_type?(type)
25
+ is_language_node? and first == type
26
+ end
27
+
28
+ def each_node(type, ignoring, &blk)
29
+ if block_given?
30
+ look_for(type, ignoring, &blk)
31
+ else
32
+ result = []
33
+ look_for(type, ignoring) {|exp| result << exp}
34
+ result
35
+ end
36
+ end
37
+
38
+ #
39
+ # Carries out a depth-first traversal of this syntax tree, yielding
40
+ # every Sexp of type +target_type+. The traversal ignores any node
41
+ # whose type is listed in the Array +ignoring+.
42
+ #
43
+ def look_for(target_type, ignoring, &blk)
44
+ each do |elem|
45
+ if Sexp === elem then
46
+ elem.look_for(target_type, ignoring, &blk) unless ignoring.include?(elem.first)
47
+ end
48
+ end
49
+ blk.call(self) if first == target_type
50
+ end
51
+ def format_ruby
52
+ return self[0].to_s unless Array === self
53
+ Ruby2Ruby.new.process(deep_copy)
54
+ end
55
+ def deep_copy
56
+ YAML::load(YAML::dump(self))
57
+ end
58
+ end
59
+
60
+ module SexpExtensions
61
+ module AttrasgnNode
62
+ def args() self[3] end
63
+ end
64
+
65
+ module CaseNode
66
+ def condition() self[1] end
67
+ end
68
+
69
+ module CallNode
70
+ def receiver() self[1] end
71
+ def method_name() self[2] end
72
+ def args() self[3] end
73
+ def arg_names
74
+ args[1..-1].map {|arg| arg[1]}
75
+ end
76
+ end
77
+
78
+ module CvarNode
79
+ def name() self[1] end
80
+ end
81
+
82
+ CvasgnNode = CvarNode
83
+ CvdeclNode = CvarNode
84
+
85
+ module MethodNode
86
+ def arg_names
87
+ unless @args
88
+ @args = argslist[1..-1].reject {|param| Sexp === param or param.to_s =~ /^&/}
89
+ end
90
+ @args
91
+ end
92
+ def parameters()
93
+ unless @params
94
+ @params = argslist.reject {|param| Sexp === param}
95
+ end
96
+ @params
97
+ end
98
+ def parameter_names
99
+ parameters[1..-1]
100
+ end
101
+ end
102
+
103
+ module DefnNode
104
+ def name() self[1] end
105
+ def argslist() self[2] end
106
+ def body() self[3] end
107
+ include MethodNode
108
+ def full_name(outer)
109
+ prefix = outer == '' ? '' : "#{outer}#"
110
+ "#{prefix}#{name}"
111
+ end
112
+ end
113
+
114
+ module DefsNode
115
+ def receiver() self[1] end
116
+ def name() self[2] end
117
+ def argslist() self[3] end
118
+ def body() self[4] end
119
+ include MethodNode
120
+ def full_name(outer)
121
+ prefix = outer == '' ? '' : "#{outer}#"
122
+ "#{prefix}#{SexpNode.format(receiver)}.#{name}"
123
+ end
124
+ end
125
+
126
+ module IfNode
127
+ def condition() self[1] end
128
+ end
129
+
130
+ module IterNode
131
+ def call() self[1] end
132
+ def args() self[2] end
133
+ def block() self[3] end
134
+ def parameters() self[2] || [] end
135
+ def parameter_names
136
+ result = parameters
137
+ return case result[0]
138
+ when :lasgn
139
+ [result[1]]
140
+ when :masgn
141
+ result[1][1..-1].map {|lasgn| lasgn[1]}
142
+ else
143
+ []
144
+ end
145
+ end
146
+ end
147
+
148
+ module LitNode
149
+ def value() self[1] end
150
+ end
151
+
152
+ module ModuleNode
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
161
+ def full_name(outer)
162
+ prefix = outer == '' ? '' : "#{outer}::"
163
+ "#{prefix}#{text_name}"
164
+ end
165
+ def text_name
166
+ SexpNode.format(name)
167
+ end
168
+ end
169
+
170
+ module ClassNode
171
+ include ModuleNode
172
+ def superclass() self[2] end
173
+ end
174
+
175
+ module YieldNode
176
+ def args() self[1..-1] end
177
+ def arg_names
178
+ args.map {|arg| arg[1]}
179
+ end
180
+ end
181
+ end
182
+
183
+ #
184
+ # Adorns an abstract syntax tree with mix-in modules to make accessing
185
+ # the tree more understandable and less implementation-dependent.
186
+ #
187
+ class TreeDresser
188
+
189
+ def dress(sexp)
190
+ sexp.extend(SexpNode)
191
+ module_name = extensions_for(sexp.sexp_type)
192
+ if SexpExtensions.const_defined?(module_name)
193
+ sexp.extend(SexpExtensions.const_get(module_name))
194
+ end
195
+ sexp[0..-1].each { |sub| dress(sub) if Array === sub }
196
+ sexp
197
+ end
198
+
199
+ def extensions_for(node_type)
200
+ "#{node_type.to_s.capitalize}Node"
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,18 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source', 'code_comment')
2
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source', 'config_file')
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source', 'core_extras')
4
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source', 'sexp_formatter')
5
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source', 'source_code')
6
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source', 'source_file')
7
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source', 'source_locator')
8
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source', 'tree_dresser')
9
+
10
+ module Reek
11
+
12
+ #
13
+ # This module contains a set of classes for interacting with Ruby
14
+ # source code and abstract syntax trees.
15
+ #
16
+ module Source # :nodoc: all
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'examiner')
2
+ require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'cli', 'report')
3
+
4
+ module Reek
5
+ module Spec
6
+
7
+ #
8
+ # An rspec matcher that matches when the +actual+ has code smells.
9
+ #
10
+ class ShouldReek # :nodoc:
11
+ def matches?(actual)
12
+ @examiner = Examiner.new(actual)
13
+ @examiner.smelly?
14
+ end
15
+ def failure_message_for_should
16
+ "Expected #{@examiner.description} to reek, but it didn't"
17
+ end
18
+ def failure_message_for_should_not
19
+ rpt = Cli::ReportFormatter.format_list(@examiner.smells)
20
+ "Expected no smells, but got:\n#{rpt}"
21
+ end
22
+ end
23
+
24
+ #
25
+ # Returns +true+ if and only if the target source code contains smells.
26
+ #
27
+ def reek
28
+ ShouldReek.new
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'examiner')
2
+
3
+ module Reek
4
+ module Spec
5
+
6
+ #
7
+ # An rspec matcher that matches when the +actual+ has the specified
8
+ # code smell.
9
+ #
10
+ class ShouldReekOf # :nodoc:
11
+ def initialize(klass, patterns)
12
+ @klass = klass
13
+ @patterns = patterns
14
+ end
15
+ def matches?(actual)
16
+ @examiner = Examiner.new(actual)
17
+ @all_smells = @examiner.smells
18
+ @all_smells.any? {|warning| warning.matches?(@klass, @patterns)}
19
+ end
20
+ def failure_message_for_should
21
+ "Expected #{@examiner.description} to reek of #{@klass}, but it didn't"
22
+ end
23
+ def failure_message_for_should_not
24
+ "Expected #{@examiner.description} not to reek of #{@klass}, but it did"
25
+ end
26
+ end
27
+
28
+ #
29
+ # Checks the target source code for instances of +smell_class+,
30
+ # and returns +true+ only if one of them has a report string matching
31
+ # all of the +patterns+.
32
+ #
33
+ def reek_of(smell_class, *patterns)
34
+ ShouldReekOf.new(smell_class, patterns)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'examiner')
2
+ require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'cli', 'report')
3
+
4
+ module Reek
5
+ module Spec
6
+
7
+ #
8
+ # An rspec matcher that matches when the +actual+ has the specified
9
+ # code smell and no others.
10
+ #
11
+ class ShouldReekOnlyOf < ShouldReekOf # :nodoc:
12
+ def matches?(actual)
13
+ matches_examiner?(Examiner.new(actual))
14
+ end
15
+ def matches_examiner?(examiner)
16
+ @examiner = examiner
17
+ @warnings = @examiner.smells
18
+ @warnings.length == 1 and @warnings[0].matches?(@klass, @patterns)
19
+ end
20
+ def failure_message_for_should
21
+ rpt = Cli::ReportFormatter.format_list(@warnings)
22
+ "Expected #{@examiner.description} to reek only of #{@klass}, but got:\n#{rpt}"
23
+ end
24
+ def failure_message_for_should_not
25
+ "Expected #{@examiner.description} not to reek only of #{@klass}, but it did"
26
+ end
27
+ end
28
+
29
+ #
30
+ # As for reek_of, but the matched smell warning must be the only warning of
31
+ # any kind in the target source code's Reek report.
32
+ #
33
+ def reek_only_of(smell_class, *patterns)
34
+ ShouldReekOnlyOf.new(smell_class, patterns)
35
+ end
36
+ end
37
+ end
data/lib/reek/spec.rb ADDED
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec', 'should_reek')
2
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec', 'should_reek_of')
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec', 'should_reek_only_of')
4
+
5
+ module Reek
6
+
7
+ #
8
+ # Provides matchers for Rspec, making it easy to check code quality.
9
+ #
10
+ # If you require this module somewhere within your spec (or in your spec_helper),
11
+ # Reek will arrange to update Spec::Runner's config so that it knows about the
12
+ # matchers defined here.
13
+ #
14
+ # === Examples
15
+ #
16
+ # Here's a spec that ensures there are no active code smells in the current project:
17
+ #
18
+ # describe 'source code quality' do
19
+ # Dir['lib/**/*.rb'].each do |path|
20
+ # it "reports no smells in #{path}" do
21
+ # File.new(path).should_not reek
22
+ # end
23
+ # end
24
+ # end
25
+ #
26
+ # And here's an even simpler way to do the same:
27
+ #
28
+ # it 'has no code smells' do
29
+ # Dir['lib/**/*.rb'].should_not reek
30
+ # end
31
+ #
32
+ # Here's a simple check of a code fragment:
33
+ #
34
+ # 'def equals(other) other.thing == self.thing end'.should_not reek
35
+ #
36
+ # To check for specific smells, use something like this:
37
+ #
38
+ # ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'
39
+ # ruby.should reek_of(:Duplication, /@other.thing[^\.]/)
40
+ # ruby.should reek_of(:Duplication, /@other.thing.foo/)
41
+ # ruby.should_not reek_of(:FeatureEnvy)
42
+ #
43
+ module Spec
44
+ end
45
+ end
46
+
47
+ if Object.const_defined?(:Spec)
48
+ Spec::Runner.configure do |config|
49
+ config.include(Reek::Spec)
50
+ end
51
+ end
data/lib/reek.rb CHANGED
@@ -1,5 +1,9 @@
1
- $:.unshift File.dirname(__FILE__)
2
-
3
- module Reek # :doc:
4
- VERSION = '1.2.6'
1
+ #
2
+ # Reek's core functionality
3
+ #
4
+ module Reek
5
+ VERSION = '1.2.8'
5
6
  end
7
+
8
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'reek', 'examiner')
9
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'reek', 'smell_warning')
data/reek.gemspec CHANGED
@@ -2,27 +2,29 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{reek}
5
- s.version = "1.2.6"
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{2009-11-28}
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.
13
13
  }
14
14
  s.email = ["kevin@rutherford-software.com"]
15
15
  s.executables = ["reek"]
16
- s.extra_rdoc_files = ["History.txt", "License.txt", "README.rdoc"]
17
- s.files = ["History.txt", "License.txt", "README.rdoc", "Rakefile", "bin/reek", "config/defaults.reek", "features/masking_smells.feature", "features/options.feature", "features/profile.feature", "features/rake_task.feature", "features/reports.feature", "features/samples.feature", "features/stdin.feature", "features/step_definitions/reek_steps.rb", "features/support/env.rb", "lib/reek.rb", "lib/reek/adapters/application.rb", "lib/reek/adapters/command_line.rb", "lib/reek/adapters/config_file.rb", "lib/reek/adapters/core_extras.rb", "lib/reek/adapters/rake_task.rb", "lib/reek/adapters/report.rb", "lib/reek/adapters/source.rb", "lib/reek/adapters/spec.rb", "lib/reek/block_context.rb", "lib/reek/class_context.rb", "lib/reek/code_context.rb", "lib/reek/code_parser.rb", "lib/reek/configuration.rb", "lib/reek/detector_stack.rb", "lib/reek/help_command.rb", "lib/reek/if_context.rb", "lib/reek/masking_collection.rb", "lib/reek/method_context.rb", "lib/reek/module_context.rb", "lib/reek/name.rb", "lib/reek/object_refs.rb", "lib/reek/reek_command.rb", "lib/reek/sexp_formatter.rb", "lib/reek/singleton_method_context.rb", "lib/reek/smell_warning.rb", "lib/reek/smells/attribute.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/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_name.rb", "lib/reek/smells/utility_function.rb", "lib/reek/sniffer.rb", "lib/reek/stop_context.rb", "lib/reek/tree_dresser.rb", "lib/reek/version_command.rb", "lib/reek/yield_call_context.rb", "reek.gemspec", "spec/reek/adapters/report_spec.rb", "spec/reek/adapters/should_reek_of_spec.rb", "spec/reek/adapters/should_reek_only_of_spec.rb", "spec/reek/adapters/should_reek_spec.rb", "spec/reek/adapters/source_spec.rb", "spec/reek/block_context_spec.rb", "spec/reek/class_context_spec.rb", "spec/reek/code_context_spec.rb", "spec/reek/code_parser_spec.rb", "spec/reek/config_spec.rb", "spec/reek/configuration_spec.rb", "spec/reek/help_command_spec.rb", "spec/reek/if_context_spec.rb", "spec/reek/masking_collection_spec.rb", "spec/reek/method_context_spec.rb", "spec/reek/module_context_spec.rb", "spec/reek/name_spec.rb", "spec/reek/object_refs_spec.rb", "spec/reek/object_source_spec.rb", "spec/reek/reek_command_spec.rb", "spec/reek/singleton_method_context_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/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/large_class_spec.rb", "spec/reek/smells/long_method_spec.rb", "spec/reek/smells/long_parameter_list_spec.rb", "spec/reek/smells/nested_iterators_spec.rb", "spec/reek/smells/simulated_polymorphism_spec.rb", "spec/reek/smells/smell_detector_spec.rb", "spec/reek/smells/uncommunicative_name_spec.rb", "spec/reek/smells/utility_function_spec.rb", "spec/reek/sniffer_spec.rb", "spec/reek/stop_context_spec.rb", "spec/reek/tree_dresser_spec.rb", "spec/reek/version_command_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"]
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/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
- s.rdoc_options = ["--main", "README.rdoc"]
24
+ s.rdoc_options = ["--main", "README.md"]
23
25
  s.require_paths = ["lib"]
24
26
  s.rubyforge_project = %q{reek}
25
- s.rubygems_version = %q{1.3.5}
27
+ s.rubygems_version = %q{1.3.6}
26
28
  s.summary = %q{Code smell detector for Ruby}
27
29
 
28
30
  if s.respond_to? :specification_version then
@@ -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
@@ -1,8 +1,7 @@
1
- require File.dirname(__FILE__) + '/../spec_helper.rb'
1
+ require File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'spec_helper')
2
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'cli', 'help_command')
2
3
 
3
- require 'reek/help_command'
4
-
5
- include Reek
4
+ include Reek::Cli
6
5
 
7
6
  describe HelpCommand do
8
7
  before :each do
@@ -0,0 +1,46 @@
1
+ require File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'spec_helper')
2
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'cli', 'reek_command')
3
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'cli', 'report')
4
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'examiner')
5
+
6
+ include Reek
7
+ include Reek::Cli
8
+
9
+ describe ReekCommand do
10
+ before :each do
11
+ @view = mock('view', :null_object => true)
12
+ end
13
+
14
+ context 'with smells' do
15
+ before :each do
16
+ examiner = Examiner.new('def x(); end')
17
+ @cmd = ReekCommand.new(QuietReport, ['def x(); end'])
18
+ end
19
+
20
+ it 'displays the correct text on the view' do
21
+ @view.should_receive(:output).with(/UncommunicativeName/)
22
+ @cmd.execute(@view)
23
+ end
24
+
25
+ it 'tells the view it succeeded' do
26
+ @view.should_receive(:report_smells)
27
+ @cmd.execute(@view)
28
+ end
29
+ end
30
+
31
+ context 'with no smells' do
32
+ before :each do
33
+ @cmd = ReekCommand.new(QuietReport, ['def clean(); end'])
34
+ end
35
+
36
+ it 'displays nothing on the view' do
37
+ @view.should_receive(:output).with('')
38
+ @cmd.execute(@view)
39
+ end
40
+
41
+ it 'tells the view it succeeded' do
42
+ @view.should_receive(:report_success)
43
+ @cmd.execute(@view)
44
+ end
45
+ end
46
+ end