inversion 0.0.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 (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
+