reek 1.3.4 → 1.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +5 -0
  3. data/README.md +27 -2
  4. data/features/command_line_interface/options.feature +5 -2
  5. data/features/command_line_interface/smells_count.feature +43 -45
  6. data/features/command_line_interface/stdin.feature +9 -15
  7. data/features/configuration_files/masking_smells.feature +9 -17
  8. data/features/rake_task/rake_task.feature +4 -4
  9. data/features/reports/reports.feature +80 -21
  10. data/features/samples.feature +8 -18
  11. data/features/step_definitions/reek_steps.rb +4 -0
  12. data/lib/reek/cli/application.rb +3 -6
  13. data/lib/reek/cli/command_line.rb +16 -6
  14. data/lib/reek/cli/reek_command.rb +4 -12
  15. data/lib/reek/cli/report.rb +61 -19
  16. data/lib/reek/config_file_exception.rb +5 -0
  17. data/lib/reek/smells/control_parameter.rb +45 -14
  18. data/lib/reek/smells/data_clump.rb +15 -39
  19. data/lib/reek/smells/duplicate_method_call.rb +76 -26
  20. data/lib/reek/source/config_file.rb +30 -19
  21. data/lib/reek/source/sexp_extensions.rb +139 -0
  22. data/lib/reek/source/sexp_node.rb +64 -0
  23. data/lib/reek/source/source_code.rb +1 -1
  24. data/lib/reek/source/tree_dresser.rb +30 -175
  25. data/lib/reek/spec/should_reek.rb +2 -5
  26. data/lib/reek/version.rb +1 -1
  27. data/reek.gemspec +1 -1
  28. data/spec/matchers/smell_of_matcher.rb +12 -15
  29. data/spec/reek/cli/report_spec.rb +10 -6
  30. data/spec/reek/core/code_parser_spec.rb +0 -6
  31. data/spec/reek/smells/control_parameter_spec.rb +195 -8
  32. data/spec/reek/smells/data_clump_spec.rb +28 -3
  33. data/spec/reek/smells/uncommunicative_method_name_spec.rb +7 -7
  34. data/spec/reek/source/sexp_extensions_spec.rb +290 -0
  35. data/spec/reek/source/sexp_node_spec.rb +28 -0
  36. data/spec/reek/source/source_code_spec.rb +59 -19
  37. data/spec/reek/source/tree_dresser_spec.rb +7 -314
  38. data/spec/reek/spec/should_reek_spec.rb +51 -64
  39. data/spec/samples/all_but_one_masked/dirty.rb +2 -2
  40. data/spec/samples/corrupt_config_file/dirty.rb +1 -0
  41. data/spec/samples/masked/dirty.rb +1 -1
  42. data/spec/samples/masked_by_dotfile/dirty.rb +2 -2
  43. data/spec/samples/no_config_file/dirty.rb +8 -0
  44. data/spec/samples/not_quite_masked/dirty.rb +0 -3
  45. data/spec/samples/three_smelly_files/dirty_one.rb +3 -0
  46. data/spec/samples/three_smelly_files/dirty_three.rb +5 -0
  47. data/spec/samples/three_smelly_files/dirty_two.rb +4 -0
  48. data/spec/spec_helper.rb +5 -0
  49. metadata +145 -137
  50. data/spec/reek/cli/reek_command_spec.rb +0 -46
@@ -8,11 +8,8 @@ module Reek
8
8
  # An rspec matcher that matches when the +actual+ has code smells.
9
9
  #
10
10
  class ShouldReek # :nodoc:
11
- def initialize(config_files = [])
12
- @config_files = config_files
13
- end
14
11
  def matches?(actual)
15
- @examiner = Examiner.new(actual, @config_files)
12
+ @examiner = Examiner.new(actual)
16
13
  @examiner.smelly?
17
14
  end
18
15
  def failure_message_for_should
