inversion 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data.tar.gz.sig +0 -0
  2. data/ChangeLog +157 -28
  3. data/History.rdoc +8 -0
  4. data/Manifest.txt +2 -3
  5. data/Rakefile +1 -3
  6. data/lib/inversion.rb +2 -2
  7. data/lib/inversion/exceptions.rb +1 -1
  8. data/lib/inversion/{template/parser.rb → parser.rb} +5 -5
  9. data/lib/inversion/renderstate.rb +55 -5
  10. data/lib/inversion/template.rb +5 -3
  11. data/lib/inversion/template/attrtag.rb +19 -12
  12. data/lib/inversion/template/begintag.rb +1 -2
  13. data/lib/inversion/template/configtag.rb +7 -1
  14. data/lib/inversion/template/containertag.rb +8 -3
  15. data/lib/inversion/template/elsetag.rb +16 -0
  16. data/lib/inversion/template/elsiftag.rb +16 -0
  17. data/lib/inversion/template/escapetag.rb +1 -1
  18. data/lib/inversion/template/fortag.rb +2 -5
  19. data/lib/inversion/template/iftag.rb +17 -35
  20. data/lib/inversion/template/importtag.rb +2 -1
  21. data/lib/inversion/template/includetag.rb +2 -0
  22. data/lib/inversion/template/node.rb +1 -1
  23. data/lib/inversion/template/tag.rb +5 -2
  24. data/lib/inversion/template/textnode.rb +1 -2
  25. data/lib/inversion/template/unlesstag.rb +16 -26
  26. data/lib/inversion/template/yieldtag.rb +3 -8
  27. data/spec/inversion/{template/parser_spec.rb → parser_spec.rb} +14 -14
  28. data/spec/inversion/renderstate_spec.rb +242 -165
  29. data/spec/inversion/template/attrtag_spec.rb +10 -18
  30. data/spec/inversion/template/begintag_spec.rb +13 -12
  31. data/spec/inversion/template/configtag_spec.rb +5 -7
  32. data/spec/inversion/template/elsetag_spec.rb +5 -5
  33. data/spec/inversion/template/elsiftag_spec.rb +5 -5
  34. data/spec/inversion/template/endtag_spec.rb +1 -1
  35. data/spec/inversion/template/fortag_spec.rb +22 -1
  36. data/spec/inversion/template/iftag_spec.rb +14 -0
  37. data/spec/inversion/template/rescuetag_spec.rb +4 -4
  38. data/spec/inversion/template/tag_spec.rb +6 -4
  39. data/spec/inversion/template/unlesstag_spec.rb +12 -6
  40. data/spec/inversion/template/yieldtag_spec.rb +2 -2
  41. metadata +31 -32
  42. metadata.gz.sig +0 -0
  43. data/lib/inversion/template/conditionaltag.rb +0 -49
@@ -3,7 +3,7 @@
3
3
 
