reek 1.2.6 → 1.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. data/.yardopts +10 -0
  2. data/History.txt +20 -0
  3. data/README.md +90 -0
  4. data/bin/reek +2 -2
  5. data/config/defaults.reek +34 -4
  6. data/features/masking_smells.feature +35 -15
  7. data/features/options.feature +2 -0
  8. data/features/rake_task.feature +11 -18
  9. data/features/reports.feature +13 -15
  10. data/features/samples.feature +90 -105
  11. data/features/stdin.feature +3 -6
  12. data/features/step_definitions/reek_steps.rb +8 -4
  13. data/features/support/env.rb +2 -3
  14. data/features/yaml.feature +124 -0
  15. data/lib/reek.rb +8 -4
  16. data/lib/reek/cli/application.rb +46 -0
  17. data/lib/reek/cli/command_line.rb +106 -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 +91 -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/block_context.rb +18 -0
  24. data/lib/reek/core/class_context.rb +23 -0
  25. data/lib/reek/core/code_context.rb +72 -0
  26. data/lib/reek/core/code_parser.rb +192 -0
  27. data/lib/reek/core/detector_stack.rb +29 -0
  28. data/lib/reek/core/masking_collection.rb +46 -0
  29. data/lib/reek/core/method_context.rb +132 -0
  30. data/lib/reek/core/module_context.rb +64 -0
  31. data/lib/reek/{object_refs.rb → core/object_refs.rb} +8 -6
  32. data/lib/reek/{singleton_method_context.rb → core/singleton_method_context.rb} +10 -5
  33. data/lib/reek/core/smell_configuration.rb +66 -0
  34. data/lib/reek/core/sniffer.rb +110 -0
  35. data/lib/reek/core/stop_context.rb +26 -0
  36. data/lib/reek/examiner.rb +88 -0
  37. data/lib/reek/rake/task.rb +124 -0
  38. data/lib/reek/smell_warning.rb +69 -13
  39. data/lib/reek/smells.rb +29 -0
  40. data/lib/reek/smells/attribute.rb +13 -14
  41. data/lib/reek/smells/boolean_parameter.rb +33 -0
  42. data/lib/reek/smells/class_variable.rb +8 -6
  43. data/lib/reek/smells/control_couple.rb +33 -17
  44. data/lib/reek/smells/data_clump.rb +10 -6
  45. data/lib/reek/smells/duplication.rb +24 -14
  46. data/lib/reek/smells/feature_envy.rb +11 -6
  47. data/lib/reek/smells/irresponsible_module.rb +28 -0
  48. data/lib/reek/smells/large_class.rb +9 -7
  49. data/lib/reek/smells/long_method.rb +6 -5
  50. data/lib/reek/smells/long_parameter_list.rb +11 -9
  51. data/lib/reek/smells/long_yield_list.rb +37 -7
  52. data/lib/reek/smells/nested_iterators.rb +34 -9
  53. data/lib/reek/smells/simulated_polymorphism.rb +15 -11
  54. data/lib/reek/smells/smell_detector.rb +24 -12
  55. data/lib/reek/smells/uncommunicative_method_name.rb +76 -0
  56. data/lib/reek/smells/uncommunicative_module_name.rb +76 -0
  57. data/lib/reek/smells/{uncommunicative_name.rb → uncommunicative_parameter_name.rb} +14 -26
  58. data/lib/reek/smells/uncommunicative_variable_name.rb +90 -0
  59. data/lib/reek/smells/utility_function.rb +33 -9
  60. data/lib/reek/source.rb +18 -0
  61. data/lib/reek/source/code_comment.rb +19 -0
  62. data/lib/reek/source/config_file.rb +72 -0
  63. data/lib/reek/source/core_extras.rb +46 -0
  64. data/lib/reek/source/sexp_formatter.rb +16 -0
  65. data/lib/reek/source/source_code.rb +44 -0
  66. data/lib/reek/source/source_file.rb +32 -0
  67. data/lib/reek/source/source_locator.rb +36 -0
  68. data/lib/reek/source/tree_dresser.rb +128 -0
  69. data/lib/reek/spec.rb +51 -0
  70. data/lib/reek/spec/should_reek.rb +34 -0
  71. data/lib/reek/spec/should_reek_of.rb +37 -0
  72. data/lib/reek/spec/should_reek_only_of.rb +36 -0
  73. data/reek.gemspec +5 -5
  74. data/spec/reek/{help_command_spec.rb → cli/help_command_spec.rb} +3 -4
  75. data/spec/reek/{reek_command_spec.rb → cli/reek_command_spec.rb} +8 -7
  76. data/spec/reek/cli/report_spec.rb +26 -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/block_context_spec.rb +26 -0
  80. data/spec/reek/core/class_context_spec.rb +53 -0
  81. data/spec/reek/{code_context_spec.rb → core/code_context_spec.rb} +15 -37
  82. data/spec/reek/{code_parser_spec.rb → core/code_parser_spec.rb} +5 -5
  83. data/spec/reek/{config_spec.rb → core/config_spec.rb} +2 -6
  84. data/spec/reek/{masking_collection_spec.rb → core/masking_collection_spec.rb} +3 -4
  85. data/spec/reek/{method_context_spec.rb → core/method_context_spec.rb} +6 -7
  86. data/spec/reek/core/module_context_spec.rb +42 -0
  87. data/spec/reek/{object_refs_spec.rb → core/object_refs_spec.rb} +5 -6
  88. data/spec/reek/core/singleton_method_context_spec.rb +15 -0
  89. data/spec/reek/core/smell_configuration_spec.rb +11 -0
  90. data/spec/reek/core/stop_context_spec.rb +17 -0
  91. data/spec/reek/examiner_spec.rb +42 -0
  92. data/spec/reek/smell_warning_spec.rb +82 -33
  93. data/spec/reek/smells/attribute_spec.rb +33 -7
  94. data/spec/reek/smells/boolean_parameter_spec.rb +76 -0
  95. data/spec/reek/smells/class_variable_spec.rb +15 -6
  96. data/spec/reek/smells/control_couple_spec.rb +40 -29
  97. data/spec/reek/smells/data_clump_spec.rb +28 -7
  98. data/spec/reek/smells/duplication_spec.rb +47 -41
  99. data/spec/reek/smells/feature_envy_spec.rb +76 -18
  100. data/spec/reek/smells/irresponsible_module_spec.rb +37 -0
  101. data/spec/reek/smells/large_class_spec.rb +91 -56
  102. data/spec/reek/smells/long_method_spec.rb +32 -7
  103. data/spec/reek/smells/long_parameter_list_spec.rb +42 -13
  104. data/spec/reek/smells/long_yield_list_spec.rb +65 -0
  105. data/spec/reek/smells/nested_iterators_spec.rb +94 -3
  106. data/spec/reek/smells/simulated_polymorphism_spec.rb +48 -20
  107. data/spec/reek/smells/smell_detector_shared.rb +28 -0
  108. data/spec/reek/smells/uncommunicative_method_name_spec.rb +57 -0
  109. data/spec/reek/smells/uncommunicative_module_name_spec.rb +67 -0
  110. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +61 -0
  111. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +124 -0
  112. data/spec/reek/smells/utility_function_spec.rb +45 -3
  113. data/spec/reek/source/code_comment_spec.rb +24 -0
  114. data/spec/reek/source/object_source_spec.rb +20 -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 +165 -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/corrupt_config_file/dirty.rb +1 -1
  130. data/spec/samples/empty_config_file/dirty.rb +2 -1
  131. data/spec/samples/exceptions.reek +1 -1
  132. data/spec/samples/masked/dirty.rb +2 -1
  133. data/spec/samples/masked/masked.reek +3 -1
  134. data/spec/samples/mixed_results/clean_one.rb +1 -0
  135. data/spec/samples/mixed_results/clean_three.rb +1 -0
  136. data/spec/samples/mixed_results/clean_two.rb +1 -0
  137. data/spec/samples/mixed_results/dirty_one.rb +1 -0
  138. data/spec/samples/mixed_results/dirty_two.rb +1 -0
  139. data/spec/samples/not_quite_masked/dirty.rb +2 -1
  140. data/spec/samples/not_quite_masked/masked.reek +1 -1
  141. data/spec/samples/overrides/masked/dirty.rb +2 -1
  142. data/spec/samples/overrides/masked/lower.reek +3 -1
  143. data/spec/samples/three_clean_files/clean_one.rb +1 -0
  144. data/spec/samples/three_clean_files/clean_three.rb +1 -0
  145. data/spec/samples/three_clean_files/clean_two.rb +1 -0
  146. data/spec/samples/two_smelly_files/dirty_one.rb +2 -1
  147. data/spec/samples/two_smelly_files/dirty_two.rb +2 -1
  148. data/spec/spec_helper.rb +1 -2
  149. data/tasks/reek.rake +2 -2
  150. data/tasks/test.rake +12 -3
  151. metadata +81 -62
  152. data/README.rdoc +0 -84
  153. data/lib/reek/adapters/application.rb +0 -46
  154. data/lib/reek/adapters/command_line.rb +0 -77
  155. data/lib/reek/adapters/config_file.rb +0 -31
  156. data/lib/reek/adapters/core_extras.rb +0 -64
  157. data/lib/reek/adapters/rake_task.rb +0 -121
  158. data/lib/reek/adapters/report.rb +0 -86
  159. data/lib/reek/adapters/source.rb +0 -72
  160. data/lib/reek/adapters/spec.rb +0 -133
  161. data/lib/reek/block_context.rb +0 -62
  162. data/lib/reek/class_context.rb +0 -41
  163. data/lib/reek/code_context.rb +0 -68
  164. data/lib/reek/code_parser.rb +0 -203
  165. data/lib/reek/configuration.rb +0 -57
  166. data/lib/reek/detector_stack.rb +0 -37
  167. data/lib/reek/help_command.rb +0 -14
  168. data/lib/reek/if_context.rb +0 -18
  169. data/lib/reek/masking_collection.rb +0 -33
  170. data/lib/reek/method_context.rb +0 -138
  171. data/lib/reek/module_context.rb +0 -49
  172. data/lib/reek/name.rb +0 -57
  173. data/lib/reek/reek_command.rb +0 -28
  174. data/lib/reek/sexp_formatter.rb +0 -10
  175. data/lib/reek/sniffer.rb +0 -177
  176. data/lib/reek/stop_context.rb +0 -35
  177. data/lib/reek/tree_dresser.rb +0 -82
  178. data/lib/reek/version_command.rb +0 -14
  179. data/lib/reek/yield_call_context.rb +0 -12
  180. data/spec/reek/adapters/report_spec.rb +0 -31
  181. data/spec/reek/adapters/should_reek_of_spec.rb +0 -138
  182. data/spec/reek/adapters/should_reek_only_of_spec.rb +0 -87
  183. data/spec/reek/block_context_spec.rb +0 -65
  184. data/spec/reek/class_context_spec.rb +0 -161
  185. data/spec/reek/configuration_spec.rb +0 -12
  186. data/spec/reek/if_context_spec.rb +0 -17
  187. data/spec/reek/module_context_spec.rb +0 -46
  188. data/spec/reek/name_spec.rb +0 -37
  189. data/spec/reek/object_source_spec.rb +0 -23
  190. data/spec/reek/singleton_method_context_spec.rb +0 -16
  191. data/spec/reek/smells/smell_detector_spec.rb +0 -36
  192. data/spec/reek/smells/uncommunicative_name_spec.rb +0 -146
  193. data/spec/reek/sniffer_spec.rb +0 -11
  194. data/spec/reek/stop_context_spec.rb +0 -33
  195. data/spec/reek/tree_dresser_spec.rb +0 -20