@@ -28,7 +25,7 @@ module Reek
28
25
  # Returns +true+ if and only if the target source code contains smells.
29
26
  #
30
27
  def reek
31
- ShouldReek.new(Dir['config/*.reek'])
28
+ ShouldReek.new
32
29
  end
33
30
  end
34
31
  end
@@ -1,3 +1,3 @@
1
1
  module Reek
2
- VERSION = '1.3.4'
2
+ VERSION = '1.3.5'
3
3
  end
@@ -28,7 +28,7 @@ and reports any code smells it finds.
28
28
 
29
29
  s.add_runtime_dependency(%q<ruby_parser>, ["~> 3.2"])
30
30
  s.add_runtime_dependency(%q<sexp_processor>)
31
- s.add_runtime_dependency(%q<ruby2ruby>, ["~> 2.0.2"])
31
+ s.add_runtime_dependency(%q<ruby2ruby>, ["~> 2.0.7"])
32
32
 
33
33
  s.add_development_dependency(%q<bundler>, ["~> 1.1"])
34
34
  s.add_development_dependency(%q<rake>)
@@ -20,7 +20,6 @@ module SmellOfMatcher
20
20
  detect_smells
21
21
 
22
22
  return false if no_smells_found?
23
- return false if wrong_smell_class_found?
24
23
  return false if wrong_number_of_smells_found?
25
24
  return false if wrong_smell_details_found?
26
25
 
@@ -48,25 +47,23 @@ module SmellOfMatcher
48
47
  end
49
48
  end
50
49
 
51
- def wrong_smell_class_found?
52
- @actual_smells.each do |smell|
53
- if smell.smell_class != @klass::SMELL_CLASS ||
54
- smell.subclass != @klass::SMELL_SUBCLASS
55
- @reason = "Found #{smell.smell_class}/#{smell.subclass}"
56
- return true
57
- end
58
- end
59
- false
60
- end
61
-
62
50
  def wrong_number_of_smells_found?
63
- expected_number_of_smells = @expected_smells.empty? ? 1 : @expected_smells.length
64
- if expected_number_of_smells != @actual_smells.length
65
- @reason = "expected #{expected_number_of_smells} smell(s), found #{@actual_smells.length}"
51
+ return false if @expected_smells.empty?
52
+
53
+ if expected_number_of_smells != actual_number_of_smells
54
+ @reason = "expected #{expected_number_of_smells} smell(s), found #{actual_number_of_smells}"
66
55
  true
67
56
  end
68
57
  end
69
58
 
59
+ def expected_number_of_smells
60
+ @expected_number_of_smells ||= @expected_smells.length
61
+ end
62
+
63
+ def actual_number_of_smells
64
+ @actual_number_of_smells ||= @actual_smells.length
65
+ end
66
+
70
67
  def wrong_smell_details_found?
71
68
  @expected_smells.zip(@actual_smells).each do |expected_smell, actual_smell|
72
69
  expected_smell.each do |key, value|
@@ -9,7 +9,9 @@ describe QuietReport, " when empty" do
9
9
  context 'empty source' do
10
10
  it 'has an empty quiet_report' do
11
11
  examiner = Examiner.new('')
12
- QuietReport.new.report(examiner).should == ''
12
+ qr = QuietReport.new
13
+ qr.add_examiner(examiner)
14
+ qr.gather_results.should == []
13
15
  end
14
16
  end
15
17
 
@@ -17,14 +19,16 @@ describe QuietReport, " when empty" do
17
19
  before :each do
18
20
  examiner = Examiner.new('def simple(a) a[3] end')
19
21
  rpt = QuietReport.new
20
- @lines = rpt.report(examiner).split("\n")
22
+ @result = rpt.add_examiner(examiner).gather_results.first
21
23
  end
22
- it 'has a header and a list of smells' do
23
- @lines.should have_at_least(3).lines
24
+
25
+ it 'has a header' do
26
+ @result.should match('string -- 2 warnings')
24
27
  end
