garterbelt 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +38 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +21 -0
- data/Rakefile +46 -0
- data/TODO +3 -0
- data/VERSION +1 -0
- data/garterbelt.gemspec +165 -0
- data/lib/garterbelt.rb +23 -0
- data/lib/page.rb +46 -0
- data/lib/renderers/cache.rb +35 -0
- data/lib/renderers/closed_tag.rb +60 -0
- data/lib/renderers/comment.rb +14 -0
- data/lib/renderers/content_rendering.rb +41 -0
- data/lib/renderers/content_tag.rb +36 -0
- data/lib/renderers/doctype.rb +24 -0
- data/lib/renderers/renderer.rb +33 -0
- data/lib/renderers/text.rb +28 -0
- data/lib/renderers/xml.rb +9 -0
- data/lib/stocking.rb +11 -0
- data/lib/support/string.rb +165 -0
- data/lib/view.rb +341 -0
- data/spec/benchmark/templates/erector.rb +37 -0
- data/spec/benchmark/templates/garterbelt.rb +37 -0
- data/spec/benchmark/vs_erector.rb +53 -0
- data/spec/garterbelt_spec.rb +49 -0
- data/spec/integration/expectations/general_view.html +17 -0
- data/spec/integration/expectations/variables/view_with_user_and_params.html +23 -0
- data/spec/integration/expectations/variables/view_with_user_email.html +23 -0
- data/spec/integration/expectations/view_partial_nest.html +24 -0
- data/spec/integration/expectations/view_with_tags.html +19 -0
- data/spec/integration/templates/view_partial_nest.rb +22 -0
- data/spec/integration/templates/view_with_cache.rb +30 -0
- data/spec/integration/templates/view_with_partial.rb +36 -0
- data/spec/integration/templates/view_with_partial_2.rb +36 -0
- data/spec/integration/templates/view_with_tags.rb +26 -0
- data/spec/integration/templates/view_with_vars.rb +32 -0
- data/spec/integration/view_spec.rb +57 -0
- data/spec/page_spec.rb +99 -0
- data/spec/renderers/cache_spec.rb +85 -0
- data/spec/renderers/closed_tag_spec.rb +172 -0
- data/spec/renderers/comment_spec.rb +68 -0
- data/spec/renderers/content_tag_spec.rb +150 -0
- data/spec/renderers/doctype_spec.rb +46 -0
- data/spec/renderers/text_spec.rb +68 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/mock_view.rb +14 -0
- data/spec/support/puters.rb +10 -0
- data/spec/view/view_basics_spec.rb +106 -0
- data/spec/view/view_caching_spec.rb +132 -0
- data/spec/view/view_partial_spec.rb +63 -0
- data/spec/view/view_rails_type_helpers.rb +148 -0
- data/spec/view/view_render_spec.rb +408 -0
- data/spec/view/view_variables_spec.rb +159 -0
- metadata +367 -0
@@ -0,0 +1,408 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Garterbelt::View do
|
4
|
+
class BasicView < Garterbelt::View
|
5
|
+
def content
|
6
|
+
end
|
7
|
+
|
8
|
+
def alt_content
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
@view = BasicView.new
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'rendering' do
|
17
|
+
it 'clears the output on start' do
|
18
|
+
@view.output = 'Foo the magic output!'
|
19
|
+
@view.render
|
20
|
+
@view.output.should_not include 'Foo the magic output!'
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'methods' do
|
24
|
+
it 'renders the :content method by default' do
|
25
|
+
@view.should_receive(:content)
|
26
|
+
@view.render
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'renders an alternate method when requested' do
|
30
|
+
@view.should_receive(:foo)
|
31
|
+
@view.render(:foo)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns the output' do
|
36
|
+
@view.render.should === @view.output
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "render_buffer" do
|
40
|
+
before do
|
41
|
+
@hr = Garterbelt::ContentTag.new(:view => @view, :type => :hr)
|
42
|
+
@input = Garterbelt::ContentTag.new(:view => @view, :type => :input)
|
43
|
+
@img = Garterbelt::ContentTag.new(:view => @view, :type => :img)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'will clear the buffer' do
|
47
|
+
@view.buffer = [:foo]
|
48
|
+
@view.render_buffer
|
49
|
+
@view.buffer.should == []
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'will call render on each tag in the buffer' do
|
53
|
+
@view.buffer << @hr << @input << @img
|
54
|
+
@hr.should_receive(:render)
|
55
|
+
@input.should_receive(:render)
|
56
|
+
@img.should_receive(:render)
|
57
|
+
|
58
|
+
@view.render_buffer
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should recycle the tags' do
|
62
|
+
@view.buffer << @hr << @input << @img
|
63
|
+
@hr.stub(:render)
|
64
|
+
@input.stub(:render)
|
65
|
+
@img.stub(:render)
|
66
|
+
|
67
|
+
@hr.should_receive(:recycle)
|
68
|
+
@input.should_receive(:recycle)
|
69
|
+
@img.should_receive(:recycle)
|
70
|
+
|
71
|
+
@view.render_buffer
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'will add non renderable items to the output as strings' do
|
75
|
+
@view.buffer << "foo " << :bar
|
76
|
+
@view.render_buffer
|
77
|
+
@view.output.should include 'foo bar'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'tag nesting' do
|
82
|
+
it 'should render correctly at one layer deep' do
|
83
|
+
@view.buffer << Garterbelt::ClosedTag.new(:type => :hr, :view => @view)
|
84
|
+
@view.render.should == "<hr>\n"
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'second level' do
|
88
|
+
before do
|
89
|
+
@view.buffer << Garterbelt::ContentTag.new(:type => :p, :view => @view) do
|
90
|
+
@view.buffer << Garterbelt::ClosedTag.new(:type => :hr, :view => @view)
|
91
|
+
end
|
92
|
+
@view.render
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should leave an empty buffer' do
|
96
|
+
@view.buffer.should be_empty
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should include the content' do
|
100
|
+
@view.output.should include "<hr>"
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should puts the nested tag inside the parent tag' do
|
104
|
+
@view.output.should match /<p>\W*<hr>\W*<\/p>/
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe 'multi level' do
|
109
|
+
before do
|
110
|
+
@view.buffer << Garterbelt::ContentTag.new(:type => :form, :view => @view) do
|
111
|
+
@view.buffer << Garterbelt::ContentTag.new(:type => :fieldset, :view => @view) do
|
112
|
+
@view.buffer << Garterbelt::ContentTag.new(:type => :label, :view => @view, :attributes => {:for => 'email'}) do
|
113
|
+
@view.buffer << Garterbelt::ClosedTag.new(:type => :input, :view => @view, :attributes => {:name => 'email', :type => 'text'})
|
114
|
+
end
|
115
|
+
@view.buffer << Garterbelt::ContentTag.new(:type => :input, :view => @view, :attributes => {:type => 'submit', :value => 'Login or whatever'})
|
116
|
+
end
|
117
|
+
end
|
118
|
+
@view.render
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should include the deepest level content' do
|
122
|
+
@view.output.should include "<input name=\"email\" type=\"text\">"
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should nest properly' do
|
126
|
+
@view.output.should match /<form>\W*<fieldset>\W*<label[^>]*>\W*<input/
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should indent properly' do
|
130
|
+
@view.output.should match /^<form>/
|
131
|
+
@view.output.should match /^ <fieldset>/
|
132
|
+
@view.output.should match /^ <label/
|
133
|
+
@view.output.should match /^ <input name="email"/
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should include content after the nesting' do
|
137
|
+
@view.output.should include "<input type=\"submit\" value=\"Login or whatever\""
|
138
|
+
@view.output.should match /^ <input type="submit"/
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe 'class method' do
|
144
|
+
before do
|
145
|
+
@rendered = @view.render
|
146
|
+
@view.stub(:recycle)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'makes a new view' do
|
150
|
+
BasicView.should_receive(:new).and_return(@view)
|
151
|
+
BasicView.render
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'renders it' do
|
155
|
+
BasicView.stub(:new).and_return(@view)
|
156
|
+
@view.should_receive(:render)
|
157
|
+
BasicView.render
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'passes the :method option to render' do
|
161
|
+
BasicView.stub(:new).and_return(@view)
|
162
|
+
@view.should_receive(:render).with(:alt_content)
|
163
|
+
BasicView.render :method => :alt_content
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'recycles the view' do
|
167
|
+
BasicView.stub(:new).and_return(@view)
|
168
|
+
@view.should_receive(:recycle)
|
169
|
+
BasicView.render
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'returns the output' do
|
173
|
+
BasicView.stub(:new).and_return(@view)
|
174
|
+
BasicView.render.should == @rendered
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe 'tag helpers' do
|
180
|
+
describe '#tag' do
|
181
|
+
it 'makes a new tag' do
|
182
|
+
Garterbelt::ContentTag.should_receive(:new).with(
|
183
|
+
:type => :p, :view => @view, :content => 'content', :attributes => {:class => 'classy'}
|
184
|
+
).and_return('content')
|
185
|
+
@view.tag(:p, "content", {:class => 'classy'})
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'returns the tag' do
|
189
|
+
@view.tag(:p, "content", {:class => 'classy'}).is_a?(Garterbelt::ContentTag).should be_true
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'adds it to the buffer' do
|
193
|
+
tag = @view.tag(:p, "content", {:class => 'classy'})
|
194
|
+
@view.buffer.should include tag
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'works with block content' do
|
198
|
+
tag = @view.tag(:p, "content", {:class => 'classy'}) do
|
199
|
+
@view.buffer << "foo"
|
200
|
+
end
|
201
|
+
tag.content.is_a?(Proc).should be_true
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe '#closed_tag' do
|
206
|
+
it 'makes a new closed tag' do
|
207
|
+
Garterbelt::ClosedTag.should_receive(:new).with(
|
208
|
+
:type => :hr, :view => @view, :attributes => {:class => 'linear'}
|
209
|
+
).and_return('content')
|
210
|
+
@view.closed_tag(:hr, :class => 'linear')
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'returns the tag' do
|
214
|
+
@view.closed_tag(:hr, :class => 'linear').is_a?(Garterbelt::ClosedTag).should be_true
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'adds it to the buffer' do
|
218
|
+
tag = @view.closed_tag(:hr, :class => 'linear')
|
219
|
+
@view.buffer.should include tag
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
describe '#non_escape_tag' do
|
224
|
+
it 'calls #tag' do
|
225
|
+
@view.should_receive(:tag)
|
226
|
+
@view.non_escape_tag(:pre, "<div>content</div>", {:class => 'classy'})
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'sets and resets the escape when escape is originally set to true' do
|
230
|
+
@view.should_receive(:escape=).with(false).ordered
|
231
|
+
@view.should_receive(:tag).ordered
|
232
|
+
@view.should_receive(:escape=).with(true).ordered
|
233
|
+
@view.non_escape_tag(:pre, "<div>content</div>", {:class => 'classy'})
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'does not set the escape when set to false' do
|
237
|
+
@view.escape = false
|
238
|
+
@view.should_not_receive(:escape=)
|
239
|
+
@view.non_escape_tag(:pre, "<div>content</div>", {:class => 'classy'})
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe '#text' do
|
244
|
+
it 'makes a new Text' do
|
245
|
+
Garterbelt::Text.should_receive(:new).and_return('some content')
|
246
|
+
@view.text("content")
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'passes the right options to Text' do
|
250
|
+
Garterbelt::Text.should_receive(:new).with({
|
251
|
+
:view => @view, :content => 'content'
|
252
|
+
}).and_return('text renderer')
|
253
|
+
@view.text("content")
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'adds the Text object to the buffer' do
|
257
|
+
@view.text("content")
|
258
|
+
text = @view.buffer.last
|
259
|
+
text.is_a?(Garterbelt::Text).should be_true
|
260
|
+
text.content.should == 'content'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe '#raw_text' do
|
265
|
+
it 'calls #text' do
|
266
|
+
@view.should_receive(:text).and_return('text')
|
267
|
+
@view.raw_text("<div>foo</div>")
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'sets escape before and after for a view that is set to escape' do
|
271
|
+
@view.should_receive(:escape=).with(false).ordered
|
272
|
+
@view.should_receive(:text).and_return('text')
|
273
|
+
@view.should_receive(:escape=).with(true).ordered
|
274
|
+
@view.raw_text("<div>foo</div>")
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'does not set escape if the view is not escaping' do
|
278
|
+
@view.escape = false
|
279
|
+
@view.should_not_receive(:escape=)
|
280
|
+
@view.raw_text("<div>foo</div>")
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe 'html tag helpers' do
|
285
|
+
describe 'content tags' do
|
286
|
+
Garterbelt::View::CONTENT_TAGS.each do |type|
|
287
|
+
it "should have a method ##{type}" do
|
288
|
+
@view.should respond_to(type)
|
289
|
+
end
|
290
|
+
|
291
|
+
it "##{type} should call #tag with argument content" do
|
292
|
+
@view.should_receive(:tag).with(type.to_sym, "my great content", {:class => 'classy'})
|
293
|
+
@view.send(type, "my great content", {:class => 'classy'})
|
294
|
+
end
|
295
|
+
|
296
|
+
it "##{type} should send along block content" do
|
297
|
+
tag = @view.send(type, {:class => 'classy'}) do
|
298
|
+
'foo'
|
299
|
+
end
|
300
|
+
tag.content.is_a?(Proc).should be_true
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe 'closed tags' do
|
306
|
+
Garterbelt::View::CLOSED_TAGS.each do |type|
|
307
|
+
it "should have a method ##{type}" do
|
308
|
+
@view.should respond_to(type)
|
309
|
+
end
|
310
|
+
|
311
|
+
it "##{type} should call #closed_tag with argument" do
|
312
|
+
@view.should_receive(:closed_tag).with(type.to_sym, {:class => 'classy'})
|
313
|
+
@view.send(type, {:class => 'classy'})
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe 'non-escaping tags' do
|
319
|
+
Garterbelt::View::NON_ESCAPE_TAGS.each do |tag|
|
320
|
+
it "responds to :#{tag}" do
|
321
|
+
@view.should respond_to(tag)
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'calls non_escape_tag' do
|
325
|
+
@view.should_receive :non_escape_tag
|
326
|
+
@view.send(tag)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe 'comment' do
|
332
|
+
it 'makes a comment object' do
|
333
|
+
@view.comment('This is a comment.').is_a?(Garterbelt::Comment).should be_true
|
334
|
+
end
|
335
|
+
|
336
|
+
it 'puts it on the buffer' do
|
337
|
+
comment = @view.comment("new comment now")
|
338
|
+
@view.buffer.last.should == comment
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
describe 'doctype' do
|
343
|
+
it 'makes a Doctype object' do
|
344
|
+
@view.doctype(:html5).is_a?(Garterbelt::Doctype).should be_true
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'puts it on the buffer' do
|
348
|
+
doctype = @view.doctype
|
349
|
+
@view.buffer.last.should == doctype
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
describe 'xml' do
|
354
|
+
it 'adds an xml to the buffer' do
|
355
|
+
xml = @view.xml
|
356
|
+
xml.is_a?(Garterbelt::Xml).should be_true
|
357
|
+
@view.buffer.last.should == xml
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'makes a closed tag with default options' do
|
361
|
+
xml = @view.xml
|
362
|
+
xml.attributes[:version].should == 1.0
|
363
|
+
xml.attributes[:encoding].should == 'utf-8'
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'uses custom attributes when desired' do
|
367
|
+
xml = @view.xml(:version => 0)
|
368
|
+
xml.attributes[:version].should == 0
|
369
|
+
xml.attributes[:encoding].should == 'utf-8'
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
describe 'head tags' do
|
374
|
+
Garterbelt::View::HEAD_TAGS.each do |type|
|
375
|
+
describe "_#{type}" do
|
376
|
+
it "it is a method" do
|
377
|
+
@view.should respond_to("_#{type}")
|
378
|
+
end
|
379
|
+
|
380
|
+
it "makes a closed tag" do
|
381
|
+
@view.should_receive(:closed_tag).with(type.to_sym)
|
382
|
+
@view.send("_#{type}")
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
describe 'page_title' do
|
388
|
+
it 'makes a content tag of type :title' do
|
389
|
+
@view.should_receive(:tag).with(:title, "My Great Page Title!")
|
390
|
+
@view.page_title "My Great Page Title!"
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
describe 'helpers' do
|
395
|
+
it 'stylesheet_link makes a link closed tag with the right options' do
|
396
|
+
@view.should_receive(:_link).with(:rel => "stylesheet", 'type' => "text/css", :href => "/foo/theme.css")
|
397
|
+
@view.stylesheet_link('/foo/theme')
|
398
|
+
end
|
399
|
+
|
400
|
+
it 'javascript_link makes a script tag with the right options' do
|
401
|
+
@view.should_receive(:script).with( :src => "/foo/script.js", 'type' => "text/javascript")
|
402
|
+
@view.javascript_link('/foo/script')
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Garterbelt::View, 'Variables' do
|
4
|
+
class NeedyView < Garterbelt::View
|
5
|
+
requires :x, :y
|
6
|
+
end
|
7
|
+
|
8
|
+
class SelectiveView < Garterbelt::View
|
9
|
+
requires_only :x, :y
|
10
|
+
end
|
11
|
+
|
12
|
+
class ExtraNeedy < NeedyView
|
13
|
+
requires :z
|
14
|
+
end
|
15
|
+
|
16
|
+
class LessNeedy < Garterbelt::View
|
17
|
+
requires :x => 'x', :y => 'y'
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'class level requirements' do
|
21
|
+
it 'the class should store a list of required variables' do
|
22
|
+
NeedyView.required.should == [:x, :y]
|
23
|
+
SelectiveView.required.should == [:x, :y]
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should add to the required set of variable of the subclass' do
|
27
|
+
ExtraNeedy.required.should == [:x, :y, :z]
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'default values' do
|
31
|
+
it '#requires and #requires_only should allow the last argument to be a hash with default values' do
|
32
|
+
lambda{ LessNeedy.new }.should_not raise_error
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'stores the default values on the class' do
|
36
|
+
LessNeedy.default_variables.should == {:y => 'y', :x => 'x'}
|
37
|
+
ExtraNeedy.default_variables.should == {}
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'inherits default values from superclass' do
|
41
|
+
class SecondGenLessNeedy < LessNeedy
|
42
|
+
end
|
43
|
+
|
44
|
+
SecondGenLessNeedy.default_variables.should == {:y => 'y', :x => 'x'}
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'adds to default values of the superclass' do
|
48
|
+
class SecondGenLessNeedy < LessNeedy
|
49
|
+
requires :z => 'z'
|
50
|
+
end
|
51
|
+
|
52
|
+
SecondGenLessNeedy.default_variables.should == {:x => 'x', :y => 'y', :z => 'z'}
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'overwrites superclass defaults' do
|
56
|
+
class SecondGenLessNeedy < LessNeedy
|
57
|
+
requires :z => 'z'
|
58
|
+
end
|
59
|
+
|
60
|
+
class ThirdGen < SecondGenLessNeedy
|
61
|
+
requires :z => 'x'
|
62
|
+
end
|
63
|
+
|
64
|
+
ThirdGen.default_variables.should == {:x => 'x', :y => 'y', :z => 'x'}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'aliases #requires to #needs' do
|
69
|
+
class AltlyNeedy < NeedyView
|
70
|
+
needs :z
|
71
|
+
end
|
72
|
+
AltlyNeedy.required.should == [:x, :y, :z]
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'aliases #requires_only to #needs_only' do
|
76
|
+
class AltlyView < Garterbelt::View
|
77
|
+
needs_only :x, :y
|
78
|
+
end
|
79
|
+
AltlyView.required.should == [:x, :y]
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'accessor' do
|
83
|
+
describe 'without default' do
|
84
|
+
it 'builds readers for the required variables' do
|
85
|
+
NeedyView.instance_methods.should include 'x'
|
86
|
+
NeedyView.instance_methods.should include 'y'
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'builds writers for the required variables' do
|
90
|
+
NeedyView.instance_methods.should include 'x='
|
91
|
+
NeedyView.instance_methods.should include 'y='
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'with defaults' do
|
96
|
+
it 'makes readers' do
|
97
|
+
LessNeedy.instance_methods.should include 'x'
|
98
|
+
LessNeedy.instance_methods.should include 'y'
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'makes writers' do
|
102
|
+
LessNeedy.instance_methods.should include 'x='
|
103
|
+
LessNeedy.instance_methods.should include 'y='
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'raises on compile if the required variables map to existing methods' do
|
108
|
+
lambda {
|
109
|
+
class Failer < Garterbelt::View
|
110
|
+
requires :p
|
111
|
+
end
|
112
|
+
}.should raise_error(ArgumentError, ":p cannot be a required variable because it maps to an existing method")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe 'initialization' do
|
118
|
+
describe 'without defaults' do
|
119
|
+
it 'sets the accessors with the values provided' do
|
120
|
+
view = NeedyView.new :x => 'foo', :y => 'bar'
|
121
|
+
view.x.should == 'foo'
|
122
|
+
view.y.should == 'bar'
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'raises an error when values are not provided' do
|
126
|
+
lambda { NeedyView.new :x => 'x' }.should raise_error(ArgumentError, "[:y] required as an initialization option")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'with defaults' do
|
131
|
+
it 'sets accessors with default values' do
|
132
|
+
view = LessNeedy.new
|
133
|
+
view.x.should == 'x'
|
134
|
+
view.y.should == 'y'
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'sets accessors with custom value' do
|
138
|
+
view = LessNeedy.new(:x => 'foo', :y => 'bar')
|
139
|
+
view.x.should == 'foo'
|
140
|
+
view.y.should == 'bar'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'builds class level accessors when it receives additional parameters' do
|
145
|
+
class ExtraNeed < Garterbelt::View
|
146
|
+
requires :x, :y
|
147
|
+
end
|
148
|
+
|
149
|
+
view = ExtraNeed.new :x => 'foo', :y => 'bar', :z => 'zardoz'
|
150
|
+
view.should respond_to :z
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'raises an error when selective and it receives additional parameters' do
|
154
|
+
lambda { SelectiveView.new(:x => 'x', :y => 'y', :z => 'zardoz') }.should raise_error(
|
155
|
+
ArgumentError, "Allowed initalization options are only [:x, :y]"
|
156
|
+
)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|