wool 0.5.1

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 (88) hide show
  1. data/.document +5 -0
  2. data/.gitignore +23 -0
  3. data/LICENSE +45 -0
  4. data/README.rdoc +17 -0
  5. data/Rakefile +77 -0
  6. data/TODO.md +17 -0
  7. data/VERSION +1 -0
  8. data/bin/wool +4 -0
  9. data/features/step_definitions/wool_steps.rb +39 -0
  10. data/features/support/env.rb +14 -0
  11. data/features/support/testdata/1_input +1 -0
  12. data/features/support/testdata/1_output +1 -0
  13. data/features/support/testdata/2_input +4 -0
  14. data/features/support/testdata/2_output +4 -0
  15. data/features/support/testdata/3_input +8 -0
  16. data/features/support/testdata/3_output +11 -0
  17. data/features/support/testdata/4_input +5 -0
  18. data/features/support/testdata/4_output +5 -0
  19. data/features/wool.feature +24 -0
  20. data/lib/wool.rb +40 -0
  21. data/lib/wool/advice/advice.rb +42 -0
  22. data/lib/wool/advice/comment_advice.rb +37 -0
  23. data/lib/wool/analysis/annotations.rb +34 -0
  24. data/lib/wool/analysis/annotations/next_annotation.rb +26 -0
  25. data/lib/wool/analysis/annotations/parent_annotation.rb +20 -0
  26. data/lib/wool/analysis/annotations/scope_annotation.rb +37 -0
  27. data/lib/wool/analysis/lexical_analysis.rb +165 -0
  28. data/lib/wool/analysis/protocol_registry.rb +32 -0
  29. data/lib/wool/analysis/protocols.rb +82 -0
  30. data/lib/wool/analysis/scope.rb +13 -0
  31. data/lib/wool/analysis/sexp_analysis.rb +98 -0
  32. data/lib/wool/analysis/signature.rb +16 -0
  33. data/lib/wool/analysis/symbol.rb +10 -0
  34. data/lib/wool/analysis/visitor.rb +36 -0
  35. data/lib/wool/analysis/wool_class.rb +47 -0
  36. data/lib/wool/rake/task.rb +42 -0
  37. data/lib/wool/runner.rb +156 -0
  38. data/lib/wool/scanner.rb +160 -0
  39. data/lib/wool/support/module_extensions.rb +84 -0
  40. data/lib/wool/third_party/trollop.rb +845 -0
  41. data/lib/wool/warning.rb +145 -0
  42. data/lib/wool/warnings/comment_spacing.rb +30 -0
  43. data/lib/wool/warnings/extra_blank_lines.rb +29 -0
  44. data/lib/wool/warnings/extra_whitespace.rb +15 -0
  45. data/lib/wool/warnings/line_length.rb +113 -0
  46. data/lib/wool/warnings/misaligned_unindentation.rb +16 -0
  47. data/lib/wool/warnings/operator_spacing.rb +63 -0
  48. data/lib/wool/warnings/rescue_exception.rb +41 -0
  49. data/lib/wool/warnings/semicolon.rb +24 -0
  50. data/lib/wool/warnings/useless_double_quotes.rb +37 -0
  51. data/spec/advice_specs/advice_spec.rb +69 -0
  52. data/spec/advice_specs/comment_advice_spec.rb +38 -0
  53. data/spec/advice_specs/spec_helper.rb +1 -0
  54. data/spec/analysis_specs/annotations_specs/next_prev_annotation_spec.rb +47 -0
  55. data/spec/analysis_specs/annotations_specs/parent_annotation_spec.rb +41 -0
  56. data/spec/analysis_specs/annotations_specs/spec_helper.rb +5 -0
  57. data/spec/analysis_specs/lexical_analysis_spec.rb +179 -0
  58. data/spec/analysis_specs/protocol_registry_spec.rb +58 -0
  59. data/spec/analysis_specs/protocols_spec.rb +49 -0
  60. data/spec/analysis_specs/scope_spec.rb +20 -0
  61. data/spec/analysis_specs/sexp_analysis_spec.rb +134 -0
  62. data/spec/analysis_specs/spec_helper.rb +2 -0
  63. data/spec/analysis_specs/visitor_spec.rb +53 -0
  64. data/spec/analysis_specs/wool_class_spec.rb +54 -0
  65. data/spec/rake_specs/spec_helper.rb +1 -0
  66. data/spec/rake_specs/task_spec.rb +67 -0
  67. data/spec/runner_spec.rb +171 -0
  68. data/spec/scanner_spec.rb +75 -0
  69. data/spec/spec.opts +1 -0
  70. data/spec/spec_helper.rb +93 -0
  71. data/spec/support_specs/module_extensions_spec.rb +91 -0
  72. data/spec/support_specs/spec_helper.rb +1 -0
  73. data/spec/warning_spec.rb +95 -0
  74. data/spec/warning_specs/comment_spacing_spec.rb +57 -0
  75. data/spec/warning_specs/extra_blank_lines_spec.rb +70 -0
  76. data/spec/warning_specs/extra_whitespace_spec.rb +33 -0
  77. data/spec/warning_specs/line_length_spec.rb +165 -0
  78. data/spec/warning_specs/misaligned_unindentation_spec.rb +35 -0
  79. data/spec/warning_specs/operator_spacing_spec.rb +101 -0
  80. data/spec/warning_specs/rescue_exception_spec.rb +105 -0
  81. data/spec/warning_specs/semicolon_spec.rb +58 -0
  82. data/spec/warning_specs/spec_helper.rb +1 -0
  83. data/spec/warning_specs/useless_double_quotes_spec.rb +62 -0
  84. data/spec/wool_spec.rb +8 -0
  85. data/status_reports/2010/12/2010-12-14.md +163 -0
  86. data/test/third_party_tests/test_trollop.rb +1181 -0
  87. data/wool.gemspec +173 -0
  88. metadata +235 -0