28
+
25
29
  it 'should mention every smell name' do
26
- @lines[0].should match('[Utility Function]')
27
- @lines[1].should match('[Feature Envy]')
30
+ @result.should match('[UncommunicativeParameterName]')
31
+ @result.should match('[Feature Envy]')
28
32
  end
29
33
  end
30
34
  end
@@ -34,9 +34,3 @@ EOS
34
34
  src.should_not reek
35
35
  end
36
36
  end
37
-
38
- describe CodeParser do
39
- it 'copes with a yield to an ivar' do
40
- 'def options() ozz.on { |@list| @prompt = !@list } end'.should_not reek
41
- end
42
- end
@@ -12,22 +12,209 @@ describe ControlParameter do
12
12
 
13
13
  it_should_behave_like 'SmellDetector'
14
14
 
15
- context 'conditional on a parameter' do
15
+ it 'should not report a ternary check on an ivar' do
16
+ src = 'def simple(arga) @ivar ? arga : 3 end'
17
+ src.should_not smell_of(ControlParameter)
18
+ end
19
+
20
+ it 'should not report a ternary check on a lvar' do
21
+ src = 'def simple(arga) lvar = 27; lvar ? arga : @ivar end'
22
+ src.should_not smell_of(ControlParameter)
23
+ end
24
+
25
+ it 'should not report when parameter is unused' do
26
+ src = 'def simple(arg) test = 1 end'
27
+ src.should_not smell_of(ControlParameter)
28
+ end
29
+
30
+ it 'should not report when parameter is used inside conditional' do
31
+ src = 'def simple(arg) if true then puts arg end end'
32
+ src.should_not smell_of(ControlParameter)
33
+ end
34
+
35
+ it 'should not report when used in first conditional but not second' do
36
+ src = <<EOS
37
+ def things(arg)
38
+ if arg
39
+ puts arg
40
+ end
41
+ if arg
42
+ puts 'a'
43
+ end
44
+ end
45
+ EOS
46
+ src.should_not smell_of(ControlParameter)
47
+ end
48
+
49
+ it 'should not report when used in second conditional but not first' do
50
+ src = <<EOS
51
+ def things(arg)
52
+ if arg
53
+ puts 'a'
54
+ end
55
+ if arg
56
+ puts arg
57
+ end
58
+ end
59
+ EOS
60
+ src.should_not smell_of(ControlParameter)
61
+ end
62
+
63
+ it 'should not report on complex non smelly method' do
64
+ src = <<EOS
65
+ def self.guess(arg)
66
+ case arg
67
+ when ""
68
+ t = self
69
+ when "a"
70
+ t = Switch::OptionalArgument
71
+ when "b"
72
+ t = Switch::PlacedArgument
73
+ else
74
+ t = Switch::RequiredArgument
75
+ end
76
+ self >= t or incompatible_argument_styles(arg, t)
77
+ t
78
+ end
79
+ EOS
80
+ src.should_not smell_of(ControlParameter)
81
+ end
82
+
83
+ context 'parameter only used to determine code path' do
16
84
  it 'should report a ternary check on a parameter' do
17
85
  src = 'def simple(arga) arga ? @ivar : 3 end'
18
86
  src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arga')
19
87
  end
