garterbelt 0.0.2
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/.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
|