@@ -0,0 +1,192 @@
1
+ require 'sexp'
2
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'block_context')
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'class_context')
4
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'method_context')
5
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'module_context')
6
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'stop_context')
7
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'singleton_method_context')
8
+
9
+ module Reek
10
+ module Core
11
+
12
+ #
13
+ # Traverses a Sexp abstract syntax tree and fires events whenever
14
+ # it encounters specific node types.
15
+ #
16
+ class CodeParser
17
+ def initialize(sniffer, ctx = StopContext.new)
18
+ @sniffer = sniffer
19
+ @element = ctx
20
+ end
21
+
22
+ def process(exp)
23
+ meth = "process_#{exp[0]}"
24
+ meth = :process_default unless self.respond_to?(meth)
25
+ self.send(meth, exp)
26
+ @element
27
+ end
28
+
29
+ def process_default(exp)
30
+ exp[0..-1].each { |sub| process(sub) if Array === sub }
31
+ end
32
+
33
+ def do_module_or_class(exp, context_class)
34
+ scope = context_class.create(@element, exp)
35
+ push(scope) do
36
+ process_default(exp) unless @element.is_struct?
37
+ check_smells(exp[0])
38
+ end
39
+ scope
40
+ end
41
+
42
+ def process_module(exp)
43
+ do_module_or_class(exp, ModuleContext)
44
+ end
45
+
46
+ def process_class(exp)
47
+ do_module_or_class(exp, ClassContext)
48
+ end
49
+
50
+ def process_defn(exp)
51
+ handle_context(MethodContext, exp[0], exp)
52
+ end
53
+
54
+ def process_defs(exp)
55
+ handle_context(SingletonMethodContext, exp[0], exp)
56
+ end
57
+
58
+ def process_args(exp) end
59
+
60
+ def process_zsuper(exp)
61
+ @element.record_use_of_self
62
+ end
63
+
64
+ def process_lit(exp)
65
+ val = exp[1]
66
+ @element.record_depends_on_self if val == :self
67
+ end
68
+
69
+ def process_iter(exp)
70
+ process(exp[1])
71
+ scope = BlockContext.new(@element, exp)
72
+ push(scope) do
73
+ process_default(exp[2..-1])
74
+ check_smells(exp[0])
75
+ end
76
+ scope
77
+ end
78
+
79
+ def process_block(exp)
80
+ @element.count_statements(CodeParser.count_statements(exp))
81
+ process_default(exp)
82
+ end
83
+
84
+ def process_call(exp)
85
+ @element.record_call_to(exp)
86
+ process_default(exp)
87
+ end
88
+
89
+ def process_attrasgn(exp)
90
+ process_call(exp)
91
+ end
92
+
93
+ def process_op_asgn1(exp)
94
+ process_call(exp)
95
+ end
96
+
97
+ def process_if(exp)
98
+ count_clause(exp[2])
99
+ count_clause(exp[3])
100
+ process_default(exp)
101
+ @element.count_statements(-1)
102
+ end
103
+
104
+ def process_while(exp)
105
+ process_until(exp)
106
+ end
107
+
108
+ def process_until(exp)
109
+ count_clause(exp[2])
110
+ process_case(exp)
111
+ end
112
+
113
+ def process_for(exp)
114
+ count_clause(exp[3])
115
+ process_case(exp)
116
+ end
117
+
118
+ def process_rescue(exp)
119
+ count_clause(exp[1])
120
+ process_case(exp)
121
+ end
122
+
123
+ def process_resbody(exp)
124
+ process_when(exp)
125
+ end
126
+
127
+ def process_case(exp)
128
+ process_default(exp)
129
+ @element.count_statements(-1)
130
+ end
131
+
132
+ def process_when(exp)
133
+ count_clause(exp[2])
134
+ process_default(exp)
135
+ end
136
+
137
+ def process_ivar(exp)
138
+ process_iasgn(exp)
139
+ end
140
+
141
+ def process_lasgn(exp)
142
+ @element.record_local_variable(exp[1])
143
+ process_default(exp)
144
+ end
145
+
146
+ def process_iasgn(exp)
147
+ @element.record_instance_variable(exp[1])
148
+ @element.record_depends_on_self
149
+ process_default(exp)
150
+ end
151
+
152
+ def process_self(exp)
153
+ @element.record_use_of_self
154
+ end
155
+
156
+ def count_clause(sexp)
157
+ if sexp and !sexp.has_type?(:block)
158
+ @element.count_statements(1)
159
+ end
160
+ end
161
+
162
+ def self.count_statements(exp)
163
+ stmts = exp[1..-1]
164
+ ignore = 0
165
+ ignore += 1 if stmts[1] == s(:nil)
166
+ stmts.length - ignore
167
+ end
168
+
169
+ private
170
+
171
+ def handle_context(klass, type, exp)
172
+ scope = klass.new(@element, exp)
173
+ push(scope) do
174
+ process_default(exp)
175
+ check_smells(type)
176
+ end
177
+ scope
178
+ end
179
+
180
+ def check_smells(type)
181
+ @sniffer.examine(@element, type)
182
+ end
183
+
184
+ def push(context)
185
+ orig = @element
186
+ @element = context
187
+ yield
188
+ @element = orig
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,29 @@
1
+
2
+ module Reek
3
+ module Core
4
+
5
+ #
6
+ # A list of smell detectors of the same class, but with differing
7
+ # configurations.
8
+ #
9
+ class DetectorStack
10
+
11
+ def initialize(default_detector)
12
+ @detectors = [default_detector]
13
+ end
14
+
15
+ def push(config)
16
+ clone = @detectors[-1].supersede_with(config)
17
+ @detectors << clone
18
+ end
19
+
20
+ def listen_to(hooks)
21
+ @detectors.each { |det| det.listen_to(hooks) }
22
+ end
23
+
24
+ def report_on(report)
25
+ @detectors.each { |det| det.report_on(report) }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,46 @@
1
+ require 'set'
2
+
3
+ module Reek
4
+ module Core
5
+
6
+ #
7
+ # A set of items that can be "masked" or "visible". If an item is added
8
+ # more than once, only the "visible" copy is retained.
9
+ #
10
+ class MaskingCollection
11
+ def initialize
12
+ @visible_items = SortedSet.new
13
+ @masked_items = SortedSet.new
14
+ end
15
+ def all_items
16
+ all = SortedSet.new(@visible_items)
17
+ all.merge(@masked_items)
18
+ all.to_a
19
+ end
20
+ def all_active_items
21
+ @visible_items
22
+ end
23
+ def found_smell(item)
24
+ @visible_items.add(item)
25
+ @masked_items.delete(item) if @masked_items.include?(item)
26
+ end
27
+ def found_masked_smell(item)
28
+ @masked_items.add(item) unless @visible_items.include?(item)
29
+ end
30
+ def num_visible_items
31
+ @visible_items.length
32
+ end
33
+ def num_masked_items
34
+ @masked_items.length
35
+ end
36
+ def each_item(&blk)
37
+ all = SortedSet.new(@visible_items)
38
+ all.merge(@masked_items)
39
+ all.each(&blk)
40
+ end
41
+ def each_visible_item(&blk)
42
+ @visible_items.each(&blk)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,132 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'object_refs')
2
+
3
+ #
4
+ # Extensions to +Array+ needed by Reek.
5
+ #
6
+ class Array
7
+ def power_set
8
+ self.inject([[]]) { |cum, element| cum.cross(element) }
9
+ end
10
+
11
+ def bounded_power_set(lower_bound)
12
+ power_set.select {|ps| ps.length > lower_bound}
13
+ end
14
+
15
+ def cross(element)
16
+ result = []
17
+ self.each do |set|
18
+ result << set
19
+ result << (set + [element])
20
+ end
21
+ result
22
+ end
23
+
24
+ def intersection
25
+ self.inject { |res, elem| elem & res }
26
+ end
27
+ end
28
+
29
+ module Reek
30
+ module Core
31
+
32
+ #
33
+ # The parameters in a method's definition.
34
+ #
35
+ module MethodParameters
36
+ def default_assignments
37
+ assignments = self[-1]
38
+ result = {}
39
+ return result unless is_assignment_block?(assignments)
40
+ assignments[1..-1].each do |exp|
41
+ result[exp[1]] = exp[2] if exp[0] == :lasgn
42
+ end
43
+ result
44
+ end
45
+ def is_arg?(param)
46
+ return false if is_assignment_block?(param)
47
+ return !(param.to_s =~ /^\&/)
48
+ end
49
+ def is_assignment_block?(param)
50
+ Array === param and param[0] == :block
51
+ end
52
+
53
+ def names
54
+ return @names if @names
55
+ @names = self[1..-1].select {|arg| is_arg?(arg)}.map {|arg| arg.to_s }
56
+ end
57
+
58
+ def length
59
+ names.length
60
+ end
61
+
62
+ def include?(name)
63
+ names.include?(name)
64
+ end
65
+ end
66
+
67
+ #
68
+ # A context wrapper for any method definition found in a syntax tree.
69
+ #
70
+ class MethodContext < CodeContext
71
+ attr_reader :parameters
72
+ attr_reader :refs
73
+ attr_reader :num_statements
74
+
75
+ def initialize(outer, exp)
76
+ super(outer, exp)
77
+ @parameters = exp[exp[0] == :defn ? 2 : 3] # SMELL: SimulatedPolymorphism
78
+ @parameters ||= []
79
+ @parameters.extend(MethodParameters)
80
+ @name = exp[1].to_s
81
+ @scope_connector = '#'
82
+ @num_statements = 0
83
+ @depends_on_self = false
84
+ @refs = ObjectRefs.new
85
+ @outer.record_method(self) # SMELL: these could be found by tree walking
86
+ end
87
+
88
+ def count_statements(num)
89
+ @num_statements += num
90
+ end
91
+
92
+ def depends_on_instance?
93
+ @depends_on_self
94
+ end
95
+
96
+ def record_call_to(exp)
97
+ record_receiver(exp)
98
+ end
99
+
100
+ def record_use_of_self
101
+ record_depends_on_self
102
+ @refs.record_reference_to_self
103
+ end
104
+
105
+ def record_instance_variable(sym)
106
+ record_use_of_self
107
+ end
108
+
109
+ def record_depends_on_self
110
+ @depends_on_self = true
111
+ end
112
+
113
+ def envious_receivers
114
+ return [] if @refs.self_is_max?
115
+ @refs.max_keys
116
+ end
117
+
118
+ private
119
+
120
+ def record_receiver(exp)
121
+ receiver, meth = exp[1..2]
122
+ receiver ||= [:self]
123
+ case receiver[0]
124
+ when :lvar
125
+ @refs.record_ref(receiver) unless meth == :new
126
+ when :self
127
+ record_use_of_self
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,64 @@
1
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'code_context')
2
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'code_parser')
3
+ require File.join( File.dirname( File.expand_path(__FILE__)), 'sniffer')
4
+
5
+ module Reek
6
+ module Core
7
+
8
+ #
9
+ # A context wrapper for any module found in a syntax tree.
10
+ #
11
+ class ModuleContext < CodeContext
12
+
13
+ class << self
14
+ def create(outer, exp)
15
+ res = resolve(exp[1], outer)
16
+ new(res[0], res[1], exp)
17
+ end
18
+
19
+ def from_s(src)
20
+ source = src.to_reek_source
21
+ sniffer = Sniffer.new(source)
22
+ CodeParser.new(sniffer).do_module_or_class(source.syntax_tree, self)
23
+ end
24
+
25
+ def resolve(exp, context)
26
+ unless Array === exp
27
+ return resolve_string(exp.to_s, context)
28
+ end
29
+ name = exp[1]
30
+ case exp[0]
31
+ when :colon2
32
+ return [resolve(name, context)[0], exp[2].to_s]
33
+ when :const
34
+ return [ModuleContext.create(context, exp), name.to_s]
35
+ when :colon3
36
+ return [StopContext.new, name.to_s]
37
+ else
38
+ return [context, name.to_s]
39
+ end
40
+ end
41
+
42
+ def resolve_string(str, context)
43
+ return [context, str.to_s] unless str =~ /::/
44
+ resolve(RubyParser.new.parse(str), context)
45
+ end
46
+ end
47
+
48
+ def initialize(outer, name, exp)
49
+ super(outer, exp)
50
+ @name = name
51
+ @scope_connector = '::'
52
+ @parsed_methods = []
53
+ end
54
+
55
+ def parameterized_methods(min_clump_size)
56
+ @parsed_methods.select {|meth| meth.parameters.length >= min_clump_size }
57
+ end
58
+
59
+ def record_method(meth)
60
+ @parsed_methods << meth
61
+ end
62
+ end
63
+ end
64
+ end