20
- it 'should not report a ternary check on an ivar' do
21
- src = 'def simple(arga) @ivar ? arga : 3 end'
88
+
89
+ it 'should spot a couple inside a block' do
90
+ src = 'def blocks(arg) @text.map { |blk| arg ? blk : "#{blk}" } end'
91
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
92
+ end
93
+
94
+ it 'should report on an if statement modifier' do
95
+ src = 'def simple(arg) args = {}; args.merge(\'a\' => \'A\') if arg end'
96
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
97
+ end
98
+
99
+ it 'should report on an unless statement modifier' do
100
+ src = 'def simple(arg) args = {}; args.merge(\'a\' => \'A\') unless arg end'
101
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
102
+ end
103
+
104
+ it 'should report on if control expression' do
105
+ src = 'def simple(arg) args = {}; if arg then args.merge(\'a\' => \'A\') end end'
106
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
107
+ end
108
+
109
+ it 'should report on if control expression with &&' do
110
+ src = 'def simple(arg) if arg && true then puts "arg" end end'
111
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
112
+ end
113
+
114
+ it 'should report on if control expression with preceding &&' do
115
+ src = 'def simple(arg) if true && arg then puts "arg" end end'
116
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
117
+ end
118
+
119
+ it 'should report on if control expression with two && conditions' do
120
+ src = 'def simple(a) ag = {}; if a && true && true then puts "2" end end'
121
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'a')
122
+ end
123
+
124
+ it 'should report on if control expression with ||' do
125
+ src = 'def simple(arg) args = {}; if arg || true then puts "arg" end end'
126
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
127
+ end
128
+
129
+ it 'should report on if control expression with or' do
130
+ src = 'def simple(arg) args = {}; if arg or true then puts "arg" end end'
131
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
132
+ end
133
+
134
+ it 'should report on && notation' do
135
+ src = 'def simple(arg) args = {}; arg && args.merge(\'a\' => \'A\') end'
136
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
137
+ end
138
+
139
+ it 'should report on || notation' do
140
+ src = 'def simple(arg) args = {}; arg || args.merge(\'a\' => \'A\') end'
141
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
142
+ end
143
+
144
+ it 'should report on case statement' do
145
+ src = 'def simple(arg) case arg when nil; nil when false; nil else nil end end'
146
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
147
+ end
148
+
149
+ it 'should report when the argument is a hash on which we access a key' do
150
+ src = 'def simple(arg) if arg[\'a\'] then puts \'a\' else puts \'b\' end end'
151
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
152
+ end
153
+
154
+ it 'should report on nested if statements that are both control parameters' do
155
+ src = <<EOS
156
+ def nested(arg)
157
+ if arg
158
+ puts 'a'
159
+ puts 'b' if arg
160
+ end
161
+ end
162
+ EOS
163
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
164
+ end
165
+
166
+ it 'should report on nested if statements where the inner if is a control parameter' do
167
+ src = <<EOS
168
+ def nested(arg)
169
+ if true
170
+ puts 'a'
171
+ puts 'b' if arg
172
+ end
173
+ end
174
+ EOS
175
+ src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
176
+ end
177
+ end
178
+
179
+ context 'parameter used besides determining code path' do
180
+ it 'should not report on if conditional expression' do
181
+ src = 'def simple(arg) if arg then use(arg) else use(@other) end end'
22
182
  src.should_not smell_of(ControlParameter)
23
183
  end
24
- it 'should not report a ternary check on a lvar' do
25
- src = 'def simple(arga) lvar = 27; lvar ? arga : @ivar end'
184
+
185
+ it 'should not report on an if statement modifier' do
186
+ src = 'def simple(arg) args = {}; args.merge(\'a\' => arg) if arg end'
26
187
  src.should_not smell_of(ControlParameter)
27
188
  end
