inversion 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data.tar.gz.sig +2 -0
  2. data/.gemtest +0 -0
  3. data/ChangeLog +836 -0
  4. data/History.md +4 -0
  5. data/Manifest.txt +74 -0
  6. data/README.rdoc +171 -0
  7. data/Rakefile +55 -0
  8. data/bin/inversion +276 -0
  9. data/lib/inversion.rb +98 -0
  10. data/lib/inversion/exceptions.rb +21 -0
  11. data/lib/inversion/mixins.rb +236 -0
  12. data/lib/inversion/monkeypatches.rb +20 -0
  13. data/lib/inversion/renderstate.rb +337 -0
  14. data/lib/inversion/sinatra.rb +35 -0
  15. data/lib/inversion/template.rb +250 -0
  16. data/lib/inversion/template/attrtag.rb +120 -0
  17. data/lib/inversion/template/calltag.rb +16 -0
  18. data/lib/inversion/template/codetag.rb +164 -0
  19. data/lib/inversion/template/commenttag.rb +54 -0
  20. data/lib/inversion/template/conditionaltag.rb +49 -0
  21. data/lib/inversion/template/configtag.rb +60 -0
  22. data/lib/inversion/template/containertag.rb +45 -0
  23. data/lib/inversion/template/elsetag.rb +62 -0
  24. data/lib/inversion/template/elsiftag.rb +49 -0
  25. data/lib/inversion/template/endtag.rb +55 -0
  26. data/lib/inversion/template/escapetag.rb +26 -0
  27. data/lib/inversion/template/fortag.rb +120 -0
  28. data/lib/inversion/template/iftag.rb +69 -0
  29. data/lib/inversion/template/importtag.rb +70 -0
  30. data/lib/inversion/template/includetag.rb +51 -0
  31. data/lib/inversion/template/node.rb +102 -0
  32. data/lib/inversion/template/parser.rb +297 -0
  33. data/lib/inversion/template/pptag.rb +28 -0
  34. data/lib/inversion/template/publishtag.rb +72 -0
  35. data/lib/inversion/template/subscribetag.rb +88 -0
  36. data/lib/inversion/template/tag.rb +150 -0
  37. data/lib/inversion/template/textnode.rb +43 -0
  38. data/lib/inversion/template/unlesstag.rb +60 -0
  39. data/lib/inversion/template/uriencodetag.rb +30 -0
  40. data/lib/inversion/template/yieldtag.rb +51 -0
  41. data/lib/inversion/tilt.rb +82 -0
  42. data/lib/inversion/utils.rb +235 -0
  43. data/spec/data/sinatra/hello.inversion +1 -0
  44. data/spec/inversion/mixins_spec.rb +177 -0
  45. data/spec/inversion/monkeypatches_spec.rb +35 -0
  46. data/spec/inversion/renderstate_spec.rb +291 -0
  47. data/spec/inversion/sinatra_spec.rb +59 -0
  48. data/spec/inversion/template/attrtag_spec.rb +216 -0
  49. data/spec/inversion/template/calltag_spec.rb +30 -0
  50. data/spec/inversion/template/codetag_spec.rb +51 -0
  51. data/spec/inversion/template/commenttag_spec.rb +84 -0
  52. data/spec/inversion/template/configtag_spec.rb +105 -0
  53. data/spec/inversion/template/containertag_spec.rb +54 -0
  54. data/spec/inversion/template/elsetag_spec.rb +105 -0
  55. data/spec/inversion/template/elsiftag_spec.rb +87 -0
  56. data/spec/inversion/template/endtag_spec.rb +78 -0
  57. data/spec/inversion/template/escapetag_spec.rb +59 -0
  58. data/spec/inversion/template/fortag_spec.rb +98 -0
  59. data/spec/inversion/template/iftag_spec.rb +241 -0
  60. data/spec/inversion/template/importtag_spec.rb +106 -0
  61. data/spec/inversion/template/includetag_spec.rb +108 -0
  62. data/spec/inversion/template/node_spec.rb +81 -0
  63. data/spec/inversion/template/parser_spec.rb +170 -0
  64. data/spec/inversion/template/pptag_spec.rb +51 -0
  65. data/spec/inversion/template/publishtag_spec.rb +69 -0
  66. data/spec/inversion/template/subscribetag_spec.rb +60 -0
  67. data/spec/inversion/template/tag_spec.rb +97 -0
  68. data/spec/inversion/template/textnode_spec.rb +86 -0
  69. data/spec/inversion/template/unlesstag_spec.rb +84 -0
  70. data/spec/inversion/template/uriencodetag_spec.rb +49 -0
  71. data/spec/inversion/template/yieldtag_spec.rb +54 -0
  72. data/spec/inversion/template_spec.rb +269 -0
  73. data/spec/inversion/tilt_spec.rb +47 -0
  74. data/spec/inversion_spec.rb +95 -0
  75. data/spec/lib/constants.rb +9 -0
  76. data/spec/lib/helpers.rb +160 -0
  77. metadata +316 -0
  78. metadata.gz.sig +0 -0
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env rspec -cfd -b
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ BEGIN {
5
+ require 'pathname'
6
+ basedir = Pathname( __FILE__ ).dirname.parent.parent.parent
7
+ libdir = basedir + 'lib'
8
+
9
+ $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
10
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
11
+ }
12
+
13
+ require 'rspec'
14
+ require 'spec/lib/helpers'
15
+ require 'inversion/template/fortag'
16
+ require 'inversion/template/textnode'
17
+ require 'inversion/template/endtag'
18
+ require 'inversion/renderstate'
19
+
20
+ describe Inversion::Template::EndTag do
21
+
22
+ before( :all ) do
23
+ setup_logging( :fatal )
24
+ end
25
+
26
+ before( :each ) do
27
+ @tag = Inversion::Template::EndTag.new
28
+ end
29
+
30
+ after( :all ) do
31
+ reset_logging()
32
+ end
33
+
34
+
35
+ it "doesn't render as anything" do
36
+ renderstate = Inversion::RenderState.new
37
+ @tag.render( renderstate ).should be_nil()
38
+ end
39
+
40
+ it "can render itself as a comment body that outputs what it closes" do
41
+ # <?for i IN foo ?>...<?end ?>
42
+ template = Inversion::Template.
43
+ new( "<?for foo in bar ?>Chunkers<?end ?>", :debugging_comments => true )
44
+ template.bar = [ :an_item ]
45
+ template.render.should =~ /<!-- End of For: { foo IN template.bar } -->/
46
+ end
47
+
48
+ it "closes the parse state's currently-open container node before it's appended" do
49
+ container = double( "container node", :tagname => 'for', :location => nil )
50
+ parserstate = mock( "parser state" )
51
+
52
+ parserstate.should_receive( :pop ).and_return( container )
53
+
54
+ @tag.before_appending( parserstate )
55
+ end
56
+
57
+ context "with a body" do
58
+
59
+ before( :each ) do
60
+ @tag = Inversion::Template::EndTag.new( 'if' )
61
+ end
62
+
63
+ it "raises an error on the addition of a mismatched end tag" do
64
+ state = Inversion::Template::Parser::State.new( :template )
65
+ opener = Inversion::Template::ForTag.new( 'foo in bar' )
66
+ state << opener
67
+
68
+ expect {
69
+ @tag.before_appending( state )
70
+ }.to raise_exception( Inversion::ParseError, /unbalanced/i )
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+
78
+
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env rspec -cfd -b
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ BEGIN {
5
+ require 'pathname'
6
+ basedir = Pathname( __FILE__ ).dirname.parent.parent.parent
7
+ libdir = basedir + 'lib'
8
+
9
+ $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
10
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
11
+ }
12
+
13
+ require 'rspec'
14
+ require 'spec/lib/helpers'
15
+ require 'inversion/template/escapetag'
16
+
17
+ describe Inversion::Template::EscapeTag do
18
+
19
+ before( :all ) do
20
+ setup_logging( :fatal )
21
+ end
22
+
23
+ after( :all ) do
24
+ reset_logging()
25
+ end
26
+
27
+ before( :each ) do
28
+ @attribute_object = mock( "template attribute" )
29
+ end
30
+
31
+
32
+ it "defaults to escaping as HTML" do
33
+ template = Inversion::Template.new( 'this is <?escape foo.bar ?>' )
34
+ template.foo = @attribute_object
35
+ @attribute_object.should_receive( :bar ).with( no_args() ).
36
+ and_return( "<the good, the bad, & the ugly>" )
37
+
38
+ template.render.should == "this is &lt;the good, the bad, &amp; the ugly&gt;"
39
+ end
40
+
41
+ it "raises an Inversion::OptionsError if the config specifies an unsupported format" do
42
+ template = Inversion::Template.new( 'this is <?escape foo.bar ?>',
43
+ :escape_format => :clowns, :on_render_error => :propagate )
44
+ template.foo = @attribute_object
45
+ @attribute_object.should_receive( :bar ).with( no_args() ).
46
+ and_return( "<the good, the bad, & the ugly>" )
47
+
48
+ expect { template.render }.to raise_error Inversion::OptionsError, /no such escape format/i
49
+ end
50
+
51
+ it "escapes as HTML if the format is set to :html" do
52
+ template = Inversion::Template.new( 'this is <?escape foo.bar ?>', :escape_format => :html )
53
+ template.foo = @attribute_object
54
+ @attribute_object.should_receive( :bar ).with( no_args() ).
55
+ and_return( "<the good, the bad, & the ugly>" )
56
+
57
+ template.render.should == "this is &lt;the good, the bad, &amp; the ugly&gt;"
58
+ end
59
+ end
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env rspec -cfd -b
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ BEGIN {
5
+ require 'pathname'
6
+ basedir = Pathname( __FILE__ ).dirname.parent.parent.parent
7
+ libdir = basedir + 'lib'
8
+
9
+ $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
10
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
11
+ }
12
+
13
+ require 'rspec'
14
+ require 'spec/lib/helpers'
15
+ require 'inversion/template/fortag'
16
+ require 'inversion/template/attrtag'
17
+ require 'inversion/template/textnode'
18
+ require 'inversion/renderstate'
19
+
20
+ describe Inversion::Template::ForTag do
21
+
22
+ before( :all ) do
23
+ setup_logging( :fatal )
24
+ end
25
+
26
+ after( :all ) do
27
+ reset_logging()
28
+ end
29
+
30
+
31
+ it "knows which identifiers should be added to the template" do
32
+ tag = Inversion::Template::ForTag.new( 'foo in bar' )
33
+ tag.identifiers.should == [ :bar ]
34
+ end
35
+
36
+ it "can iterate over single items of a collection attribute" do
37
+ tag = Inversion::Template::ForTag.new( 'foo in bar' )
38
+
39
+ tag.block_args.should == [ :foo ]
40
+ tag.enumerator.should == 'bar'
41
+ end
42
+
43
+ it "should render as nothing if the corresponding attribute in the template is unset" do
44
+ render_state = Inversion::RenderState.new( :bar => nil )
45
+
46
+ # <?for foo in bar ?>
47
+ tag = Inversion::Template::ForTag.new( 'foo in bar' )
48
+
49
+ # [<?attr foo?>]
50
+ tag << Inversion::Template::TextNode.new( '[' )
51
+ tag << Inversion::Template::AttrTag.new( 'foo' )
52
+ tag << Inversion::Template::TextNode.new( ']' )
53
+
54
+ tag.render( render_state ).should be_nil()
55
+ end
56
+
57
+ it "renders each of its subnodes for each iteration, replacing its " +
58
+ "block arguments with the yielded values" do
59
+ render_state = Inversion::RenderState.new( :bar => %w[monkey goat] )
60
+
61
+ # <?for foo in bar ?>
62
+ tag = Inversion::Template::ForTag.new( 'foo in bar' )
63
+
64
+ # [<?attr foo?>]
65
+ tag << Inversion::Template::TextNode.new( '[' )
66
+ tag << Inversion::Template::AttrTag.new( 'foo' )
67
+ tag << Inversion::Template::TextNode.new( ']' )
68
+
69
+ tag.render( render_state ).should == "[monkey][goat]"
70
+ end
71
+
72
+ it "raises a ParseError if a keyword other than 'in' is used" do
73
+ expect {
74
+ Inversion::Template::ForTag.new( 'foo begin bar' )
75
+ }.to raise_exception( Inversion::ParseError, /invalid/i )
76
+ end
77
+
78
+ context "multidimensional collections" do
79
+
80
+ it "can be expanded into multiple block arguments" do
81
+ tag = Inversion::Template::ForTag.new( 'splip, splorp in splap' )
82
+
83
+ tag.block_args.should == [ :splip, :splorp ]
84
+ tag.enumerator.should == 'splap'
85
+ end
86
+
87
+
88
+ it "can be expanded into multiple block arguments (sans spaces)" do
89
+ tag = Inversion::Template::ForTag.new( 'splip,splorp,sploop in splap' )
90
+
91
+ tag.block_args.should == [ :splip, :splorp, :sploop ]
92
+ tag.enumerator.should == 'splap'
93
+ end
94
+ end
95
+ end
96
+
97
+
98
+
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env rspec -cfd -b
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ BEGIN {
5
+ require 'pathname'
6
+ basedir = Pathname( __FILE__ ).dirname.parent.parent.parent
7
+ libdir = basedir + 'lib'
8
+
9
+ $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
10
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
11
+ }
12
+
13
+ require 'rspec'
14
+ require 'spec/lib/helpers'
15
+ require 'inversion/template/iftag'
16
+ require 'inversion/template/textnode'
17
+ require 'inversion/renderstate'
18
+
19
+ describe Inversion::Template::IfTag do
20
+
21
+ before( :all ) do
22
+ setup_logging( :fatal )
23
+ end
24
+
25
+ after( :all ) do
26
+ reset_logging()
27
+ end
28
+
29
+
30
+ it "renders its contents if its attribute is true" do
31
+ tag = Inversion::Template::IfTag.new( 'attribute' )
32
+ tag << Inversion::Template::TextNode.new( 'the body' )
33
+
34
+ renderstate = Inversion::RenderState.new( :attribute => true )
35
+ tag.render( renderstate )
36
+ renderstate.to_s.should == 'the body'
37
+ end
38
+
39
+
40
+ it "renders its contents if its methodchain is true" do
41
+ tag = Inversion::Template::IfTag.new( 'attribute.key?(:foo)' )
42
+ tag << Inversion::Template::TextNode.new( 'the body' )
43
+
44
+ renderstate = Inversion::RenderState.new( :attribute => {:foo => 1} )
45
+ tag.render( renderstate )
46
+ renderstate.to_s.should == 'the body'
47
+ end
48
+
49
+ it "doesn't render its contents if its attribute is false" do
50
+ tag = Inversion::Template::IfTag.new( 'attribute' )
51
+ tag << Inversion::Template::TextNode.new( 'the body' )
52
+
53
+ renderstate = Inversion::RenderState.new( :attribute => nil )
54
+ tag.render( renderstate )
55
+ renderstate.to_s.should == ''
56
+ end
57
+
58
+ it "doesn't render its contents if its methodchain is false" do
59
+ tag = Inversion::Template::IfTag.new( 'attribute.key?(:foo)' )
60
+ tag << Inversion::Template::TextNode.new( 'the body' )
61
+
62
+ renderstate = Inversion::RenderState.new( :attribute => {:bar => 1} )
63
+ tag.render( renderstate )
64
+ renderstate.to_s.should == ''
65
+ end
66
+
67
+ context "with a single 'else' clause" do
68
+
69
+ before( :each ) do
70
+ @tag = Inversion::Template::IfTag.new( 'attribute' )
71
+ @tag << Inversion::Template::TextNode.new( 'the body before else' )
72
+ @tag << Inversion::Template::ElseTag.new
73
+ @tag << Inversion::Template::TextNode.new( 'the body after else' )
74
+
75
+ end
76
+
77
+ it "only renders the first half of the contents if its attribute is true" do
78
+ renderstate = Inversion::RenderState.new( :attribute => true )
79
+ @tag.render( renderstate )
80
+ renderstate.to_s.should == 'the body before else'
81
+ end
82
+
83
+ it "only renders the second half of the contents if its attribute is true" do
84
+ renderstate = Inversion::RenderState.new( :attribute => false )
85
+ @tag.render( renderstate )
86
+ renderstate.to_s.should == 'the body after else'
87
+ end
88
+
89
+ end
90
+
91
+ context "with a single 'elsif' and an 'else' clause" do
92
+
93
+ before( :each ) do
94
+ @tag = Inversion::Template::IfTag.new( 'attribute' )
95
+ @tag << Inversion::Template::TextNode.new( 'the body before elsif' )
96
+ @tag << Inversion::Template::ElsifTag.new( 'elsifattribute' )
97
+ @tag << Inversion::Template::TextNode.new( 'the body after elsif' )
98
+ @tag << Inversion::Template::ElseTag.new
99
+ @tag << Inversion::Template::TextNode.new( 'the body after else' )
100
+ end
101
+
102
+ it "only renders the first third of the contents if its attribute is true" do
103
+ renderstate = Inversion::RenderState.new( :attribute => true )
104
+ @tag.render( renderstate )
105
+ renderstate.to_s.should == 'the body before elsif'
106
+ end
107
+
108
+ it "only renders the second third of the contents if the attribute is false and the " +
109
+ "elsif's attribute is true" do
110
+ renderstate = Inversion::RenderState.new( :attribute => false, :elsifattribute => true )
111
+ @tag.render( renderstate )
112
+ renderstate.to_s.should == 'the body after elsif'
113
+ end
114
+
115
+ it "only renders the last third of the contents if both the attribute and the elsif's " +
116
+ "attribute are false" do
117
+ renderstate = Inversion::RenderState.new( :attribute => false, :elsifattribute => false )
118
+ @tag.render( renderstate )
119
+ renderstate.to_s.should == 'the body after else'
120
+ end
121
+
122
+ end
123
+
124
+
125
+ context "with only a single 'elsif' clause" do
126
+
127
+ before( :each ) do
128
+ @tag = Inversion::Template::IfTag.new( 'attribute' )
129
+ @tag << Inversion::Template::TextNode.new( 'the body before elsif' )
130
+ @tag << Inversion::Template::ElsifTag.new( 'elsifattribute' )
131
+ @tag << Inversion::Template::TextNode.new( 'the body after elsif' )
132
+ end
133
+
134
+ it "only renders the first half of the contents if its attribute is true" do
135
+ renderstate = Inversion::RenderState.new( :attribute => true )
136
+ @tag.render( renderstate )
137
+ renderstate.to_s.should == 'the body before elsif'
138
+ end
139
+
140
+ it "only renders the second half of the contents if the attribute is false and the " +
141
+ "elsif's attribute is true" do
142
+ renderstate = Inversion::RenderState.new( :attribute => false, :elsifattribute => true )
143
+ @tag.render( renderstate )
144
+ renderstate.to_s.should == 'the body after elsif'
145
+ end
146
+
147
+ it "doesn't render anything if both the attribute and the elsif's attribute are false" do
148
+ renderstate = Inversion::RenderState.new( :attribute => false, :elsifattribute => false )
149
+ @tag.render( renderstate )
150
+ renderstate.to_s.should == ''
151
+ end
152
+
153
+ end
154
+
155
+
156
+ context "with two 'elsif' clauses" do
157
+
158
+ before( :each ) do
159
+ @tag = Inversion::Template::IfTag.new( 'attribute' )
160
+ @tag << Inversion::Template::TextNode.new( 'the body before elsif' )
161
+ @tag << Inversion::Template::ElsifTag.new( 'elsifattribute' )
162
+ @tag << Inversion::Template::TextNode.new( 'the body after elsif1' )
163
+ @tag << Inversion::Template::ElsifTag.new( 'elsifattribute2' )
164
+ @tag << Inversion::Template::TextNode.new( 'the body after elsif2' )
165
+ end
166
+
167
+ it "only renders the first third of the contents if its attribute is true" do
168
+ renderstate = Inversion::RenderState.new( :attribute => true )
169
+ @tag.render( renderstate )
170
+ renderstate.to_s.should == 'the body before elsif'
171
+ end
172
+
173
+ it "only renders the second third of the contents if the attribute is false and the " +
174
+ "first elsif's attribute is true" do
175
+ renderstate = Inversion::RenderState.new( :elsifattribute => true )
176
+ @tag.render( renderstate )
177
+ renderstate.to_s.should == 'the body after elsif1'
178
+ end
179
+
180
+ it "only renders the last third of the contents if both the attribute and the first elsif's " +
181
+ "attribute are false, but the second elsif's attribute is true" do
182
+ renderstate = Inversion::RenderState.new( :elsifattribute2 => true )
183
+ @tag.render( renderstate )
184
+ renderstate.to_s.should == 'the body after elsif2'
185
+ end
186
+
187
+ it "doesn't render anything if all three attributes are false" do
188
+ renderstate = Inversion::RenderState.new
189
+ @tag.render( renderstate )
190
+ renderstate.to_s.should == ''
191
+ end
192
+
193
+ end
194
+
195
+
196
+ context "with two 'elsif' clauses and an 'else' clause" do
197
+
198
+ before( :each ) do
199
+ @tag = Inversion::Template::IfTag.new( 'attribute' )
200
+ @tag << Inversion::Template::TextNode.new( 'the body before elsif' )
201
+ @tag << Inversion::Template::ElsifTag.new( 'elsifattribute' )
202
+ @tag << Inversion::Template::TextNode.new( 'the body after elsif1' )
203
+ @tag << Inversion::Template::ElsifTag.new( 'elsifattribute2' )
204
+ @tag << Inversion::Template::TextNode.new( 'the body after elsif2' )
205
+ @tag << Inversion::Template::ElseTag.new
206
+ @tag << Inversion::Template::TextNode.new( 'the body after else' )
207
+ end
208
+
209
+ it "only renders the first quarter of the contents if its attribute is true" do
210
+ renderstate = Inversion::RenderState.new( :attribute => true )
211
+ @tag.render( renderstate )
212
+ renderstate.to_s.should == 'the body before elsif'
213
+ end
214
+
215
+ it "only renders the second quarter of the contents if the attribute is false and the " +
216
+ "first elsif's attribute is true" do
217
+ renderstate = Inversion::RenderState.new( :elsifattribute => true )
218
+ @tag.render( renderstate )
219
+ renderstate.to_s.should == 'the body after elsif1'
220
+ end
221
+
222
+ it "only renders the third quarter of the contents if both the attribute and the first elsif's " +
223
+ "attribute are false, but the second elsif's attribute is true" do
224
+ renderstate = Inversion::RenderState.new( :elsifattribute2 => true )
225
+ @tag.render( renderstate )
226
+ renderstate.to_s.should == 'the body after elsif2'
227
+ end
228
+
229
+ it "renders the last quarter of the contents if all three attributes are false" do
230
+ renderstate = Inversion::RenderState.new
231
+ @tag.render( renderstate )
232
+ renderstate.to_s.should == 'the body after else'
233
+ end
234
+
235
+ end
236
+
237
+
238
+ end
239
+
240
+
241
+