4
4
  BEGIN {
5
5
  require 'pathname'
6
- basedir = Pathname( __FILE__ ).dirname.parent.parent.parent
6
+ basedir = Pathname( __FILE__ ).dirname.parent.parent
7
7
  libdir = basedir + 'lib'
8
8
 
9
9
  $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
@@ -12,9 +12,9 @@ BEGIN {
12
12
 
13
13
  require 'rspec'
14
14
  require 'spec/lib/helpers'
15
- require 'inversion/template/parser'
15
+ require 'inversion/parser'
16
16
 
17
- describe Inversion::Template::Parser do
17
+ describe Inversion::Parser do
18
18
 
19
19
  before( :all ) do
20
20
  setup_logging( :fatal )
@@ -26,7 +26,7 @@ describe Inversion::Template::Parser do
26
26
  end
27
27
 
28
28
  it "parses a string with no PIs as a single text node" do
29
- result = Inversion::Template::Parser.new( @template ).parse( "render unto Caesar" )
29
+ result = Inversion::Parser.new( @template ).parse( "render unto Caesar" )
30
30
 
31
31
  result.should have( 1 ).member
32
32
  result.first.should be_a( Inversion::Template::TextNode )
@@ -34,18 +34,18 @@ describe Inversion::Template::Parser do
34
34
  end
35
35
 
36
36
  it "parses an empty string as a empty tree" do
37
- result = Inversion::Template::Parser.new( @template ).parse( "" )
37
+ result = Inversion::Parser.new( @template ).parse( "" )
38
38
  result.should be_empty
39
39
  end
40
40
 
41
41
  it "raises a ParseError on mismatched tag brackets" do
42
42
  expect {
43
- Inversion::Template::Parser.new( @template ).parse( '[?foo bar ?>' )
43
+ Inversion::Parser.new( @template ).parse( '[?foo bar ?>' )
44
44
  }.to raise_error( Inversion::ParseError, /mismatched start and end brackets/i )
45
45
  end
46
46
 
47
47
  it "parses a string with a single 'attr' tag as a single AttrTag node" do
48
- result = Inversion::Template::Parser.new( @template ).parse( "<?attr foo ?>" )
48
+ result = Inversion::Parser.new( @template ).parse( "<?attr foo ?>" )
49
49
 
50
50
  result.should have( 1 ).member
51
51
  result.first.should be_a( Inversion::Template::AttrTag )
@@ -53,7 +53,7 @@ describe Inversion::Template::Parser do
53
53
  end
54
54
 
55
55
  it "parses a single 'attr' tag surrounded by plain text" do
56
- result = Inversion::Template::Parser.new( @template ).parse( "beginning<?attr foo ?>end" )
56
+ result = Inversion::Parser.new( @template ).parse( "beginning<?attr foo ?>end" )
57
57
 
58
58
  result.should have( 3 ).members
59
59
  result[0].should be_a( Inversion::Template::TextNode )
@@ -63,7 +63,7 @@ describe Inversion::Template::Parser do
63
63
  end
64
64
 
65
65
  it "ignores unknown tags by default" do
66
- result = Inversion::Template::Parser.new( @template ).parse( "Text <?hoooowhat ?>" )
66
+ result = Inversion::Parser.new( @template ).parse( "Text <?hoooowhat ?>" )
67
67
 
68
68
  result.should have( 2 ).members
69
69
  result[0].should be_a( Inversion::Template::TextNode )
@@ -73,28 +73,28 @@ describe Inversion::Template::Parser do
73
73
 
74
74
  it "can raise exceptions on unknown tags" do
75
75
  expect {
76
- Inversion::Template::Parser.new( @template, :ignore_unknown_tags => false ).
76
+ Inversion::Parser.new( @template, :ignore_unknown_tags => false ).
77
77
  parse( "Text <?hoooowhat ?>" )
78
78
  }.to raise_exception( Inversion::ParseError, /unknown tag/i )
79
79
  end
80
80
 
81
81
  it "can raise exceptions on unclosed (nested) tags" do
82
82
  expect {
83
- Inversion::Template::Parser.new( @template ).parse( "Text <?attr something <?attr something_else ?>" )
83
+ Inversion::Parser.new( @template ).parse( "Text <?attr something <?attr something_else ?>" )
84
84
  }.to raise_exception( Inversion::ParseError, /unclosed or nested tag/i )
85
85
  end
86
86
 
87
87
  it "can raise exceptions on unclosed (eof) tags" do
88
88
  expect {
89
- Inversion::Template::Parser.new( @template ).parse( "Text <?hoooowhat" )
89
+ Inversion::Parser.new( @template ).parse( "Text <?hoooowhat" )
90
90
  }.to raise_exception( Inversion::ParseError, /unclosed tag/i )
91
91
  end
92
92
 
93
93
 
94
- describe Inversion::Template::Parser::State do
94
+ describe Inversion::Parser::State do
95
95
 
96
96
  before( :each ) do
97
- @state = Inversion::Template::Parser::State.new( @template )
97
+ @state = Inversion::Parser::State.new( @template )
98
98
  end
99
99
 
100
100
  it "returns the node tree if it's well-formed" do
@@ -23,245 +23,287 @@ describe Inversion::RenderState do
23
23
  setup_logging( :fatal )
24
24
  end
25
25
 
26
- it "copies its initial attributes" do
27
- attributes = { :foot => "in mouth", :bear => "in woods" }
28
-
29
- state = Inversion::RenderState.new( attributes )
30
-
31
- state.attributes.should_not equal( attributes )
32
- state.attributes[:foot].should == "in mouth"
33
- state.attributes[:foot].should_not equal( attributes[:foot] )
34
- state.attributes[:bear].should == "in woods"
35
- state.attributes[:bear].should_not equal( attributes[:bear] )
26
+ after( :all ) do
27
+ reset_logging()
36
28
  end
37
29
 
38
30
 
39
- it "preserves tainted status when copying its attributes" do
40
- attributes = { :danger => "in pants" }
41
- attributes[:danger].taint
31
+ it "provides access to the block it was constructed with if there was one" do
32
+ block = Proc.new {}
33
+ state = Inversion::RenderState.new( &block )
34
+ state.block.should equal( block )
35
+ end
42
36
 
37
+ it "can evaluate code in the context of itself" do
38
+ attributes = { :foot => "in mouth", :bear => "in woods" }
43
39
  state = Inversion::RenderState.new( attributes )
44
-
45
- state.attributes[:danger].should be_tainted()
40
+ state.eval( "foot" ).should == 'in mouth'
46
41
  end
47
42
 
48
43
 
49
- it "preserves frozen status when copying its attributes" do
50
- attributes = { :danger => "in pants" }
51
- attributes[:danger].freeze
44
+ describe "overridable attributes" do
52
45
 
53
- state = Inversion::RenderState.new( attributes )
46
+ it "copies its initial attributes" do
47
+ attributes = { :foot => "in mouth", :bear => "in woods" }
54
48
 
55
- state.attributes[:danger].should be_frozen()
56
- end
49
+ state = Inversion::RenderState.new( attributes )
57
50
 
51
+ state.attributes.should_not equal( attributes )
52
+ state.attributes[:foot].should == "in mouth"
53
+ state.attributes[:foot].should_not equal( attributes[:foot] )
54
+ state.attributes[:bear].should == "in woods"
55
+ state.attributes[:bear].should_not equal( attributes[:bear] )
56
+ end
58
57
 
59
- it "preserves singleton methods on attribute objects when copying" do
60
- obj = Object.new
61
- def obj.foo; "foo!"; end
58
+ it "preserves tainted status when copying its attributes" do
59
+ attributes = { :danger => "in pants" }
60
+ attributes[:danger].taint
62
61
 
63
- state = Inversion::RenderState.new( :foo => obj )
62
+ state = Inversion::RenderState.new( attributes )
64
63
 
65
- state.attributes[:foo].singleton_methods.map( &:to_sym ).should include( :foo )
66
- end
64
+ state.attributes[:danger].should be_tainted()
65
+ end
67
66
 
67
+ it "preserves singleton methods on attribute objects when copying" do
68
+ obj = Object.new
69
+ def obj.foo; "foo!"; end
68
70
 
69
- it "provides access to the block it was constructed with if there was one" do
70
- block = Proc.new {}
71
- state = Inversion::RenderState.new( &block )
72
- state.block.should equal( block )
73
- end
71
+ state = Inversion::RenderState.new( :foo => obj )
74
72
 
73
+ state.attributes[:foo].singleton_methods.map( &:to_sym ).should include( :foo )
74
+ end
75
75
 
76
- it "can evaluate code in the context of itself" do
77
- attributes = { :foot => "in mouth", :bear => "in woods" }
76
+ it "preserves frozen status when copying its attributes" do
77
+ attributes = { :danger => "in pants" }
78
+ attributes[:danger].freeze
78
79
 
79
- state = Inversion::RenderState.new( attributes )
80
+ state = Inversion::RenderState.new( attributes )
80
81
 
81
- state.eval( "foot" ).should == 'in mouth'
82
- end
82
+ state.attributes[:danger].should be_frozen()
83
+ end
83
84
 
84
- it "can override its attributes for the duration of a block" do
85
- attributes = { :foot => "in mouth", :bear => "in woods" }
85
+ it "can override its attributes for the duration of a block" do
86
+ attributes = { :foot => "in mouth", :bear => "in woods" }
86
87
 
87
- state = Inversion::RenderState.new( attributes )
88
+ state = Inversion::RenderState.new( attributes )
88
89
 
89
- state.with_attributes( :foot => 'ball' ) do
90
- state.foot.should == 'ball'
91
- state.bear.should == 'in woods'
92
- end
90
+ state.with_attributes( :foot => 'ball' ) do
91
+ state.foot.should == 'ball'
92
+ state.bear.should == 'in woods'
93
+ end
93
94
 
94
- state.attributes[:foot].should == 'in mouth'
95
- end
95
+ state.attributes[:foot].should == 'in mouth'
96
+ end
96
97
 
97
98
 
98
- it "restores the original attributes if the block raises an exception" do
99
- attributes = { :foot => "in mouth", :bear => "in woods" }
99
+ it "restores the original attributes if the block raises an exception" do
100
+ attributes = { :foot => "in mouth", :bear => "in woods" }
100
101
 
101
- state = Inversion::RenderState.new( attributes )
102
+ state = Inversion::RenderState.new( attributes )
102
103
 
103
- expect {
104
- state.with_attributes( {} ) do
105
- raise "Charlie dooo!"
106
- end
107
- }.to raise_error()
104
+ expect {
105
+ state.with_attributes( {} ) do
106
+ raise "Charlie dooo!"
107
+ end
108
+ }.to raise_error()
108
109
 
109
- state.attributes[:foot].should == 'in mouth'
110
- end
110
+ state.attributes[:foot].should == 'in mouth'
111
+ end
111
112
 
112
113
 
113
- it "raises an error if #with_attributes is called without a block" do
114
- expect {
115
- Inversion::RenderState.new.with_attributes( {} )
116
- }.to raise_error( LocalJumpError, /no block/i )
117
- end
114
+ it "raises an error if #with_attributes is called without a block" do
115
+ expect {
116
+ Inversion::RenderState.new.with_attributes( {} )
117
+ }.to raise_error( LocalJumpError, /no block/i )
118
+ end
118
119
 
120
+ it "provides accessor methods for its attributes" do
121
+ state = Inversion::RenderState.new( :bar => :the_attribute_value )
122
+ state.bar.should == :the_attribute_value
123
+ end
119
124
 
120
- it "can override the render destination for the duration of a block" do
121
- state = Inversion::RenderState.new
125
+ it "doesn't error if an accessor for a non-existant attribute is called" do
126
+ state = Inversion::RenderState.new( :bar => :the_attribute_value )
127
+ state.foo.should be_nil()
128
+ end
122
129
 
123
- original_dest = state.destination
124
- newdest = []
125
- node = Inversion::Template::TextNode.new( "New!" )
126
- rval = state.with_destination( newdest ) do
127
- state << node
130
+ it "can be merged with another RenderState" do
131
+ state = Inversion::RenderState.new(
132
+ {:bar => :the_bar_value},
133
+ {:debugging_comments => false} )
134
+ anotherstate = Inversion::RenderState.new(
135
+ {:foo => :the_foo_value},
136
+ {:debugging_comments => true, :on_render_error => :propagate} )
137
+
138
+ thirdstate = state.merge( anotherstate )
139
+
140
+ thirdstate.attributes.should == {
141
+ :bar => :the_bar_value,
142
+ :foo => :the_foo_value
143
+ }
144
+ thirdstate.options.should include(
145
+ :debugging_comments => true,
146
+ :on_render_error => :propagate
147
+ )
128
148
  end
129
- rval.should equal( newdest )
130
149
 
131
- newdest.should have( 1 ).member
132
- newdest.should include( 'New!' )
133
- state.destination.should equal( original_dest )
134
150
  end
135
151
 
136
- it "restores the original destination if the block raises an exception" do
137
- state = Inversion::RenderState.new
138
152
 
139
- original_dest = state.destination
153
+ describe "context-aware tag state" do
140
154
 
141
- expect {
142
- state.with_destination( [] ) do
143
- raise "New!"
144
- end
145
- }.to raise_error()
155
+ before( :each ) do
156
+ @renderstate = Inversion::RenderState.new
157
+ end
146
158
 
147
- state.destination.should equal( original_dest )
148
- end
159
+ it "provides a mechanism for storing tag state for the current render" do
160
+ @renderstate.tag_data.should be_a( Hash )
161
+ end
149
162
 
150
- it "raises an error if #with_destination is called without a block" do
151
- expect {
152
- Inversion::RenderState.new.with_destination( [] )
153
- }.to raise_error( LocalJumpError, /no block/i )
154
- end
163
+ it "can override tag state for the duration of a block" do
164
+ @renderstate.tag_data[ :montana ] = 'excellent fishing'
165
+ @renderstate.tag_data[ :colorado ] = 'fine fishing'
155
166
 
156
- it "adds a debugging comment when appending a node if debugging comments are enabled" do
157
- node = Inversion::Template::AttrTag.new( 'foo' )
158
- state = Inversion::RenderState.new( {}, :debugging_comments => true )
167
+ @renderstate.with_tag_data( :alaska => 'good fishing' ) do
168
+ @renderstate.tag_data[:alaska].should == 'good fishing'
169
+ @renderstate.tag_data[:alaska] = 'blueberry bear poop'
170
+ @renderstate.tag_data[:colorado] = 'Boulder has hippies'
171
+ end
159
172
 
160
- state << node
173
+ @renderstate.tag_data.should_not have_key( :alaska )
174
+ @renderstate.tag_data[:montana].should == 'excellent fishing'
175
+ @renderstate.tag_data[:colorado].should == 'fine fishing'
176
+ end
161
177
 
162
- state.to_s.should == '<!-- Attr: { template.foo } -->'
163
178
  end
164
179
 
165
- it "doesn't add a debugging comment when appending a node if debugging comments are disabled" do
166
- node = Inversion::Template::AttrTag.new( 'foo' )
167
- state = Inversion::RenderState.new( {}, :debugging_comments => false )
180
+ describe "render destinations" do
168
181
 
169
- state << node
182
+ it "can override the render destination for the duration of a block" do
183
+ state = Inversion::RenderState.new
170
184
 
171
- state.to_s.should == ''
172
- end
185
+ original_dest = state.destination
186
+ newdest = []
187
+ node = Inversion::Template::TextNode.new( "New!" )
188
+ rval = state.with_destination( newdest ) do
189
+ state << node
190
+ end
191
+ rval.should equal( newdest )
173
192
 
174
- it "ignores errors while rendering appended nodes in 'ignore' mode" do
175
- node = Inversion::Template::AttrTag.new( 'boom.klang' )
176
- state = Inversion::RenderState.new( {}, :on_render_error => :ignore )
193
+ newdest.should have( 1 ).member
194
+ newdest.should include( 'New!' )
195
+ state.destination.should equal( original_dest )
196
+ end
177
197
 
178
- state << node
198
+ it "restores the original destination if the block raises an exception" do
199
+ state = Inversion::RenderState.new
179
200
 
180
- state.to_s.should == ''
181
- end
201
+ original_dest = state.destination
182
202
 
183
- it "adds a comment for errors while rendering appended nodes in 'comment' mode" do
184
- node = Inversion::Template::AttrTag.new( 'boom.klang' )
185
- state = Inversion::RenderState.new( {}, :on_render_error => :comment )
203
+ expect {
204
+ state.with_destination( [] ) do
205
+ raise "New!"
206
+ end
207
+ }.to raise_error()
186
208
 
187
- state << node
209
+ state.destination.should equal( original_dest )
210
+ end
211
+
212
+ it "raises an error if #with_destination is called without a block" do
213
+ expect {
214
+ Inversion::RenderState.new.with_destination( [] )
215
+ }.to raise_error( LocalJumpError, /no block/i )
216
+ end
188
217
 
189
- state.to_s.should == "<!-- NoMethodError: undefined method `klang' for nil:NilClass -->"
190
218
  end
191
219
 
192
- it "re-raises errors while rendering appended nodes in 'propagate' mode" do
193
- node = Inversion::Template::AttrTag.new( 'boom.klang' )
194
- state = Inversion::RenderState.new( {}, :on_render_error => :propagate )
195
220
 
196
- expect {
221
+ describe "debugging comments" do
222
+
223
+ it "adds a debugging comment when appending a node if debugging comments are enabled" do
224
+ node = Inversion::Template::AttrTag.new( 'foo' )
225
+ state = Inversion::RenderState.new( {}, :debugging_comments => true )
226
+
197
227
  state << node
198
- }.to raise_error( NoMethodError, /undefined method/ )
199
- end
200
228
 
201
- it "calls the provided handler if an exception is raised while the error handler has been " +
202
- "overridden" do
203
- handler = Proc.new do |state, node, err|
204
- "Yum, I eat %p from %p! Tasting good!" % [err.class, node.class]
229
+ state.to_s.should == '<!-- Attr: { template.foo } -->'
205
230
  end
206
- node = Inversion::Template::AttrTag.new( 'boom.klang' )
207
- state = Inversion::RenderState.new( {}, :on_render_error => :propagate )
208
- defhandler = state.errhandler
209
231
 
210
- expect {
211
- state.with_error_handler( handler ) do
212
- state << node
213
- end
214
- }.to_not raise_error()
232
+ it "doesn't add a debugging comment when appending a node if debugging comments are disabled" do
233
+ node = Inversion::Template::AttrTag.new( 'foo' )
234
+ state = Inversion::RenderState.new( {}, :debugging_comments => false )
215
235
 
216
- state.to_s.should =~ /yum, i eat nomethoderror/i
217
- state.errhandler.should equal( defhandler )
218
- end
236
+ state << node
237
+
238
+ state.to_s.should == ''
239
+ end
219
240
 
220
- it "raises an exception if the error handler is set to something that doesn't respond to #call" do
221
- state = Inversion::RenderState.new
222
- expect {
223
- state.with_error_handler( :foo )
224
- }.to raise_error( ArgumentError, /doesn't respond_to #call/i )
225
241
  end
226
242
 
227
- it "re-raises errors while rendering appended nodes in 'propagate' mode" do
228
- node = Inversion::Template::AttrTag.new( 'boom.klang' )
229
- state = Inversion::RenderState.new( {}, :on_render_error => :propagate )
230
243
 
231
- expect {
244
+ describe "error-handling" do
245
+
246
+ it "ignores errors while rendering appended nodes in 'ignore' mode" do
247
+ node = Inversion::Template::AttrTag.new( 'boom.klang' )
248
+ state = Inversion::RenderState.new( {}, :on_render_error => :ignore )
249
+
232
250
  state << node
233
- }.to raise_error( NoMethodError, /undefined method/ )
234
- end
235
251
 
236
- it "provides accessor methods for its attributes" do
237
- state = Inversion::RenderState.new( :bar => :the_attribute_value )
238
- state.bar.should == :the_attribute_value
239
- end
252
+ state.to_s.should == ''
253
+ end
240
254
 
241
- it "doesn't error if an accessor for a non-existant attribute is called" do
242
- state = Inversion::RenderState.new( :bar => :the_attribute_value )
243
- state.foo.should be_nil()
244
- end
255
+ it "adds a comment for errors while rendering appended nodes in 'comment' mode" do
256
+ node = Inversion::Template::AttrTag.new( 'boom.klang' )
257
+ state = Inversion::RenderState.new( {}, :on_render_error => :comment )
258
+
259
+ state << node
245
260
 
261
+ state.to_s.should == "<!-- NoMethodError: undefined method `klang' for nil:NilClass -->"
262
+ end
246
263
 
247
- it "can be merged with another RenderState" do
248
- state = Inversion::RenderState.new(
249
- {:bar => :the_bar_value},
250
- {:debugging_comments => false} )
251
- anotherstate = Inversion::RenderState.new(
252
- {:foo => :the_foo_value},
253
- {:debugging_comments => true, :on_render_error => :propagate} )
264
+ it "re-raises errors while rendering appended nodes in 'propagate' mode" do
265
+ node = Inversion::Template::AttrTag.new( 'boom.klang' )
266
+ state = Inversion::RenderState.new( {}, :on_render_error => :propagate )
254
267
 
255
- thirdstate = state.merge( anotherstate )
268
+ expect {
269
+ state << node
270
+ }.to raise_error( NoMethodError, /undefined method/ )
271
+ end
256
272
 
257
- thirdstate.attributes.should == {
258
- :bar => :the_bar_value,
259
- :foo => :the_foo_value
260
- }
261
- thirdstate.options.should include(
262
- :debugging_comments => true,
263
- :on_render_error => :propagate
264
- )
273
+ it "calls the provided handler if an exception is raised while the error handler has been " +
274
+ "overridden" do
275
+ handler = Proc.new do |state, node, err|
276
+ "Yum, I eat %p from %p! Tasting good!" % [err.class, node.class]
277
+ end
278
+ node = Inversion::Template::AttrTag.new( 'boom.klang' )
279
+ state = Inversion::RenderState.new( {}, :on_render_error => :propagate )
280
+ defhandler = state.errhandler
281
+
282
+ expect {
283
+ state.with_error_handler( handler ) do
284
+ state << node
285
+ end
286
+ }.to_not raise_error()
287
+
288
+ state.to_s.should =~ /yum, i eat nomethoderror/i
289
+ state.errhandler.should equal( defhandler )
290
+ end
291
+
292
+ it "raises an exception if the error handler is set to something that doesn't respond to #call" do
293
+ state = Inversion::RenderState.new
294
+ expect {
295
+ state.with_error_handler( :foo )
296
+ }.to raise_error( ArgumentError, /doesn't respond_to #call/i )
297
+ end
298
+
299
+ it "re-raises errors while rendering appended nodes in 'propagate' mode" do
300
+ node = Inversion::Template::AttrTag.new( 'boom.klang' )
301
+ state = Inversion::RenderState.new( {}, :on_render_error => :propagate )
302
+
303
+ expect {
304
+ state << node
305
+ }.to raise_error( NoMethodError, /undefined method/ )
306
+ end
265
307
 
266
308
  end
267
309
 
@@ -287,5 +329,40 @@ describe Inversion::RenderState do
287
329
 
288
330
  end
289
331
 
332
+
333
+ describe "conditional rendering" do
334
+
335
+ before( :each ) do
336
+ @state = Inversion::RenderState.new
337
+ end
338
+
339
+ it "allows rendering to be explicitly enabled and disabled" do
340
+ @state.rendering_enabled?.should be_true()
341
+ @state.disable_rendering
342
+ @state.rendering_enabled?.should be_false()
343
+ @state.enable_rendering
344
+ @state.rendering_enabled?.should be_true()
345
+ end
346
+
347
+ it "allows rendering to be toggled" do
348
+ @state.rendering_enabled?.should be_true()
349
+ @state.toggle_rendering
350
+ @state.rendering_enabled?.should be_false()
351
+ @state.toggle_rendering
352
+ @state.rendering_enabled?.should be_true()
353
+ end
354
+
355
+ it "doesn't render nodes that are appended to it if rendering is disabled" do
356
+ @state << Inversion::Template::TextNode.new( "before" )
357
+ @state.disable_rendering
358
+ @state << Inversion::Template::TextNode.new( "during" )
359
+ @state.enable_rendering
360
+ @state << Inversion::Template::TextNode.new( "after" )
361
+
362
+ @state.to_s.should == 'beforeafter'
363
+ end
364
+
365
+ end
366
+
290
367
  end
291
368