28
- it 'should spot a couple inside a block' do
29
- src = 'def blocks(arg) @text.map { |blk| arg ? blk : "#{blk}" } end'
30
- src.should smell_of(ControlParameter, ControlParameter::PARAMETER_KEY => 'arg')
189
+
190
+ it 'should not report on an unless statement modifier' do
191
+ src = 'def simple(arg) args = {}; args.merge(\'a\' => arg) unless arg end'
192
+ src.should_not smell_of(ControlParameter)
193
+ end
194
+
195
+ it 'should not report on if control expression' do
196
+ src = 'def simple(arg) args = {}; if arg then args.merge(\'a\' => arg) end end'
197
+ src.should_not smell_of(ControlParameter)
198
+ end
199
+
200
+ it 'should not report on if control expression with &&' do
201
+ src = 'def simple(arg) if arg && true then puts arg end end'
202
+ src.should_not smell_of(ControlParameter)
203
+ end
204
+
205
+ it 'should not report on && notation' do
206
+ src = 'def simple(arg) args = {}; arg && args.merge(\'a\' => arg) end'
207
+ src.should_not smell_of(ControlParameter)
208
+ end
209
+
210
+ it 'should not report on || notation' do
211
+ src = 'def simple(arg) args = {}; arg || args.merge(\'a\' => arg) end'
212
+ src.should_not smell_of(ControlParameter)
213
+ end
214
+
215
+ it 'should not report when parameter is used outside conditional' do
216
+ src = 'def simple(arg) puts arg; if arg then @a = 1 end end'
217
+ src.should_not smell_of(ControlParameter)
31
218
  end
32
219
  end
33
220
 
@@ -42,7 +42,7 @@ EOS
42
42
  it 'reports the number of occurrences' do
43
43
  @smells[0].smell[DataClump::OCCURRENCES_KEY].should == 3
44
44
  end
45
- it 'reports all parameters' do
45
+ it 'reports all methods' do
46
46
  @smells[0].smell[DataClump::METHODS_KEY].should == ['first', 'second', 'third']
47
47
  end
48
48
  it 'reports the declaration line numbers' do
@@ -56,7 +56,7 @@ EOS
56
56
  end
57
57
  end
58
58
 
59
- it 'reports 3 swapped pairs in a class' do
59
+ it 'reports 3 swapped pairs' do
60
60
  src = <<EOS
61
61
  #{@context} Scrunch
62
62
  def one(pa, pb) @field == :sym ? 0 : 3; end
@@ -68,7 +68,7 @@ EOS
68
68
  DataClump::PARAMETERS_KEY => ['pa', 'pb']})
69
69
  end
70
70
 
71
- it 'reports 3 identical parameter sets in a class' do
71
+ it 'reports 3 identical parameter sets' do
72
72
  src = <<EOS
73
73
  #{@context} Scrunch
74
74
  def first(pa, pb, pc) @field == :sym ? 0 : 3; end
@@ -115,6 +115,31 @@ end
115
115
  EOS
116
116
  src.should smell_of(DataClump, DataClump::OCCURRENCES_KEY => 5)
117
117
  end
118
+
119
+ it 'correctly checks number of occurences' do
120
+ src = <<-EOS
121
+ #{@context} Smelly
122
+ def fa(p1, p2, p3) end
123
+ def fb(p2, p3, p4) end
124
+ def fc(p3, p4, p5) end
125
+ def fd(p4, p5, p1) end
126
+ def fe(p5, p1, p2) end
127
+ end
128
+ EOS
129
+ src.should_not smell_of(DataClump)
130
+ end
131
+
132
+ it 'detects clumps smaller than the total number of arguments' do
133
+ src = <<-EOS
134
+ #{@context} Smelly
135
+ def fa(p1, p2, p3) end
136
+ def fb(p1, p3, p2) end
137
+ def fc(p4, p1, p2) end
138
+ end
139
+ EOS
140
+ src.should smell_of(DataClump,
141
+ { DataClump::PARAMETERS_KEY => %w(p1 p2) })
142
+ end
118
143
  end
119
144
 
120
145
  describe DataClump do
@@ -23,19 +23,19 @@ describe UncommunicativeMethodName do
23
23
 
24
24
  ['x', 'x2', 'method2'].each do |method_name|
25
25
  context 'with a bad name' do
26
- before :each do
27
- src = 'def x() end'
26
+ before do
27
+ src = "def #{method_name}; end"
28
28
  ctx = CodeContext.new(nil, src.to_reek_source.syntax_tree)
