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.
- data.tar.gz.sig +2 -0
- data/.gemtest +0 -0
- data/ChangeLog +836 -0
- data/History.md +4 -0
- data/Manifest.txt +74 -0
- data/README.rdoc +171 -0
- data/Rakefile +55 -0
- data/bin/inversion +276 -0
- data/lib/inversion.rb +98 -0
- data/lib/inversion/exceptions.rb +21 -0
- data/lib/inversion/mixins.rb +236 -0
- data/lib/inversion/monkeypatches.rb +20 -0
- data/lib/inversion/renderstate.rb +337 -0
- data/lib/inversion/sinatra.rb +35 -0
- data/lib/inversion/template.rb +250 -0
- data/lib/inversion/template/attrtag.rb +120 -0
- data/lib/inversion/template/calltag.rb +16 -0
- data/lib/inversion/template/codetag.rb +164 -0
- data/lib/inversion/template/commenttag.rb +54 -0
- data/lib/inversion/template/conditionaltag.rb +49 -0
- data/lib/inversion/template/configtag.rb +60 -0
- data/lib/inversion/template/containertag.rb +45 -0
- data/lib/inversion/template/elsetag.rb +62 -0
- data/lib/inversion/template/elsiftag.rb +49 -0
- data/lib/inversion/template/endtag.rb +55 -0
- data/lib/inversion/template/escapetag.rb +26 -0
- data/lib/inversion/template/fortag.rb +120 -0
- data/lib/inversion/template/iftag.rb +69 -0
- data/lib/inversion/template/importtag.rb +70 -0
- data/lib/inversion/template/includetag.rb +51 -0
- data/lib/inversion/template/node.rb +102 -0
- data/lib/inversion/template/parser.rb +297 -0
- data/lib/inversion/template/pptag.rb +28 -0
- data/lib/inversion/template/publishtag.rb +72 -0
- data/lib/inversion/template/subscribetag.rb +88 -0
- data/lib/inversion/template/tag.rb +150 -0
- data/lib/inversion/template/textnode.rb +43 -0
- data/lib/inversion/template/unlesstag.rb +60 -0
- data/lib/inversion/template/uriencodetag.rb +30 -0
- data/lib/inversion/template/yieldtag.rb +51 -0
- data/lib/inversion/tilt.rb +82 -0
- data/lib/inversion/utils.rb +235 -0
- data/spec/data/sinatra/hello.inversion +1 -0
- data/spec/inversion/mixins_spec.rb +177 -0
- data/spec/inversion/monkeypatches_spec.rb +35 -0
- data/spec/inversion/renderstate_spec.rb +291 -0
- data/spec/inversion/sinatra_spec.rb +59 -0
- data/spec/inversion/template/attrtag_spec.rb +216 -0
- data/spec/inversion/template/calltag_spec.rb +30 -0
- data/spec/inversion/template/codetag_spec.rb +51 -0
- data/spec/inversion/template/commenttag_spec.rb +84 -0
- data/spec/inversion/template/configtag_spec.rb +105 -0
- data/spec/inversion/template/containertag_spec.rb +54 -0
- data/spec/inversion/template/elsetag_spec.rb +105 -0
- data/spec/inversion/template/elsiftag_spec.rb +87 -0
- data/spec/inversion/template/endtag_spec.rb +78 -0
- data/spec/inversion/template/escapetag_spec.rb +59 -0
- data/spec/inversion/template/fortag_spec.rb +98 -0
- data/spec/inversion/template/iftag_spec.rb +241 -0
- data/spec/inversion/template/importtag_spec.rb +106 -0
- data/spec/inversion/template/includetag_spec.rb +108 -0
- data/spec/inversion/template/node_spec.rb +81 -0
- data/spec/inversion/template/parser_spec.rb +170 -0
- data/spec/inversion/template/pptag_spec.rb +51 -0
- data/spec/inversion/template/publishtag_spec.rb +69 -0
- data/spec/inversion/template/subscribetag_spec.rb +60 -0
- data/spec/inversion/template/tag_spec.rb +97 -0
- data/spec/inversion/template/textnode_spec.rb +86 -0
- data/spec/inversion/template/unlesstag_spec.rb +84 -0
- data/spec/inversion/template/uriencodetag_spec.rb +49 -0
- data/spec/inversion/template/yieldtag_spec.rb +54 -0
- data/spec/inversion/template_spec.rb +269 -0
- data/spec/inversion/tilt_spec.rb +47 -0
- data/spec/inversion_spec.rb +95 -0
- data/spec/lib/constants.rb +9 -0
- data/spec/lib/helpers.rb +160 -0
- metadata +316 -0
- 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
|
+
|