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,35 @@
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
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 'ripper'
14
+
15
+ require 'rspec'
16
+ require 'spec/lib/helpers'
17
+
18
+ require 'inversion/monkeypatches'
19
+
20
+
21
+ describe Inversion, "monkeypatches" do
22
+
23
+ describe Inversion::RipperAdditions do
24
+
25
+ it "exposes the Ripper::TokenPattern::MatchData's #tokens array" do
26
+ tagpattern = Ripper::TokenPattern.compile( '$(ident) $(sp) $(ident)' )
27
+ matchdata = tagpattern.match( "foo bar" )
28
+ matchdata.tokens.map {|tok| tok[1] }.should == [ :on_ident, :on_sp, :on_ident ]
29
+ end
30
+
31
+ end
32
+
33
+
34
+ end
35
+
@@ -0,0 +1,291 @@
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
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
+
15
+ require 'spec/lib/helpers'
16
+ require 'inversion/renderstate'
17
+ require 'inversion/template/attrtag'
18
+ require 'inversion/template/textnode'
19
+
20
+ describe Inversion::RenderState do
21
+
22
+ before( :all ) do
23
+ setup_logging( :fatal )
24
+ end
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] )
36
+ end
37
+
38
+
39
+ it "preserves tainted status when copying its attributes" do
40
+ attributes = { :danger => "in pants" }
41
+ attributes[:danger].taint
42
+
43
+ state = Inversion::RenderState.new( attributes )
44
+
45
+ state.attributes[:danger].should be_tainted()
46
+ end
47
+
48
+
49
+ it "preserves frozen status when copying its attributes" do
50
+ attributes = { :danger => "in pants" }
51
+ attributes[:danger].freeze
52
+
53
+ state = Inversion::RenderState.new( attributes )
54
+
55
+ state.attributes[:danger].should be_frozen()
56
+ end
57
+
58
+
59
+ it "preserves singleton methods on attribute objects when copying" do
60
+ obj = Object.new
61
+ def obj.foo; "foo!"; end
62
+
63
+ state = Inversion::RenderState.new( :foo => obj )
64
+
65
+ state.attributes[:foo].singleton_methods.map( &:to_sym ).should include( :foo )
66
+ end
67
+
68
+
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
74
+
75
+
76
+ it "can evaluate code in the context of itself" do
77
+ attributes = { :foot => "in mouth", :bear => "in woods" }
78
+
79
+ state = Inversion::RenderState.new( attributes )
80
+
81
+ state.eval( "foot" ).should == 'in mouth'
82
+ end
83
+
84
+ it "can override its attributes for the duration of a block" do
85
+ attributes = { :foot => "in mouth", :bear => "in woods" }
86
+
87
+ state = Inversion::RenderState.new( attributes )
88
+
89
+ state.with_attributes( :foot => 'ball' ) do
90
+ state.foot.should == 'ball'
91
+ state.bear.should == 'in woods'
92
+ end
93
+
94
+ state.attributes[:foot].should == 'in mouth'
95
+ end
96
+
97
+
98
+ it "restores the original attributes if the block raises an exception" do
99
+ attributes = { :foot => "in mouth", :bear => "in woods" }
100
+
101
+ state = Inversion::RenderState.new( attributes )
102
+
103
+ expect {
104
+ state.with_attributes( {} ) do
105
+ raise "Charlie dooo!"
106
+ end
107
+ }.to raise_error()
108
+
109
+ state.attributes[:foot].should == 'in mouth'
110
+ end
111
+
112
+
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
118
+
119
+
120
+ it "can override the render destination for the duration of a block" do
121
+ state = Inversion::RenderState.new
122
+
123
+ original_dest = state.destination
124
+ newdest = []
125
+ node = Inversion::Template::TextNode.new( "New!" )
126
+ rval = state.with_destination( newdest ) do
127
+ state << node
128
+ end
129
+ rval.should equal( newdest )
130
+
131
+ newdest.should have( 1 ).member
132
+ newdest.should include( 'New!' )
133
+ state.destination.should equal( original_dest )
134
+ end
135
+
136
+ it "restores the original destination if the block raises an exception" do
137
+ state = Inversion::RenderState.new
138
+
139
+ original_dest = state.destination
140
+
141
+ expect {
142
+ state.with_destination( [] ) do
143
+ raise "New!"
144
+ end
145
+ }.to raise_error()
146
+
147
+ state.destination.should equal( original_dest )
148
+ end
149
+
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
155
+
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 )
159
+
160
+ state << node
161
+
162
+ state.to_s.should == '<!-- Attr: { template.foo } -->'
163
+ end
164
+
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 )
168
+
169
+ state << node
170
+
171
+ state.to_s.should == ''
172
+ end
173
+
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 )
177
+
178
+ state << node
179
+
180
+ state.to_s.should == ''
181
+ end
182
+
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 )
186
+
187
+ state << node
188
+
189
+ state.to_s.should == "<!-- NoMethodError: undefined method `klang' for nil:NilClass -->"
190
+ end
191
+
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
+
196
+ expect {
197
+ state << node
198
+ }.to raise_error( NoMethodError, /undefined method/ )
199
+ end
200
+
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]
205
+ end
206
+ node = Inversion::Template::AttrTag.new( 'boom.klang' )
207
+ state = Inversion::RenderState.new( {}, :on_render_error => :propagate )
208
+ defhandler = state.errhandler
209
+
210
+ expect {
211
+ state.with_error_handler( handler ) do
212
+ state << node
213
+ end
214
+ }.to_not raise_error()
215
+
216
+ state.to_s.should =~ /yum, i eat nomethoderror/i
217
+ state.errhandler.should equal( defhandler )
218
+ end
219
+
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
+ end
226
+
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
+
231
+ expect {
232
+ state << node
233
+ }.to raise_error( NoMethodError, /undefined method/ )
234
+ end
235
+
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
240
+
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
245
+
246
+
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} )
254
+
255
+ thirdstate = state.merge( anotherstate )
256
+
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
+ )
265
+
266
+ end
267
+
268
+
269
+ describe "publish/subscribe:" do
270
+
271
+ before( :each ) do
272
+ @state = Inversion::RenderState.new
273
+ end
274
+
275
+ it "doesn't have any subscriptions by default" do
276
+ @state.subscriptions.should == {}
277
+ end
278
+
279
+ it "allows an object to subscribe to node publications" do
280
+ subscriber = Object.new
281
+
282
+ @state.subscribe( :the_key, subscriber )
283
+
284
+ @state.subscriptions.should have( 1 ).member
285
+ @state.subscriptions[ :the_key ].should == [ subscriber ]
286
+ end
287
+
288
+ end
289
+
290
+ end
291
+
@@ -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
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
+
16
+ begin
17
+ require 'rack/test'
18
+ require 'inversion/sinatra'
19
+ $sinatra_support = true
20
+ rescue LoadError => err
21
+ warn "Sintra support testing disabled: %p: %s" % [ err.class, err.message ]
22
+ $sinatra_support = false
23
+ end
24
+
25
+ describe "Sinatra support", :if => $sinatra_support do
26
+ include Rack::Test::Methods if defined?( ::Rack )
27
+
28
+ before( :all ) do
29
+ setup_logging( :fatal )
30
+ end
31
+
32
+ before( :each ) do
33
+ @datadir = Pathname( __FILE__ ).dirname.parent + 'data'
34
+ Sinatra::Base.set :environment, :test
35
+ end
36
+
37
+ def app
38
+ @app
39
+ end
40
+
41
+ it "extends the Sinatra DSL with an #inversion helper method" do
42
+ Sinatra::Base.instance_methods.should include( :inversion )
43
+ end
44
+
45
+ it "renders .inversion files in views path" do
46
+ @app = Sinatra.new( Sinatra::Base ) do
47
+ set :views, File.dirname( __FILE__ ) + '/../data/sinatra'
48
+ get '/' do
49
+ inversion :hello
50
+ end
51
+ end
52
+
53
+ get '/'
54
+ last_response.should be_ok
55
+ last_response.body.should == 'Hello, Sinatra!'
56
+ end
57
+
58
+ end
59
+
@@ -0,0 +1,216 @@
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/attrtag'
16
+
17
+ describe Inversion::Template::AttrTag do
18
+
19
+ before( :all ) do
20
+ setup_logging( :fatal )
21
+ end
22
+
23
+ after( :all ) do
24
+ reset_logging()
25
+ end
26
+
27
+ describe "parsing" do
28
+
29
+ it "can have a simple attribute name" do
30
+ Inversion::Template::AttrTag.new( 'foo' ).name.should == :foo
31
+ end
32
+
33
+ it "can have an attribute name and a format string" do
34
+ tag = Inversion::Template::AttrTag.new( '"%0.2f" % foo' )
35
+ tag.name.should == :foo
36
+ tag.format.should == '%0.2f'
37
+ end
38
+
39
+ it "raises an exception with an unknown operator" do
40
+ expect {
41
+ Inversion::Template::AttrTag.new( '"%0.2f" + foo' )
42
+ }.to raise_exception( Inversion::ParseError, /expected/ )
43
+ end
44
+
45
+ it "raises an exception if it has more than one identifier" do
46
+ expect {
47
+ Inversion::Template::AttrTag.new( '"%0.2f" % [ foo, bar ]' )
48
+ }.to raise_exception( Inversion::ParseError, /expected/ )
49
+ end
50
+
51
+ it "supports simple <identifier>.<methodname> syntax" do
52
+ tag = Inversion::Template::AttrTag.new( 'foo.bar' )
53
+
54
+ tag.name.should == :foo
55
+ tag.methodchain.should == '.bar'
56
+ end
57
+
58
+ it "supports index operator (<identifier>.methodname[ <arguments> ]) syntax" do
59
+ tag = Inversion::Template::AttrTag.new( 'foo.bar[8]' )
60
+
61
+ tag.name.should == :foo
62
+ tag.methodchain.should == '.bar[8]'
63
+ end
64
+
65
+ it "supports index operator (<identifier>[ <arguments> ]) syntax" do
66
+ tag = Inversion::Template::AttrTag.new( 'foo[8]' )
67
+
68
+ tag.name.should == :foo
69
+ tag.methodchain.should == '[8]'
70
+ end
71
+
72
+ it "supports <identifier>.<methodname>( <arguments> ) syntax" do
73
+ tag = Inversion::Template::AttrTag.new( 'foo.bar( 8, :baz )' )
74
+
75
+ tag.name.should == :foo
76
+ tag.methodchain.should == '.bar( 8, :baz )'
77
+ end
78
+
79
+ it "can have a format with a methodchain" do
80
+ tag = Inversion::Template::AttrTag.new( '"%0.02f" % foo.bar( 8 )' )
81
+
82
+ tag.name.should == :foo
83
+ tag.methodchain.should == '.bar( 8 )'
84
+ tag.format.should == '%0.02f'
85
+ end
86
+ end
87
+
88
+ describe "rendering" do
89
+
90
+ it "can render itself as a comment for template debugging" do
91
+ tag = Inversion::Template::AttrTag.new( 'foo.bar( 8, :baz )' )
92
+ tag.as_comment_body.should == "Attr: { template.foo.bar( 8, :baz ) }"
93
+ end
94
+
95
+ context "without a format" do
96
+
97
+ before( :each ) do
98
+ @tag = Inversion::Template::AttrTag.new( 'foo' )
99
+ end
100
+
101
+ it "renders as the stringified contents of the template attribute with the same name" do
102
+ template = stub( "template object", :attributes => {:foo => %w[floppy the turtle]} )
103
+ @tag.render( template ).should == ["floppy", "the", "turtle"]
104
+ end
105
+
106
+ it "doesn't error if the attribute isn't set on the template" do
107
+ template = stub( "template object", :attributes => { :foo => nil } )
108
+ @tag.render( template ).should == nil
109
+ end
110
+
111
+ it "returns false when the rendered value is false" do
112
+ template = stub( "template object", :attributes => { :foo => false } )
113
+ @tag.render( template ).should equal( false )
114
+ end
115
+
116
+ it "can render itself as a comment for template debugging" do
117
+ @tag.as_comment_body.should == 'Attr: { template.foo }'
118
+ end
119
+
120
+ end
121
+
122
+ context "with a format" do
123
+
124
+ before( :each ) do
125
+ @tag = Inversion::Template::AttrTag.new( 'foo' )
126
+ @tag.format = "%0.2f"
127
+ end
128
+
129
+ it "renders as the formatted contents of the template attribute with the same name" do
130
+ attributes = double( "template object attributes" )
131
+ template = stub( "template object", :attributes => attributes )
132
+
133
+ attributes.should_receive( :[] ).with( :foo ).and_return( Math::PI )
134
+
135
+ @tag.render( template ).should == '3.14'
136
+ end
137
+
138
+ it "doesn't error if the attribute isn't set on the template" do
139
+ attributes = double( "template object attributes" )
140
+ template = stub( "template object", :attributes => attributes )
141
+
142
+ attributes.should_receive( :[] ).with( :foo ).and_return( nil )
143
+
144
+ @tag.render( template ).should == nil
145
+ end
146
+
147
+ it "can render itself as a comment for template debugging" do
148
+ @tag.as_comment_body.
149
+ should == 'Attr: { template.foo } with format: "%0.2f"'
150
+ end
151
+
152
+ end
153
+
154
+ context "with a methodchain" do
155
+
156
+ before( :each ) do
157
+ @attribute_object = mock( "template attribute" )
158
+ end
159
+
160
+ it "renders a single method call with no arguments" do
161
+ template = Inversion::Template.new( 'this is <?attr foo.bar ?>' )
162
+ template.foo = @attribute_object
163
+ @attribute_object.should_receive( :bar ).with( no_args() ).and_return( "the result" )
164
+
165
+ template.render.should == "this is the result"
166
+ end
167
+
168
+ it "renders a single method call with one argument" do
169
+ template = Inversion::Template.new( 'this is <?attr foo.bar(8) ?>' )
170
+ template.foo = @attribute_object
171
+ @attribute_object.should_receive( :bar ).with( 8 ).and_return( "the result" )
172
+
173
+ template.render.should == "this is the result"
174
+ end
175
+
176
+ it "renders a call with a single index operator" do
177
+ template = Inversion::Template.new( 'lines end with <?attr config[:line_ending] ?>' )
178
+ template.config = { :line_ending => 'newline' }
179
+
180
+ template.render.should == "lines end with newline"
181
+ end
182
+
183
+ it "renders a single method call with multiple arguments" do
184
+ template = Inversion::Template.new( 'this is <?attr foo.bar(8, :woo) ?>' )
185
+ template.foo = @attribute_object
186
+ @attribute_object.should_receive( :bar ).with( 8, :woo ).and_return( "the result" )
187
+
188
+ template.render.should == "this is the result"
189
+ end
190
+
191
+ it "renders multiple method calls with no arguments" do
192
+ additional_object = mock( 'additional template attribute' )
193
+ template = Inversion::Template.new( 'this is <?attr foo.bar.baz ?>' )
194
+ template.foo = @attribute_object
195
+ template.foo.should_receive( :bar ).and_return( additional_object )
196
+ additional_object.should_receive( :baz ).with( no_args() ).and_return( "the result" )
197
+
198
+ template.render.should == "this is the result"
199
+ end
200
+
201
+ it "renders multiple method calls with arguments" do
202
+ additional_object = mock( 'additional template attribute' )
203
+ template = Inversion::Template.new( 'this is <?attr foo.bar( 8 ).baz( :woo ) ?>' )
204
+ template.foo = @attribute_object
205
+ template.foo.should_receive( :bar ).with( 8 ).and_return( additional_object )
206
+ additional_object.should_receive( :baz ).with( :woo ).and_return( "the result" )
207
+
208
+ template.render.should == "this is the result"
209
+ end
210
+ end
211
+
212
+ end
213
+
214
+ end
215
+
216
+