reek 1.2.1 → 1.2.3
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.
- data/History.txt +10 -0
- data/Rakefile +0 -1
- data/config/defaults.reek +8 -6
- data/features/masking_smells.feature +9 -9
- data/features/options.feature +2 -2
- data/features/profile.feature +34 -0
- data/features/rake_task.feature +74 -0
- data/features/reports.feature +1 -1
- data/features/samples.feature +4 -4
- data/features/stdin.feature +1 -1
- data/features/step_definitions/reek_steps.rb +11 -7
- data/features/support/env.rb +26 -18
- data/lib/reek/adapters/application.rb +9 -2
- data/lib/reek/adapters/command_line.rb +2 -2
- data/lib/reek/adapters/core_extras.rb +0 -8
- data/lib/reek/adapters/source.rb +4 -1
- data/lib/reek/adapters/spec.rb +1 -4
- data/lib/reek/block_context.rb +14 -8
- data/lib/reek/class_context.rb +6 -55
- data/lib/reek/code_context.rb +10 -0
- data/lib/reek/code_parser.rb +26 -53
- data/lib/reek/configuration.rb +12 -6
- data/lib/reek/if_context.rb +2 -3
- data/lib/reek/method_context.rb +8 -12
- data/lib/reek/module_context.rb +35 -16
- data/lib/reek/name.rb +2 -0
- data/lib/reek/object_refs.rb +0 -3
- data/lib/reek/sexp_formatter.rb +0 -2
- data/lib/reek/smells/attribute.rb +48 -0
- data/lib/reek/smells/class_variable.rb +17 -4
- data/lib/reek/smells/control_couple.rb +3 -10
- data/lib/reek/smells/data_clump.rb +10 -10
- data/lib/reek/smells/feature_envy.rb +1 -8
- data/lib/reek/smells/large_class.rb +3 -3
- data/lib/reek/smells/simulated_polymorphism.rb +17 -3
- data/lib/reek/smells/smell_detector.rb +11 -2
- data/lib/reek/smells/utility_function.rb +1 -1
- data/lib/reek/sniffer.rb +2 -8
- data/lib/reek/stop_context.rb +1 -1
- data/lib/reek/tree_dresser.rb +74 -0
- data/lib/reek.rb +1 -1
- data/reek.gemspec +3 -3
- data/spec/reek/adapters/should_reek_of_spec.rb +7 -1
- data/spec/reek/block_context_spec.rb +6 -6
- data/spec/reek/class_context_spec.rb +2 -23
- data/spec/reek/code_context_spec.rb +149 -67
- data/spec/reek/code_parser_spec.rb +35 -51
- data/spec/reek/method_context_spec.rb +4 -4
- data/spec/reek/singleton_method_context_spec.rb +1 -1
- data/spec/reek/smells/attribute_spec.rb +26 -0
- data/spec/reek/smells/behaves_like_variable_detector.rb +39 -0
- data/spec/reek/smells/class_variable_spec.rb +77 -43
- data/spec/reek/smells/control_couple_spec.rb +1 -1
- data/spec/reek/smells/data_clump_spec.rb +31 -13
- data/spec/reek/smells/feature_envy_spec.rb +1 -1
- data/spec/reek/smells/large_class_spec.rb +32 -69
- data/spec/reek/smells/long_parameter_list_spec.rb +0 -12
- data/spec/reek/smells/simulated_polymorphism_spec.rb +66 -18
- data/spec/reek/smells/utility_function_spec.rb +0 -21
- data/spec/reek/sniffer_spec.rb +1 -0
- data/spec/samples/not_quite_masked/dirty.rb +2 -0
- data/spec/spec_helper.rb +1 -1
- data/tasks/reek.rake +1 -1
- data/tasks/test.rake +3 -4
- metadata +8 -5
- data/lib/reek/adapters/object_source.rb +0 -52
- data/lib/reek/exceptions.reek +0 -20
- data/spec/quality/reek_source_spec.rb +0 -15
@@ -0,0 +1,74 @@
|
|
1
|
+
module Reek
|
2
|
+
|
3
|
+
#
|
4
|
+
# Extensions to +Sexp+ to allow +CodeParser+ to navigate the abstract
|
5
|
+
# syntax tree more easily.
|
6
|
+
#
|
7
|
+
module SexpNode
|
8
|
+
def children
|
9
|
+
find_all { |item| Sexp === item }
|
10
|
+
end
|
11
|
+
|
12
|
+
def is_language_node?
|
13
|
+
first.class == Symbol
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_type?(type)
|
17
|
+
is_language_node? and first == type
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Carries out a depth-first traversal of this syntax tree, yielding
|
22
|
+
# every Sexp of type +target_type+. The traversal ignores any node
|
23
|
+
# whose type is listed in the Array +ignoring+.
|
24
|
+
#
|
25
|
+
def look_for(target_type, ignoring, &blk)
|
26
|
+
each do |elem|
|
27
|
+
if Sexp === elem then
|
28
|
+
elem.look_for(target_type, ignoring, &blk) unless ignoring.include?(elem.first)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
blk.call(self) if first == target_type
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module CvarNode
|
36
|
+
def name
|
37
|
+
self[1]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module IfNode
|
42
|
+
def condition
|
43
|
+
self[1]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module CaseNode
|
48
|
+
def condition
|
49
|
+
self[1]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class TreeDresser
|
54
|
+
# SMELL: Duplication
|
55
|
+
# Put these into a new module and build the mapping automagically
|
56
|
+
# based on the node type
|
57
|
+
EXTENSIONS = {
|
58
|
+
:cvar => CvarNode,
|
59
|
+
:cvasgn => CvarNode,
|
60
|
+
:cvdecl => CvarNode,
|
61
|
+
:if => IfNode,
|
62
|
+
:case => CaseNode,
|
63
|
+
}
|
64
|
+
|
65
|
+
def dress(sexp)
|
66
|
+
sexp.extend(SexpNode)
|
67
|
+
if EXTENSIONS.has_key?(sexp[0])
|
68
|
+
sexp.extend(EXTENSIONS[sexp[0]])
|
69
|
+
end
|
70
|
+
sexp[0..-1].each { |sub| dress(sub) if Array === sub }
|
71
|
+
sexp
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/reek.rb
CHANGED
data/reek.gemspec
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{reek}
|
5
|
-
s.version = "1.2.
|
5
|
+
s.version = "1.2.3"
|
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-
|
9
|
+
s.date = %q{2009-11-02}
|
10
10
|
s.default_executable = %q{reek}
|
11
11
|
s.description = %q{Code smell detector for Ruby}
|
12
12
|
s.email = ["kevin@rutherford-software.com"]
|
13
13
|
s.executables = ["reek"]
|
14
14
|
s.extra_rdoc_files = ["History.txt", "License.txt", "README.rdoc"]
|
15
|
-
s.files = ["History.txt", "License.txt", "README.rdoc", "Rakefile", "bin/reek", "config/defaults.reek", "features/masking_smells.feature", "features/options.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/
|
15
|
+
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/if_context.rb", "lib/reek/method_context.rb", "lib/reek/module_context.rb", "lib/reek/name.rb", "lib/reek/object_refs.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/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/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/if_context_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/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/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
16
|
s.homepage = %q{http://wiki.github.com/kevinrutherford/reek}
|
17
17
|
s.post_install_message = %q{
|
18
18
|
For more information on reek, see http://wiki.github.com/kevinrutherford/reek
|
@@ -8,8 +8,14 @@ include Reek::Spec
|
|
8
8
|
|
9
9
|
# belongs in its own spec file
|
10
10
|
describe ReekMatcher do
|
11
|
+
before :each do
|
12
|
+
smelly_code = Dir['spec/samples/two_smelly_files/*.rb']
|
13
|
+
@sniffers = smelly_code.sniff.sniffers
|
14
|
+
@full = FullReport.new(@sniffers, '%c %w (%s)', false).report
|
15
|
+
end
|
16
|
+
|
11
17
|
it 'reports quietly' do
|
12
|
-
@
|
18
|
+
ReekMatcher.create_reporter(@sniffers).should_not == @full
|
13
19
|
end
|
14
20
|
end
|
15
21
|
|
@@ -9,37 +9,37 @@ describe BlockContext do
|
|
9
9
|
|
10
10
|
it "should record single parameter" do
|
11
11
|
element = StopContext.new
|
12
|
-
element = BlockContext.new(element, s(s(:lasgn, :x), nil))
|
12
|
+
element = BlockContext.new(element, s(:iter, nil, s(:lasgn, :x), nil))
|
13
13
|
element.variable_names.should == [Name.new(:x)]
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should record single parameter within a method" do
|
17
17
|
element = StopContext.new
|
18
18
|
element = MethodContext.new(element, s(:defn, :help))
|
19
|
-
element = BlockContext.new(element, s(s(:lasgn, :x), nil))
|
19
|
+
element = BlockContext.new(element, s(:iter, nil, s(:lasgn, :x), nil))
|
20
20
|
element.variable_names.should == [Name.new(:x)]
|
21
21
|
end
|
22
22
|
|
23
23
|
it "records multiple parameters" do
|
24
24
|
element = StopContext.new
|
25
|
-
element = BlockContext.new(element, s(s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))), nil))
|
25
|
+
element = BlockContext.new(element, s(:iter, nil, s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))), nil))
|
26
26
|
element.variable_names.should == [Name.new(:x), Name.new(:y)]
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should not pass parameters upward" do
|
30
30
|
mc = MethodContext.new(StopContext.new, s(:defn, :help, s(:args)))
|
31
|
-
element = BlockContext.new(mc, s(s(:lasgn, :x)))
|
31
|
+
element = BlockContext.new(mc, s(:iter, nil, s(:lasgn, :x)))
|
32
32
|
mc.variable_names.should be_empty
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'records local variables' do
|
36
|
-
bctx = BlockContext.new(StopContext.new, nil)
|
36
|
+
bctx = BlockContext.new(StopContext.new, s(nil, nil))
|
37
37
|
bctx.record_local_variable(:q2)
|
38
38
|
bctx.variable_names.should include(Name.new(:q2))
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'copes with a yield to an ivar' do
|
42
|
-
scope = BlockContext.new(StopContext.new,
|
42
|
+
scope = BlockContext.new(StopContext.new, s(:iter, nil, s(:iasgn, :@list), s(:self)))
|
43
43
|
scope.record_instance_variable(:@list)
|
44
44
|
scope.variable_names.should == [:@list]
|
45
45
|
end
|
@@ -150,33 +150,12 @@ describe CodeContext, 'find class' do
|
|
150
150
|
end
|
151
151
|
|
152
152
|
describe ClassContext do
|
153
|
-
it '
|
153
|
+
it 'does not report empty class in another module' do
|
154
154
|
'class Treetop::Runtime::SyntaxNode; end'.should_not reek
|
155
155
|
end
|
156
156
|
|
157
|
-
it '
|
157
|
+
it 'deals with :: scoped names' do
|
158
158
|
element = ClassContext.create(StopContext.new, [:colon2, [:colon2, [:const, :Treetop], :Runtime], :SyntaxNode])
|
159
159
|
element.num_methods.should == 0
|
160
160
|
end
|
161
|
-
|
162
|
-
it 'counts conditionals correctly' do
|
163
|
-
src = <<EOS
|
164
|
-
class Scrunch
|
165
|
-
def first
|
166
|
-
return @field == :sym ? 0 : 3;
|
167
|
-
end
|
168
|
-
def second
|
169
|
-
if @field == :sym
|
170
|
-
@other += " quarts"
|
171
|
-
end
|
172
|
-
end
|
173
|
-
def third
|
174
|
-
raise 'flu!' unless @field == :sym
|
175
|
-
end
|
176
|
-
end
|
177
|
-
EOS
|
178
|
-
|
179
|
-
ctx = ClassContext.from_s(src)
|
180
|
-
ctx.conditionals.length.should == 3
|
181
|
-
end
|
182
161
|
end
|
@@ -9,85 +9,167 @@ require 'reek/stop_context'
|
|
9
9
|
|
10
10
|
include Reek
|
11
11
|
|
12
|
-
describe CodeContext
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
describe CodeContext do
|
13
|
+
context 'to_s' do
|
14
|
+
it "reports the full context" do
|
15
|
+
element = StopContext.new
|
16
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
17
|
+
element = ClassContext.new(element, [0, :klass], s())
|
18
|
+
element = MethodContext.new(element, [0, :bad])
|
19
|
+
element = BlockContext.new(element, s(nil, nil))
|
20
|
+
element.to_s.should match(/bad/)
|
21
|
+
element.to_s.should match(/klass/)
|
22
|
+
element.to_s.should match(/mod/)
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
it 'reports the method name via if context' do
|
26
|
+
element1 = StopContext.new
|
27
|
+
element2 = MethodContext.new(element1, [0, :bad])
|
28
|
+
element3 = IfContext.new(element2, [0,1])
|
29
|
+
BlockContext.new(element3, s(nil, nil)).to_s.should match(/bad/)
|
30
|
+
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
it 'reports the method name via nested blocks' do
|
33
|
+
element1 = StopContext.new
|
34
|
+
element2 = MethodContext.new(element1, [0, :bad])
|
35
|
+
element3 = BlockContext.new(element2, s(nil, nil))
|
36
|
+
BlockContext.new(element3, s(nil, nil)).to_s.should match(/bad/)
|
37
|
+
end
|
37
38
|
end
|
38
|
-
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
40
|
+
context 'instance variables' do
|
41
|
+
it 'should pass instance variables down to the first class' do
|
42
|
+
element = StopContext.new
|
43
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
44
|
+
class_element = ClassContext.new(element, [0, :klass], s())
|
45
|
+
element = MethodContext.new(class_element, [0, :bad])
|
46
|
+
element = BlockContext.new(element, s(nil, nil))
|
47
|
+
element.record_instance_variable(:fred)
|
48
|
+
class_element.variable_names.size.should == 1
|
49
|
+
class_element.variable_names.should include(Name.new(:fred))
|
50
|
+
end
|
50
51
|
end
|
51
|
-
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
53
|
+
context 'generics' do
|
54
|
+
it 'should pass unknown method calls down the stack' do
|
55
|
+
stop = StopContext.new
|
56
|
+
def stop.bananas(arg1, arg2) arg1 + arg2 + 43 end
|
57
|
+
element = ModuleContext.new(stop, Name.new(:mod), s(:module, :mod, nil))
|
58
|
+
class_element = ClassContext.new(element, [0, :klass], s())
|
59
|
+
element = MethodContext.new(class_element, [0, :bad])
|
60
|
+
element = BlockContext.new(element, s(nil, nil))
|
61
|
+
element.bananas(17, -5).should == 55
|
62
|
+
end
|
62
63
|
end
|
63
|
-
end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
65
|
+
context 'name matching' do
|
66
|
+
it 'should recognise itself in a collection of names' do
|
67
|
+
element = StopContext.new
|
68
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
69
|
+
element.matches?(['banana', 'mod']).should == true
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should recognise itself in a collection of REs' do
|
73
|
+
element = StopContext.new
|
74
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
75
|
+
element.matches?([/banana/, /mod/]).should == true
|
76
|
+
end
|
71
77
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
78
|
+
it 'should recognise its fq name in a collection of names' do
|
79
|
+
element = StopContext.new
|
80
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
81
|
+
element = ClassContext.create(element, s(:class, :klass))
|
82
|
+
element.matches?(['banana', 'mod']).should == true
|
83
|
+
element.matches?(['banana', 'mod::klass']).should == true
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should recognise its fq name in a collection of names' do
|
87
|
+
element = StopContext.new
|
88
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
89
|
+
element = ClassContext.create(element, s(:class, :klass))
|
90
|
+
element.matches?([/banana/, /mod/]).should == true
|
91
|
+
element.matches?([/banana/, /mod::klass/]).should == true
|
92
|
+
end
|
76
93
|
end
|
77
94
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
95
|
+
context 'enumerating syntax elements' do
|
96
|
+
context 'in an empty module' do
|
97
|
+
before :each do
|
98
|
+
@module_name = 'Emptiness'
|
99
|
+
src = "module #{@module_name}; end"
|
100
|
+
ast = src.to_reek_source.syntax_tree
|
101
|
+
@ctx = CodeContext.new(nil, ast)
|
102
|
+
end
|
103
|
+
it 'yields no calls' do
|
104
|
+
@ctx.each(:call, []) {|exp| raise "#{exp} yielded by empty module!"}
|
105
|
+
end
|
106
|
+
it 'yields one module' do
|
107
|
+
mods = 0
|
108
|
+
@ctx.each(:module, []) {|exp| mods += 1}
|
109
|
+
mods.should == 1
|
110
|
+
end
|
111
|
+
it "yields the module's full AST" do
|
112
|
+
@ctx.each(:module, []) {|exp| exp[1].should == @module_name.to_sym}
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'with no block' do
|
116
|
+
it 'returns an empty array of ifs' do
|
117
|
+
@ctx.each(:if, []).should be_empty
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'with a nested element' do
|
123
|
+
before :each do
|
124
|
+
@module_name = 'Loneliness'
|
125
|
+
@method_name = 'calloo'
|
126
|
+
src = "module #{@module_name}; def #{@method_name}; puts('hello') end; end"
|
127
|
+
ast = src.to_reek_source.syntax_tree
|
128
|
+
@ctx = CodeContext.new(nil, ast)
|
129
|
+
end
|
130
|
+
it 'yields no ifs' do
|
131
|
+
@ctx.each(:if, []) {|exp| raise "#{exp} yielded by empty module!"}
|
132
|
+
end
|
133
|
+
it 'yields one module' do
|
134
|
+
@ctx.each(:module, []).length.should == 1
|
135
|
+
end
|
136
|
+
it "yields the module's full AST" do
|
137
|
+
@ctx.each(:module, []) {|exp| exp[1].should == @module_name.to_sym}
|
138
|
+
end
|
139
|
+
it 'yields one method' do
|
140
|
+
@ctx.each(:defn, []).length.should == 1
|
141
|
+
end
|
142
|
+
it "yields the method's full AST" do
|
143
|
+
@ctx.each(:defn, []) {|exp| exp[1].should == @method_name.to_sym}
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'pruning the traversal' do
|
147
|
+
it 'ignores the call inside the method' do
|
148
|
+
@ctx.each(:call, [:defn]).should be_empty
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'finds 3 ifs in a class' do
|
154
|
+
src = <<EOS
|
155
|
+
class Scrunch
|
156
|
+
def first
|
157
|
+
return @field == :sym ? 0 : 3;
|
158
|
+
end
|
159
|
+
def second
|
160
|
+
if @field == :sym
|
161
|
+
@other += " quarts"
|
162
|
+
end
|
84
163
|
end
|
164
|
+
def third
|
165
|
+
raise 'flu!' unless @field == :sym
|
166
|
+
end
|
167
|
+
end
|
168
|
+
EOS
|
85
169
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
element.matches?([/banana/, /mod/]).should == true
|
91
|
-
element.matches?([/banana/, /mod::klass/]).should == true
|
170
|
+
ast = src.to_reek_source.syntax_tree
|
171
|
+
ctx = CodeContext.new(nil, ast)
|
172
|
+
ctx.each(:if, []).length.should == 3
|
173
|
+
end
|
92
174
|
end
|
93
175
|
end
|
@@ -5,22 +5,22 @@ require 'reek/code_parser'
|
|
5
5
|
include Reek
|
6
6
|
|
7
7
|
describe CodeParser, "with no method definitions" do
|
8
|
-
it '
|
8
|
+
it 'reports no problems for empty source code' do
|
9
9
|
''.should_not reek
|
10
10
|
end
|
11
|
-
it '
|
11
|
+
it 'reports no problems for empty class' do
|
12
12
|
'class Fred; end'.should_not reek
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
describe CodeParser, 'with a global method definition' do
|
17
|
-
it '
|
17
|
+
it 'reports no problems for simple method' do
|
18
18
|
'def Outermost::fred() true; end'.should_not reek
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
describe CodeParser, 'when a yield is the receiver' do
|
23
|
-
it '
|
23
|
+
it 'reports no problems' do
|
24
24
|
source = 'def values(*args)
|
25
25
|
@to_sql += case
|
26
26
|
when block_given? then " #{yield.to_sql}"
|
@@ -39,105 +39,89 @@ describe CodeParser do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
describe CodeParser do
|
42
|
-
context 'with no
|
42
|
+
context 'with no attributes' do
|
43
43
|
it 'records nothing in the class' do
|
44
44
|
klass = ClassContext.from_s('class Fred; end')
|
45
|
-
klass.
|
45
|
+
klass.attributes.should be_empty
|
46
46
|
end
|
47
47
|
it 'records nothing in the module' do
|
48
48
|
ctx = ModuleContext.from_s('module Fred; end')
|
49
|
-
ctx.
|
49
|
+
ctx.attributes.should be_empty
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
context 'with one
|
54
|
-
shared_examples_for 'one
|
55
|
-
it 'records the
|
56
|
-
@ctx.
|
53
|
+
context 'with one attribute' do
|
54
|
+
shared_examples_for 'one attribute found' do
|
55
|
+
it 'records the attribute' do
|
56
|
+
@ctx.attributes.should include(Name.new(:property))
|
57
57
|
end
|
58
|
-
it 'records only that
|
59
|
-
@ctx.
|
58
|
+
it 'records only that attribute' do
|
59
|
+
@ctx.attributes.length.should == 1
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
context 'declared in a class' do
|
64
64
|
before :each do
|
65
|
-
@ctx = ClassContext.from_s('class Fred;
|
65
|
+
@ctx = ClassContext.from_s('class Fred; attr :property; end')
|
66
66
|
end
|
67
67
|
|
68
|
-
it_should_behave_like 'one
|
68
|
+
it_should_behave_like 'one attribute found'
|
69
69
|
end
|
70
70
|
|
71
|
-
context '
|
71
|
+
context 'reader in a class' do
|
72
72
|
before :each do
|
73
|
-
@ctx = ClassContext.from_s('class Fred;
|
73
|
+
@ctx = ClassContext.from_s('class Fred; attr_reader :property; end')
|
74
74
|
end
|
75
75
|
|
76
|
-
it_should_behave_like 'one
|
76
|
+
it_should_behave_like 'one attribute found'
|
77
77
|
end
|
78
78
|
|
79
|
-
context '
|
79
|
+
context 'writer in a class' do
|
80
80
|
before :each do
|
81
|
-
@ctx = ClassContext.from_s('class Fred;
|
81
|
+
@ctx = ClassContext.from_s('class Fred; attr_writer :property; end')
|
82
82
|
end
|
83
83
|
|
84
|
-
it_should_behave_like 'one
|
84
|
+
it_should_behave_like 'one attribute found'
|
85
85
|
end
|
86
86
|
|
87
|
-
context '
|
87
|
+
context 'accessor in a class' do
|
88
88
|
before :each do
|
89
|
-
@ctx = ClassContext.from_s('class Fred;
|
89
|
+
@ctx = ClassContext.from_s('class Fred; attr_accessor :property; end')
|
90
90
|
end
|
91
91
|
|
92
|
-
it_should_behave_like 'one
|
93
|
-
end
|
94
|
-
|
95
|
-
context 'used twice in a class' do
|
96
|
-
before :each do
|
97
|
-
@ctx = ClassContext.from_s('class Fred; def jeff() @@tools = {}; end; def jim() @@tools = {}; end; end')
|
98
|
-
end
|
99
|
-
|
100
|
-
it_should_behave_like 'one variable found'
|
92
|
+
it_should_behave_like 'one attribute found'
|
101
93
|
end
|
102
94
|
|
103
95
|
context 'declared in a module' do
|
104
96
|
before :each do
|
105
|
-
@ctx =
|
106
|
-
end
|
107
|
-
|
108
|
-
it_should_behave_like 'one variable found'
|
109
|
-
end
|
110
|
-
|
111
|
-
context 'used in a module' do
|
112
|
-
before :each do
|
113
|
-
@ctx = ClassContext.from_s('module Fred; def jim() @@tools = {}; end; end')
|
97
|
+
@ctx = ModuleContext.from_s('module Fred; attr :property; end')
|
114
98
|
end
|
115
99
|
|
116
|
-
it_should_behave_like 'one
|
100
|
+
it_should_behave_like 'one attribute found'
|
117
101
|
end
|
118
102
|
|
119
|
-
context '
|
103
|
+
context 'reader in a module' do
|
120
104
|
before :each do
|
121
|
-
@ctx =
|
105
|
+
@ctx = ModuleContext.from_s('module Fred; attr_reader :property; end')
|
122
106
|
end
|
123
107
|
|
124
|
-
it_should_behave_like 'one
|
108
|
+
it_should_behave_like 'one attribute found'
|
125
109
|
end
|
126
110
|
|
127
|
-
context '
|
111
|
+
context 'writer in a module' do
|
128
112
|
before :each do
|
129
|
-
@ctx =
|
113
|
+
@ctx = ModuleContext.from_s('module Fred; attr_writer :property; end')
|
130
114
|
end
|
131
115
|
|
132
|
-
it_should_behave_like 'one
|
116
|
+
it_should_behave_like 'one attribute found'
|
133
117
|
end
|
134
118
|
|
135
|
-
context '
|
119
|
+
context 'accessor in a module' do
|
136
120
|
before :each do
|
137
|
-
@ctx =
|
121
|
+
@ctx = ModuleContext.from_s('module Fred; attr_accessor :property; end')
|
138
122
|
end
|
139
123
|
|
140
|
-
it_should_behave_like 'one
|
124
|
+
it_should_behave_like 'one attribute found'
|
141
125
|
end
|
142
126
|
end
|
143
127
|
end
|
@@ -7,7 +7,7 @@ include Reek
|
|
7
7
|
|
8
8
|
describe MethodContext, 'matching' do
|
9
9
|
before :each do
|
10
|
-
@element = MethodContext.new(StopContext.new,
|
10
|
+
@element = MethodContext.new(StopContext.new, s(0, :mod), s(:module, :mod, nil))
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'should recognise itself in a collection of names' do
|
@@ -24,9 +24,9 @@ end
|
|
24
24
|
describe MethodContext, 'matching fq names' do
|
25
25
|
before :each do
|
26
26
|
element = StopContext.new
|
27
|
-
element = ModuleContext.new(element, Name.new(:mod))
|
28
|
-
element = ClassContext.new(element, Name.new(:klass))
|
29
|
-
@element = MethodContext.new(element,
|
27
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
28
|
+
element = ClassContext.new(element, Name.new(:klass), s())
|
29
|
+
@element = MethodContext.new(element, s(0, :meth))
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'should recognise itself in a collection of names' do
|