@@ -0,0 +1,58 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'ostruct'
3
+
4
+ describe ProtocolRegistry do
5
+ before(:each) do
6
+ @backup_all = ProtocolRegistry.protocols.dup
7
+ @backup_map = ProtocolRegistry.class_protocols.dup
8
+
9
+ ProtocolRegistry.protocols = []
10
+ ProtocolRegistry.class_protocols = {}
11
+ end
12
+
13
+ after(:each) do
14
+ ProtocolRegistry.protocols = @backup_all
15
+ ProtocolRegistry.class_protocols = @backup_map
16
+ end
17
+
18
+ context '#add_protocol' do
19
+ it 'adds a protocol to the main protocol list' do
20
+ x = Object.new
21
+ ProtocolRegistry.add_protocol x
22
+ ProtocolRegistry.protocols.should include(x)
23
+ end
24
+ end
25
+
26
+ context '#add_class_protocol' do
27
+ it 'adds a protocol to the main protocol list, and adds a shortcut in the class map' do
28
+ x = OpenStruct.new
29
+ x.class_used = OpenStruct.new
30
+ x.class_used.path = 'SuperPath'
31
+ ProtocolRegistry.add_class_protocol x
32
+ ProtocolRegistry.protocols.should include(x)
33
+ ProtocolRegistry.class_protocols['SuperPath'].should == x
34
+ end
35
+ end
36
+
37
+ context '#[]' do
38
+ it 'looks up quick queries by class path' do
39
+ x = OpenStruct.new
40
+ x.class_used = OpenStruct.new
41
+ x.class_used.path = 'SuperPath'
42
+ ProtocolRegistry.add_class_protocol x
43
+ ProtocolRegistry['SuperPath'].should == [x]
44
+ end
45
+ end
46
+
47
+ context '#query' do
48
+ context 'with :class_path specified' do
49
+ it 'finds the classes in the registry with the given path' do
50
+ x = OpenStruct.new
51
+ x.class_used = OpenStruct.new
52
+ x.class_used.path = 'SuperPath'
53
+ ProtocolRegistry.add_class_protocol x
54
+ ProtocolRegistry.query(:class_path => 'SuperPath').should == [x]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Protocols::ClassProtocol do
4
+ before do
5
+ a = WoolClass.new('A')
6
+ @a_proto = a.protocol
7
+ b = WoolClass.new('B') do |b|
8
+ b.add_method(WoolMethod.new('foo') do |method|
9
+ method.add_signature(@a_proto, [])
10
+ method.add_signature(b.protocol, [@a_proto])
11
+ end)
12
+ b.add_method(WoolMethod.new('bar') do |method|
13
+ method.add_signature(b.protocol, [@a_proto, b.protocol])
14
+ end)
15
+ end
16
+ @b_proto = b.protocol
17
+ end
18
+
19
+ context '#signatures' do
20
+ it 'returns an empty list when no methods are declared' do
21
+ @a_proto.signatures.should be_empty
22
+ end
23
+
24
+ it "gets its class's signatures when they are specified, which are its methods' signatures" do
25
+ @b_proto.signatures.should include(Signature.new('foo', @a_proto, []))
26
+ @b_proto.signatures.should include(Signature.new('foo', @b_proto, [@a_proto]))
27
+ @b_proto.signatures.should include(Signature.new('bar', @b_proto, [@a_proto, @b_proto]))
28
+ end
29
+ end
30
+ end
31
+
32
+ describe Protocols::UnionProtocol do
33
+ before do
34
+ @first, @second, @third = mock(:proto1), mock(:proto2), mock(:proto3)
35
+ @union = Protocols::UnionProtocol.new([@first, @second, @third])
36
+ end
37
+
38
+ context '#signatures' do
39
+ it "returns the union of all the protocols' signatures" do
40
+ sigs = mock(:sig1), mock(:sig2), mock(:sig3), mock(:sig4), mock(:sig5), mock(:sig6)
41
+ [@first, @second, @third].each_with_index do |proto, idx|
42
+ proto.should_receive(:signatures).and_return(sigs[idx * 2, 3])
43
+ end
44
+ final_sigs = @union.signatures
45
+ sigs.each {|sig| final_sigs.should include(sig)}
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Scope::GlobalScope do
4
+ it 'is a Scope object' do
5
+ Scope::GlobalScope.should be_a(Scope)
6
+ end
7
+
8
+ it 'has no parent' do
9
+ Scope::GlobalScope.parent.should be_nil
10
+ end
11
+
12
+ it 'has a self pointer that is an Object' do
13
+ # self_ptr is a Symbol
14
+ Scope::GlobalScope.self_ptr.class_used.path.should == 'Object'
15
+ end
16
+
17
+ it 'has Object in its constants table' do
18
+ Scope::GlobalScope.constants['Object'].should_not be_nil
19
+ end
20
+ end
@@ -0,0 +1,134 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe SexpAnalysis do
4
+ describe Sexp do
5
+ before do
6
+ @sexp = Sexp.new([:if, [:abc, 2, 3], [:def, 4, 5], nil])
7
+ end
8
+
9
+ describe '#type' do
10
+ it 'returns the type of the sexp' do
11
+ @sexp.type.should == :if
12
+ @sexp[1].type.should == :abc
13
+ @sexp[2].type.should == :def
14
+ end
15
+ end
16
+
17
+ describe '#children' do
18
+ it 'returns the children of a normal sexp' do
19
+ @sexp.children.should == [[:abc, 2, 3], [:def, 4, 5], nil]
20
+ @sexp[1].children.should == [2, 3]
21
+ @sexp[2].children.should == [4, 5]
22
+ end
23
+
24
+ it 'returns everything in an array if a whole-array sexp' do
25
+ Sexp.new([[:abc], 2, 3, [:def, 3, 4]]).children.should == [[:abc], 2, 3, [:def, 3, 4]]
26
+ end
27
+ end
28
+
29
+ context 'with annotations' do
30
+ describe '#initialize' do
31
+ before do
32
+ annotator_1, annotator_2 = Object.new, Object.new
33
+ def annotator_1.annotate!(node)
34
+ x = node[0]
35
+ def x.weird_thing!
36
+ "silly!"
37
+ end
38
+ def x.weird_thing_2!
39
+ "hello"
40
+ end
41
+ end
42
+ def annotator_2.annotate!(node)
43
+ x = node[0]
44
+ def x.weird_thing_2!
45
+ "world"
46
+ end
47
+ end
48
+ @old_annotations, Sexp.annotations = Sexp.annotations, [annotator_1, annotator_2]
49
+ end
50
+
51
+ after do
52
+ Sexp.annotations = @old_annotations
53
+ end
54
+
55
+ it 'runs the annotators in order' do
56
+ x = Object.new
57
+ def x.name; "x"; end
58
+ result = Sexp.new([x])
59
+ x.weird_thing!.should == "silly!"
60
+ x.weird_thing_2!.should == "world"
61
+ end
62
+ end
63
+
64
+ context '#eval_as_constant' do
65
+ it 'converts :var_ref constants' do
66
+ result = mock
67
+ scope = Scope.new(nil, nil, {'B' => result})
68
+ sexp = Sexp.new([:var_ref, [:@const, 'B', [1, 17]]])
69
+ sexp.eval_as_constant(scope).should == result
70
+ end
71
+
72
+ it 'converts :const_ref constants' do
73
+ result = mock
74
+ scope = Scope.new(nil, nil, {'C' => result})
75
+ sexp = Sexp.new([:const_ref, [:@const, 'C', [4, 17]]])
76
+ sexp.eval_as_constant(scope).should == result
77
+ end
78
+
79
+ it 'converts :top_const_ref constants' do
80
+ result = mock
81
+ Scope::GlobalScope.constants['__testing_ref__'] = result
82
+ sexp = Sexp.new([:top_const_ref, [:@const, '__testing_ref__', [4, 17]]])
83
+ sexp.eval_as_constant(nil).should == result
84
+ end
85
+
86
+ it 'converts :const_path_ref constants' do
87
+ input = [:const_path_ref, [:const_path_ref, [:const_path_ref, [:const_path_ref,
88
+ [:var_ref, [:@const, "B", [1, 7]]], [:@const, "M", [1, 10]]],
89
+ [:@const, "C", [1, 13]]], [:@const, "D", [1, 16]]], [:@const, "E", [1, 19]]]
90
+ global, b_sym, m_sym, c_sym, d_sym, e_sym = mock, mock, mock, mock, mock, mock
91
+ b_scope, m_scope, c_scope, d_scope = mock, mock, mock, mock
92
+ global.should_receive(:constants).and_return({'B' => b_sym})
93
+ b_sym.should_receive(:scope).and_return(b_scope)
94
+ b_scope.should_receive(:constants).and_return({'M' => m_sym})
95
+ m_sym.should_receive(:scope).and_return(m_scope)
96
+ m_scope.should_receive(:constants).and_return({'C' => c_sym})
97
+ c_sym.should_receive(:scope).and_return(c_scope)
98
+ c_scope.should_receive(:constants).and_return({'D' => d_sym})
99
+ d_sym.should_receive(:scope).and_return(d_scope)
100
+ d_scope.should_receive(:constants).and_return({'E' => e_sym})
101
+ sexp = Sexp.new(input)
102
+ sexp.eval_as_constant(global).should == e_sym
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ before do
109
+ @class = Class.new do
110
+ include SexpAnalysis
111
+ attr_accessor :body
112
+ def initialize(body)
113
+ self.body = body
114
+ end
115
+ end
116
+ end
117
+
118
+ context '#parse' do
119
+ it 'parses its body' do
120
+ @class.new('a').parse.should ==
121
+ [:program, [[:var_ref, [:@ident, "a", [1, 0]]]]]
122
+ end
123
+ end
124
+
125
+ context '#find_sexps' do
126
+ it 'searches its body' do
127
+ @class.new('a + b').find_sexps(:binary).should_not be_empty
128
+ end
129
+
130
+ it 'returns an empty array if no sexps are found' do
131
+ @class.new('a + b').find_sexps(:rescue).should be_empty
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,2 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ include SexpAnalysis
@@ -0,0 +1,53 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Visitor do
4
+ before do
5
+ @class = Class.new do
6
+ include Visitor
7
+ def visit_foo(node)
8
+ node.visited = node[1]
9
+ end
10
+ def visit_bar(node)
11
+ node.product = node[1] * node[2]
12
+ end
13
+ end
14
+ end
15
+
16
+ context '#visit' do
17
+ it 'runs the matching method when one is defined' do
18
+ a = Sexp.new([:foo, true])
19
+ a.should_receive(:visited=).with(true)
20
+ @class.new.visit(a)
21
+ b = Sexp.new([:bar, 3, 2])
22
+ b.should_receive(:product=).with(6)
23
+ @class.new.visit(b)
24
+ end
25
+
26
+ it 'automatically DFSs the tree to visit nodes when they are not handled' do
27
+ a = Sexp.new([:a, [:b, [:foo, false], 2, 3], [:c, [:bar, 19, 22], [:bar, 23, 2]]])
28
+ a[1][1].should_receive(:visited=).with(false)
29
+ a[2][1].should_receive(:product=).with(19 * 22)
30
+ a[2][2].should_receive(:product=).with(46)
31
+ @class.new.visit(a)
32
+ end
33
+
34
+ it 'calls #default_visit when it encounters an unknown AST node' do
35
+ klass = Class.new do
36
+ include Visitor
37
+ def default_visit(node)
38
+ node.count = (@count ||= 1)
39
+ @count += 1
40
+ node.children.select {|x| Sexp === x}.each {|x| visit(x)}
41
+ end
42
+ def visit_bar(node)
43
+ end
44
+ end
45
+ a = Sexp.new([:a, [:b, [:foo, false], 2, 3], [:c, [:bar, 19, 22], [:bar, 23, 2]]])
46
+ a.should_receive(:count=).with(1)
47
+ a[1].should_receive(:count=).with(2)
48
+ a[1][1].should_receive(:count=).with(3)
49
+ a[2].should_receive(:count=).with(4)
50
+ klass.new.visit(a)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'pp'
3
+
4
+ describe WoolClass do
5
+ before do
6
+ @a = WoolClass.new('A')
7
+ @b = WoolClass.new('B') do |b|
8
+ b.add_method(WoolMethod.new('foo') do |method|
9
+ method.add_signature(@a.protocol, [])
10
+ method.add_signature(b.protocol, [@a.protocol])
11
+ end)
12
+ b.add_method(WoolMethod.new('bar') do |method|
13
+ method.add_signature(b.protocol, [@a.protocol, b.protocol])
14
+ end)
15
+ end
16
+ end
17
+
18
+ context '#signatures' do
19
+ it 'returns an empty list when no methods are declared' do
20
+ @a.signatures.should be_empty
21
+ end
22
+
23
+ it "flattens all its method's signatures" do
24
+ @b.signatures.should include(Signature.new('foo', @a.protocol, []))
25
+ @b.signatures.should include(Signature.new('foo', @b.protocol, [@a.protocol]))
26
+ @b.signatures.should include(
27
+ Signature.new('bar', @b.protocol, [@a.protocol, @b.protocol]))
28
+ end
29
+ end
30
+ end
31
+
32
+ describe WoolMethod do
33
+ before do
34
+ @a = WoolClass.new('A')
35
+ @b = WoolClass.new('B')
36
+ @method = WoolMethod.new('foobar')
37
+ end
38
+
39
+ context '#add_signature' do
40
+ it 'creates signature objects and returns them in #signatures' do
41
+ @method.add_signature(@a.protocol, [])
42
+ @method.add_signature(@b.protocol, [@a.protocol, @a.protocol])
43
+ @method.signatures.should include(Signature.new('foobar', @a.protocol, []))
44
+ @method.signatures.should include(
45
+ Signature.new('foobar', @b.protocol, [@a.protocol, @a.protocol]))
46
+ end
47
+ end
48
+
49
+ context '#name' do
50
+ it 'returns the name' do
51
+ @method.name.should == 'foobar'
52
+ end
53
+ end
54
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
@@ -0,0 +1,67 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'tmpdir'
5
+ require 'wool/rake/task'
6
+
7
+ describe Rake::WoolTask do
8
+ context '#initialize' do
9
+ it 'yields to allow setting :libs and :extras' do
10
+ task_name = "temptask1-#{rand(65329)}".to_sym
11
+ task = Rake::WoolTask.new(task_name) do |wool|
12
+ wool.libs = "LOL"
13
+ wool.extras = "hai"
14
+ end
15
+ task.settings.libs.should == "LOL"
16
+ task.settings.extras.should == "hai"
17
+ end
18
+
19
+ it 'creates a Rake task with the given name that calls #run' do
20
+ task_name = "temptask2_#{rand(65000)}".to_sym
21
+ task = Rake::WoolTask.new(task_name)
22
+ Rake::Task[task_name].should_not be_nil
23
+ task.should_receive(:run)
24
+ Rake::Task[task_name].invoke
25
+ end
26
+
27
+ it 'allows you to specify which warnings to use' do
28
+ task_name = "temptask3_#{rand(65000)}".to_sym
29
+ task = Rake::WoolTask.new(task_name) do |wool|
30
+ wool.using << :one << :two
31
+ end
32
+ task.settings.using.should == [:one, :two]
33
+ end
34
+
35
+ it 'defaults to using all warnings' do
36
+ task_name = "temptask4_#{rand(65000)}".to_sym
37
+ task = Rake::WoolTask.new(task_name) do |wool|
38
+ end
39
+ task.settings.using.should == [:all]
40
+ end
41
+ end
42
+
43
+ context '#run' do
44
+ it 'searches the listed libraries for files' do
45
+ Dir.should_receive(:[]).with('lib/**/*.rb').and_return([])
46
+ Dir.should_receive(:[]).with('spec/**/*.rb').and_return([])
47
+ task = Rake::WoolTask.new("temptask3-#{rand(65329)}".to_sym) do |wool|
48
+ wool.libs << 'lib' << 'spec'
49
+ end
50
+ swizzling_io { task.run }
51
+ end
52
+
53
+ it 'scans the matching files' do
54
+ test_file = File.open(File.join(Dir.tmpdir, 'test_input'), 'w') do |fp|
55
+ fp << 'a + b '
56
+ end
57
+ Dir.should_receive(:[]).with('lib/**/*.rb').and_return([File.join(Dir.tmpdir, 'test_input')])
58
+ Dir.should_receive(:[]).with('spec/**/*.rb').and_return([])
59
+ task = Rake::WoolTask.new("temptask4-#{rand(65329)}".to_sym) do |wool|
60
+ wool.libs << 'lib' << 'spec'
61
+ end
62
+ printout = swizzling_io { task.run }
63
+ printout.should =~ /whitespace/
64
+ printout.should =~ /1 are fixable/
65
+ end
66
+ end
67
+ end