29
- @smells = @detector.examine_context(ctx)
30
- @warning = @smells[0]
29
+ smells = @detector.examine_context(ctx)
30
+ @warning = smells[0]
31
31
  end
32
32
 
33
33
  it_should_behave_like 'common fields set correctly'
34
34
 
35
35
  it 'reports the correct values' do
36
- @smells[0].smell[UncommunicativeMethodName::METHOD_NAME_KEY].should == 'x'
37
- @smells[0].lines.should == [1]
38
- @smells[0].context.should == 'x'
36
+ @warning.smell[UncommunicativeMethodName::METHOD_NAME_KEY].should == method_name
37
+ @warning.lines.should == [1]
38
+ @warning.context.should == method_name
39
39
  end
40
40
  end
41
41
  end
@@ -0,0 +1,290 @@
1
+ require 'spec_helper'
2
+ require 'reek/source/sexp_extensions'
3
+
4
+ include Reek::Source
5
+
6
+ describe SexpExtensions::DefnNode do
7
+ context 'with no parameters' do
8
+ before :each do
9
+ @node = s(:defn, :hello, s(:args))
10
+ @node.extend(SexpExtensions::DefnNode)
11
+ end
12
+ it 'has no arg names' do
13
+ @node.arg_names.should == s()
14
+ end
15
+ it 'has no parameter names' do
16
+ @node.parameter_names.should == s()
17
+ end
18
+ it 'includes outer scope in its full name' do
19
+ @node.full_name('Fred').should == 'Fred#hello'
20
+ end
21
+ it 'includes no marker in its full name with empty outer scope' do
22
+ @node.full_name('').should == 'hello'
23
+ end
24
+ end
25
+
26
+ context 'with 1 parameter' do
27
+ before :each do
28
+ @node = s(:defn, :hello, s(:args, :param))
29
+ @node.extend(SexpExtensions::DefnNode)
30
+ end
31
+ it 'has 1 arg name' do
32
+ @node.arg_names.should == s(:param)
33
+ end
34
+ it 'has 1 parameter name' do
35
+ @node.parameter_names.should == s(:param)
36
+ end
37
+ it 'includes outer scope in its full name' do
38
+ @node.full_name('Fred').should == 'Fred#hello'
39
+ end
40
+ it 'includes no marker in its full name with empty outer scope' do
41
+ @node.full_name('').should == 'hello'
42
+ end
43
+ end
44
+
45
+ context 'with a block' do
46
+ before :each do
47
+ @node = s(:defn, :hello, s(:args, :param, :"&blk"))
48
+ @node.extend(SexpExtensions::DefnNode)
49
+ end
50
+ it 'has 1 arg name' do
51
+ @node.arg_names.should == s(:param)
52
+ end
53
+ it 'has 2 parameter names' do
54
+ @node.parameter_names.should == s(:param, :"&blk")
55
+ end
56
+ it 'includes outer scope in its full name' do
57
+ @node.full_name('Fred').should == 'Fred#hello'
58
+ end
59
+ it 'includes no marker in its full name with empty outer scope' do
60
+ @node.full_name('').should == 'hello'
61
+ end
62
+ end
63
+
64
+ context 'with 1 defaulted parameter' do
65
+ before :each do
66
+ @node = s(:defn, :hello, s(:args, s(:lasgn, :param, s(:array))))
67
+ @node.extend(SexpExtensions::DefnNode)
68
+ end
69
+ it 'has 1 arg name' do
70
+ @node.arg_names.should == s(:param)
71
+ end
72
+ it 'has 1 parameter name' do
73
+ @node.parameter_names.should == s(:param)
74
+ end
75
+ it 'includes outer scope in its full name' do
76
+ @node.full_name('Fred').should == 'Fred#hello'
77
+ end
78
+ it 'includes no marker in its full name with empty outer scope' do
79
+ @node.full_name('').should == 'hello'
80
+ end
81
+ end
82
+
83
+ context 'with a body with 2 statements' do
84
+ before :each do
85
+ @node = s(:defn, :hello, s(:args), s(:first), s(:second))
86
+ @node.extend(SexpExtensions::DefnNode)
87
+ end
88
+
89
+ it 'has 2 body statements' do
90
+ @node.body.should == s(s(:first), s(:second))
91
+ end
92
+
93
+ it 'has a body extended with SexpNode' do
94
+ b = @node.body
95
+ (class << b; self; end).ancestors.first.should == SexpNode
96
+ end
97
+ end
98
+ end
99
+
100
+ describe SexpExtensions::DefsNode do
101
+ context 'with no parameters' do
102
+ before :each do
103
+ @node = s(:defs, :obj, :hello, s(:args))
104
+ @node.extend(SexpExtensions::DefsNode)
105
+ end
106
+ it 'has no arg names' do
107
+ @node.arg_names.should == s()
108
+ end
109
+ it 'has no parameter names' do
110
+ @node.parameter_names.should == s()
111
+ end
112
+ it 'includes outer scope in its full name' do
113
+ @node.full_name('Fred').should == 'Fred#obj.hello'
114
+ end
115
+ it 'includes no marker in its full name with empty outer scope' do
116
+ @node.full_name('').should == 'obj.hello'
117
+ end
118
+ end
119
+
120
+ context 'with 1 parameter' do
121
+ before :each do
122
+ @node = s(:defs, :obj, :hello, s(:args, :param))
123
+ @node.extend(SexpExtensions::DefsNode)
124
+ end
125
+ it 'has 1 arg name' do
126
+ @node.arg_names.should == s(:param)
127
+ end
128
+ it 'has 1 parameter name' do
129
+ @node.parameter_names.should == s(:param)
130
+ end
131
+ it 'includes outer scope in its full name' do
132
+ @node.full_name('Fred').should == 'Fred#obj.hello'
133
+ end
134
+ it 'includes no marker in its full name with empty outer scope' do
135
+ @node.full_name('').should == 'obj.hello'
136
+ end
137
+ end
138
+
139
+ context 'with a block' do
140
+ before :each do
141
+ @node = s(:defs, :obj, :hello, s(:args, :param, :"&blk"))
142
+ @node.extend(SexpExtensions::DefsNode)
143
+ end
144
+ it 'has 1 arg name' do
145
+ @node.arg_names.should == s(:param)
146
+ end
147
+ it 'has 2 parameter names' do
148
+ @node.parameter_names.should == s(:param, :"&blk")
149
+ end
150
+ it 'includes outer scope in its full name' do
151
+ @node.full_name('Fred').should == 'Fred#obj.hello'
152
+ end
153
+ it 'includes no marker in its full name with empty outer scope' do
154
+ @node.full_name('').should == 'obj.hello'
155
+ end
156
+ end
157
+
158
+ context 'with 1 defaulted parameter' do
159
+ before :each do
160
+ @node = s(:defs, :obj, :hello, s(:args, s(:lasgn, :param, s(:array))))
161
+ @node.extend(SexpExtensions::DefsNode)
162
+ end
163
+ it 'has 1 arg name' do
164
+ @node.arg_names.should == s(:param)
165
+ end
166
+ it 'has 1 parameter name' do
167
+ @node.parameter_names.should == s(:param)
168
+ end
169
+ it 'includes outer scope in its full name' do
170
+ @node.full_name('Fred').should == 'Fred#obj.hello'
171
+ end
172
+ it 'includes no marker in its full name with empty outer scope' do
173
+ @node.full_name('').should == 'obj.hello'
174
+ end
175
+ end
176
+
177
+ context 'with a body with 2 statements' do
178
+ before :each do
179
+ @node = s(:defs, s(:self), :hello, s(:args), s(:first), s(:second))
180
+ @node.extend(SexpExtensions::DefsNode)
181
+ end
182
+
183
+ it 'has 2 body statements' do
184
+ @node.body.should == s(s(:first), s(:second))
185
+ end
186
+
187
+ it 'has a body extended with SexpNode' do
188
+ b = @node.body
189
+ (class << b; self; end).ancestors.first.should == SexpNode
190
+ end
191
+ end
192
+ end
193
+
194
+ describe SexpExtensions::CallNode do
195
+ context 'with no parameters' do
196
+ before :each do
197
+ @node = s(:call, nil, :hello)
198
+ @node.extend(SexpExtensions::CallNode)
199
+ end
200
+ it 'has no parameter names' do
201
+ @node.parameter_names.should == nil
202
+ end
203
+ end
204
+
205
+ context 'with 1 literal parameter' do
206
+ before :each do
207
+ @node = s(:call, nil, :hello, s(:lit, :param))
208
+ @node.extend(SexpExtensions::CallNode)
209
+ end
210
+ it 'has 1 argument name' do
211
+ @node.arg_names.should == [:param]
212
+ end
213
+ end
214
+
215
+ context 'with 2 literal parameters' do
216
+ before :each do
217
+ @node = s(:call, nil, :hello, s(:lit, :x), s(:lit, :y))
218
+ @node.extend(SexpExtensions::CallNode)
219
+ end
220
+ it 'has 2 argument names' do
221
+ @node.arg_names.should == [:x, :y]
222
+ end
223
+ end
224
+ end
225
+
226
+ describe SexpExtensions::IterNode do
227
+ context 'with no parameters' do
228
+ before :each do
229
+ @node = s(:iter, s(), s(:args), s())
230
+ @node.extend(SexpExtensions::IterNode)
231
+ end
232
+ it 'has no parameter names' do
233
+ @node.parameter_names.should == []
234
+ end
235
+ end
236
+
237
+ context 'with 1 parameter' do
238
+ before :each do
239
+ @node = s(:iter, s(), s(:args, :param), s())
240
+ @node.extend(SexpExtensions::IterNode)
241
+ end
242
+ it 'has 1 parameter name' do
243
+ @node.parameter_names.should == s(:param)
244
+ end
245
+ end
246
+
247
+ context 'with 2 parameters' do
248
+ before :each do
249
+ @node = s(:iter, s(), s(:args, :x, :y), s())
250
+ @node.extend(SexpExtensions::IterNode)
251
+ end
252
+ it 'has 2 parameter names' do
253
+ @node.parameter_names.should == [:x, :y]
254
+ end
255
+ end
256
+ end
257
+
258
+ describe SexpExtensions::ModuleNode do
259
+ context 'with a simple name' do
260
+ subject do
261
+ mod = ast(:module, :Fred, nil)
262
+ mod
263
+ end
264
+ its(:name) { should == :Fred }
265
+ its(:simple_name) { should == :Fred }
266
+ its(:text_name) { should == 'Fred' }
267
+ it 'has a simple full_name' do
268
+ subject.full_name('').should == 'Fred'
269
+ end
270
+ it 'has a fq full_name' do
271
+ subject.full_name('Blimey::O::Reilly').should == 'Blimey::O::Reilly::Fred'
272
+ end
273
+ end
274
+
275
+ context 'with a scoped name' do
276
+ subject do
277
+ mod = ast(:module, s(:colon2, s(:const, :Foo), :Bar), nil)
278
+ mod
279
+ end
280
+ its(:name) { should == s(:colon2, s(:const, :Foo), :Bar) }
281
+ its(:simple_name) { should == :Bar }
282
+ its(:text_name) { should == 'Foo::Bar' }
283
+ it 'has a simple full_name' do
284
+ subject.full_name('').should == 'Foo::Bar'
285
+ end
286
+ it 'has a fq full_name' do
287
+ subject.full_name('Blimey::O::Reilly').should == 'Blimey::O::Reilly::Foo::Bar'
288
+ end
289
+ end
290
+ end