hobo 0.5.3 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/hobo +18 -4
- data/hobo_files/plugin/CHANGES.txt +511 -0
- data/hobo_files/plugin/README +8 -3
- data/hobo_files/plugin/Rakefile +81 -0
- data/hobo_files/plugin/generators/hobo/hobo_generator.rb +4 -4
- data/hobo_files/plugin/generators/hobo/templates/guest.rb +1 -1
- data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +1 -1
- data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +16 -22
- data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +4 -6
- data/hobo_files/plugin/generators/hobo_front_controller/templates/search.dryml +6 -5
- data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +4 -6
- data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +237 -0
- data/hobo_files/plugin/generators/hobo_migration/templates/migration.rb +9 -0
- data/hobo_files/plugin/generators/hobo_model/USAGE +2 -3
- data/hobo_files/plugin/generators/hobo_model/hobo_model_generator.rb +1 -14
- data/hobo_files/plugin/generators/hobo_model/templates/fixtures.yml +1 -6
- data/hobo_files/plugin/generators/hobo_model/templates/model.rb +10 -4
- data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +7 -6
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_base.css +68 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.css +93 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +11 -6
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/plus.png +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/stylesheets/application.css +24 -14
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +28 -44
- data/hobo_files/plugin/generators/hobo_user_model/USAGE +2 -12
- data/hobo_files/plugin/generators/hobo_user_model/hobo_user_model_generator.rb +1 -14
- data/hobo_files/plugin/generators/hobo_user_model/templates/fixtures.yml +0 -6
- data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +8 -1
- data/hobo_files/plugin/init.rb +6 -2
- data/hobo_files/plugin/lib/active_record/has_many_association.rb +23 -12
- data/hobo_files/plugin/lib/extensions.rb +134 -40
- data/hobo_files/plugin/lib/extensions/test_case.rb +0 -1
- data/hobo_files/plugin/lib/hobo.rb +77 -46
- data/hobo_files/plugin/lib/hobo/authenticated_user.rb +24 -2
- data/hobo_files/plugin/lib/hobo/authentication_support.rb +2 -1
- data/hobo_files/plugin/lib/hobo/controller.rb +35 -12
- data/hobo_files/plugin/lib/hobo/define_tags.rb +4 -4
- data/hobo_files/plugin/lib/hobo/dryml.rb +33 -51
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +47 -34
- data/hobo_files/plugin/lib/hobo/dryml/scoped_variables.rb +37 -0
- data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +27 -5
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +545 -302
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +305 -135
- data/hobo_files/plugin/lib/hobo/email_address.rb +5 -0
- data/hobo_files/plugin/lib/hobo/field_spec.rb +66 -0
- data/hobo_files/plugin/lib/hobo/hobo_helper.rb +325 -0
- data/hobo_files/plugin/lib/hobo/html_string.rb +2 -0
- data/hobo_files/plugin/lib/hobo/lazy_hash.rb +13 -1
- data/hobo_files/plugin/lib/hobo/markdown_string.rb +3 -1
- data/hobo_files/plugin/lib/hobo/model.rb +185 -66
- data/hobo_files/plugin/lib/hobo/model_controller.rb +56 -49
- data/hobo_files/plugin/lib/hobo/password_string.rb +2 -0
- data/hobo_files/plugin/lib/hobo/plugins.rb +75 -0
- data/hobo_files/plugin/lib/hobo/rapid_helper.rb +98 -0
- data/hobo_files/plugin/lib/hobo/static_tags +0 -3
- data/hobo_files/plugin/lib/hobo/textile_string.rb +11 -1
- data/hobo_files/plugin/lib/hobo/undefined.rb +1 -1
- data/hobo_files/plugin/lib/rexml.rb +166 -75
- data/hobo_files/plugin/spec/fixtures/users.yml +9 -0
- data/hobo_files/plugin/spec/spec.opts +6 -0
- data/hobo_files/plugin/spec/spec_helper.rb +28 -0
- data/hobo_files/plugin/spec/unit/hobo/dryml/template_spec.rb +650 -0
- data/hobo_files/plugin/tags/core.dryml +58 -4
- data/hobo_files/plugin/tags/rapid.dryml +289 -135
- data/hobo_files/plugin/tags/rapid_document_tags.dryml +49 -0
- data/hobo_files/plugin/tags/rapid_editing.dryml +92 -69
- data/hobo_files/plugin/tags/rapid_forms.dryml +242 -0
- data/hobo_files/plugin/tags/rapid_navigation.dryml +65 -65
- data/hobo_files/plugin/tags/rapid_pages.dryml +197 -124
- data/hobo_files/plugin/tags/rapid_support.dryml +23 -0
- metadata +29 -22
- data/hobo_files/plugin/generators/hobo_model/templates/migration.rb +0 -13
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/default_mapping.rb +0 -11
- data/hobo_files/plugin/generators/hobo_user_model/templates/migration.rb +0 -15
- data/hobo_files/plugin/lib/hobo/HtmlString +0 -3
- data/hobo_files/plugin/lib/hobo/controller_helpers.rb +0 -135
- data/hobo_files/plugin/lib/hobo/core.rb +0 -475
- data/hobo_files/plugin/lib/hobo/rapid.rb +0 -447
- data/hobo_files/plugin/test/hobo_dryml_template_test.rb +0 -7
- data/hobo_files/plugin/test/hobo_test.rb +0 -7
@@ -0,0 +1,650 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
|
3
|
+
Template = Hobo::Dryml::Template
|
4
|
+
DrymlException = Hobo::Dryml::DrymlException
|
5
|
+
|
6
|
+
describe Template do
|
7
|
+
|
8
|
+
# --- Tag Compilation Examples --- #
|
9
|
+
|
10
|
+
# --- Compilation: Calling Block Tags --- #
|
11
|
+
|
12
|
+
it "should compile block-tag calls as method calls" do
|
13
|
+
compile_dryml("<foo/>").should == "<%= foo() %>"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should compile attributes as keyword parameters" do
|
17
|
+
compile_dryml("<foo a='1' b='2'/>").should == '<%= foo({:a => "1", :b => "2"}) %>'
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should compile code attributes as ruby code" do
|
21
|
+
compile_dryml("<foo a='&1 + 2'/>").should == '<%= foo({:a => (1 + 2)}) %>'
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should compile block-tag attributes with no RHS as passing `true`" do
|
25
|
+
compile_dryml("<foo a/>").should == '<%= foo({:a => (true)}) %>'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should compile content of a block-tag call as a Ruby block" do
|
29
|
+
compile_dryml("<foo>the body</foo>").should == "<% _output(foo() do |foo_default_tagbody| %>the body<% end) %>"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should support <default_tagbody/> inside the content of a block-tag" do
|
33
|
+
compile_dryml("<foo>!!<default_tagbody/>??</foo>").should ==
|
34
|
+
"<% _output(foo() do |foo_default_tagbody| %>!!<% foo_default_tagbody && foo_default_tagbody.call %>??<% end) %>"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should support the 'for' attribute on <default_tagbody/>" do
|
38
|
+
compile_dryml("<x><y>123<default_tagbody for='x'/>456</y></x>").should ==
|
39
|
+
"<% _output(x() do |x_default_tagbody| %><% _output(y() do |y_default_tagbody| %>" +
|
40
|
+
"123<% x_default_tagbody && x_default_tagbody.call %>456" +
|
41
|
+
"<% end) %><% end) %>"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should allow :foo as a shorthand for field='foo' on block tags" do
|
45
|
+
compile_dryml("<foo:name/>").should == '<%= foo({:field => "name"}) %>'
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should allow :title as a shorthand for field='title' on block tags (title is a static tag)" do
|
49
|
+
compile_dryml("<foo:name/>").should == '<%= foo({:field => "name"}) %>'
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should allow close tags to ommit the :field_name part" do
|
53
|
+
compile_dryml("<foo:name></foo>").should == '<%= foo({:field => "name"}) %>'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should compile block-tag calls with merge_attrs" do
|
57
|
+
compile_dryml("<foo merge_attrs/>").should == "<%= foo({}.merge((attributes) || {})) %>"
|
58
|
+
compile_dryml("<foo a='1' merge_attrs/>").should == '<%= foo({:a => "1"}.merge((attributes) || {})) %>'
|
59
|
+
end
|
60
|
+
|
61
|
+
# --- Compilation: Defining Block Tags --- #
|
62
|
+
|
63
|
+
it "should compile defs with lower-case names as block tags" do
|
64
|
+
compile_def("<def tag='foo'></def>").should ==
|
65
|
+
"<% def foo(all_attributes={}, &__block__); " +
|
66
|
+
"parameters = nil; " +
|
67
|
+
"_tag_context(all_attributes, __block__) do |tagbody| attributes, = _tag_locals(all_attributes, []) %>" +
|
68
|
+
"<% _erbout; end; end %>"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should compile attrs in defs as local variables" do
|
72
|
+
compile_def("<def tag='foo' attrs='a, b'></def>").should ==
|
73
|
+
"<% def foo(all_attributes={}, &__block__); " +
|
74
|
+
"parameters = nil; " +
|
75
|
+
"_tag_context(all_attributes, __block__) do |tagbody| " +
|
76
|
+
"a, b, attributes, = _tag_locals(all_attributes, [:a, :b]) %>" +
|
77
|
+
"<% _erbout; end; end %>"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should allow a default tagbody to be given inside the <tagbody> tag" do
|
81
|
+
compile_def("<def tag='foo'>123 <tagbody>blah</tagbody> 456</def>").should ==
|
82
|
+
"<% def foo(all_attributes={}, &__block__); " +
|
83
|
+
"parameters = nil; " +
|
84
|
+
"_tag_context(all_attributes, __block__) do |tagbody| attributes, = _tag_locals(all_attributes, []) %>" +
|
85
|
+
"123 " +
|
86
|
+
"<% _output(do_tagbody(tagbody, {}, proc { %>blah<% })) %>" +
|
87
|
+
" 456" +
|
88
|
+
"<% _erbout; end; end %>"
|
89
|
+
end
|
90
|
+
|
91
|
+
# --- Compilation: Defining Templates --- #
|
92
|
+
|
93
|
+
it "should compile defs with cap names as templates" do
|
94
|
+
# Note the presence of the `parameters` param, which block-tags don't have
|
95
|
+
compile_def("<def tag='Foo'></def>").should ==
|
96
|
+
"<% def Foo(all_attributes={}, all_parameters={}, &__block__); " +
|
97
|
+
"parameters = all_parameters - []; " +
|
98
|
+
"_tag_context(all_attributes, __block__) do |tagbody| attributes, = _tag_locals(all_attributes, []) %>" +
|
99
|
+
"<% _erbout; end; end %>"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should dissallow `param` outside of template definitions" do
|
103
|
+
proc { compile_dryml("<foo param/>") }.should raise_error(DrymlException)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should compile param tag-calls as calls to `call_block_tag_parameter`" do
|
107
|
+
compile_in_template("<foo param a='1'/>").should == '<%= call_block_tag_parameter(:foo, {:a => "1"}, all_parameters[:foo]) %>'
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should compile with support for named params" do
|
111
|
+
compile_in_template("<foo param='zap'/>").should == "<%= call_block_tag_parameter(:foo, {}, all_parameters[:zap]) %>"
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should compile a param tag-call with a body" do
|
115
|
+
compile_in_template("<foo param>abc</foo>").should ==
|
116
|
+
"<% _output(call_block_tag_parameter(:foo, {}, all_parameters[:foo]) do |foo_default_tagbody| %>abc<% end) %>"
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should compile a param tag-call with a body and a call to <default_tagbody/>" do
|
120
|
+
compile_in_template("<foo param>!!<default_tagbody/>!!</foo>").should ==
|
121
|
+
"<% _output(call_block_tag_parameter(:foo, {}, all_parameters[:foo]) do |foo_default_tagbody| %>" +
|
122
|
+
"!!<% foo_default_tagbody && foo_default_tagbody.call %>!!<% end) %>"
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should compile param template-calls as calls to `call_template_parameter`" do
|
126
|
+
compile_in_template("<Foo param/>").should ==
|
127
|
+
"<% _output(call_template_parameter(:Foo, {}, {}, all_parameters[:Foo])) %>"
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should compile param template-calls with parameters as calls to `call_template_parameter`" do
|
131
|
+
compile_in_template("<Foo param><a x='1'/></Foo>").should ==
|
132
|
+
'<% _output(call_template_parameter(:Foo, {}, {:a => proc { {:x => "1"} }, }, all_parameters[:Foo])) %>'
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should compile template parameters with param" do
|
136
|
+
compile_in_template("<Foo><abc param/></Foo>").should ==
|
137
|
+
'<% _output(Foo({}, {:abc => merge_option_procs(proc { {} }, all_parameters[:abc]), })) %>'
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should compile template parameters with named params" do
|
141
|
+
compile_in_template("<Foo><abc param='x'/></Foo>").should ==
|
142
|
+
'<% _output(Foo({}, {:abc => merge_option_procs(proc { {} }, all_parameters[:x]), })) %>'
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should compile template parameters with param and attributes" do
|
146
|
+
compile_in_template("<Foo><abc param='x' a='b'/></Foo>").should ==
|
147
|
+
'<% _output(Foo({}, {:abc => merge_option_procs(proc { {:a => "b"} }, all_parameters[:x]), })) %>'
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should compile template parameters with param and a tag body" do
|
151
|
+
compile_in_template("<Foo><abc param>ha!</abc></Foo>").should ==
|
152
|
+
'<% _output(Foo({}, {:abc => merge_option_procs(' +
|
153
|
+
'proc { {:tagbody => proc {|abc_default_tagbody| new_context { %>ha!<% } } } }, all_parameters[:abc]), })) %>'
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should compile template parameters which are template calls themselves" do
|
157
|
+
compile_in_template("<Foo><Baa param x='1'/></Foo>").should ==
|
158
|
+
'<% _output(Foo({}, {:Baa => merge_template_parameter_procs(proc { [{:x => "1"}, {}] }, all_parameters[:Baa]), })) %>'
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should compile template parameters which are templates themselves with their own parameters" do
|
162
|
+
compile_in_template("<Foo><Baa param><x>hello</x></Baa></Foo>").should ==
|
163
|
+
'<% _output(Foo({}, {:Baa => merge_template_parameter_procs(' +
|
164
|
+
'proc { [{}, {:x => proc { {:tagbody => proc {|x_default_tagbody| new_context { %>hello<% } } } }, }] }, all_parameters[:Baa]), })) %>'
|
165
|
+
end
|
166
|
+
|
167
|
+
# --- Compilation: Calling Templates --- #
|
168
|
+
|
169
|
+
it "should compile template calls as method calls" do
|
170
|
+
compile_dryml("<Foo/>").should == "<% _output(Foo({}, {})) %>"
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should compile attributes on template calls as keyword parameters" do
|
174
|
+
compile_dryml("<Foo a='1' b='2'/>").should == '<% _output(Foo({:a => "1", :b => "2"}, {})) %>'
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should compile template parameters as procs" do
|
178
|
+
compile_dryml("<Foo><x>hello</x><y>world</y></Foo>").should ==
|
179
|
+
'<% _output(Foo({}, {' +
|
180
|
+
':x => proc { {:tagbody => proc {|x_default_tagbody| new_context { %>hello<% } } } }, ' +
|
181
|
+
':y => proc { {:tagbody => proc {|y_default_tagbody| new_context { %>world<% } } } }, })) %>'
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should compile template parameters with attributes" do
|
185
|
+
compile_dryml("<Foo><abc x='1'>hello</abc></Foo>").should ==
|
186
|
+
'<% _output(Foo({}, {:abc => proc { {:x => "1", :tagbody => proc {|abc_default_tagbody| new_context { %>hello<% } } } }, })) %>'
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should allow :foo as a shorthand for field='foo' on template tags" do
|
190
|
+
compile_dryml("<Foo:name/>").should == '<% _output(Foo({:field => "name"}, {})) %>'
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should dissallow tag-bodies on template calls" do
|
194
|
+
proc { compile_dryml("<Foo>this is a tag body</Foo>") }.should raise_error(DrymlException)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should dissallow tag-bodies on nested template calls" do
|
198
|
+
proc { compile_dryml("<Foo><Baa>this is a tag body</Baa></Foo>") }.should raise_error(DrymlException)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should compile template parameters which are themselves templates" do
|
202
|
+
# Template parameters which are themselves templates are procs
|
203
|
+
# that return a pair of hashes, the first is the attributes to the
|
204
|
+
# template, the second is the sub-template procs
|
205
|
+
compile_dryml("<Foo><Baa x='1'><a>hello</a></Baa></Foo>").should ==
|
206
|
+
'<% _output(Foo({}, ' +
|
207
|
+
'{:Baa => proc { [{:x => "1"}, {:a => proc { {:tagbody => proc {|a_default_tagbody| new_context { %>hello<% } } } }, }] }, })) %>'
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should compile 'replace' parameters" do
|
211
|
+
compile_dryml("<Page><head replace>abc</head></Page>").should ==
|
212
|
+
'<% _output(Page({}, {:head => proc {|head__default| new_context { %>abc<% } }, })) %>'
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should compile 'replace' parameters with a default parameter call" do
|
216
|
+
compile_dryml("<Page><head replace>abc <head restore>blah</head></head></Page>").should ==
|
217
|
+
|
218
|
+
'<% _output(Page({}, {:head => proc {|head__default| new_context { %>abc ' +
|
219
|
+
'<% _output(head__default.call_with_block({}) do |head_default_tagbody| %>blah<% end) %>' +
|
220
|
+
'<% } }, })) %>'
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should compile 'replace' template parameters with a default parameter call" do
|
224
|
+
compile_dryml("<Page><Head replace>abc <Head restore/></Head></Page>").should ==
|
225
|
+
|
226
|
+
'<% _output(Page({}, {:Head => proc {|Head__default| new_context { %>abc ' +
|
227
|
+
'<% _output(Head__default.call({}, {})) %>' +
|
228
|
+
'<% } }, })) %>'
|
229
|
+
end
|
230
|
+
|
231
|
+
# --- Tag Evalutation Examples --- #
|
232
|
+
|
233
|
+
|
234
|
+
# --- Static Tags --- #
|
235
|
+
|
236
|
+
it "should pass through tags declared as static" do
|
237
|
+
"p".should be_in(Hobo.static_tags)
|
238
|
+
eval_dryml("<p>abc</p>").should == "<p>abc</p>"
|
239
|
+
eval_dryml("<p x='1' y='2'>abc</p>").should == "<p x='1' y='2'>abc</p>"
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should pass through attributes with no rhs on static tags" do
|
243
|
+
"p".should be_in(Hobo.static_tags)
|
244
|
+
eval_dryml("<p foo baa>abc</p>").should == "<p foo baa>abc</p>"
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should support attribute merging on static tags" do
|
248
|
+
eval_dryml(%(<p class="big" id="x" merge_attrs="&{:class => 'small', :id => 'y', :a => 'b'}"/>)).
|
249
|
+
should be_dom_equal_to('<p class="big small" id="y" a="b"/>')
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should support attribute merging of all extra attributes on static tags" do
|
253
|
+
eval_dryml(%(<def tag="x"><p class="big" id="x" merge_attrs/></def>
|
254
|
+
<x class='small' id='y' a='b'/>)).
|
255
|
+
should be_dom_equal_to('<p class="big small" id="y" a="b"/>')
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
# --- Block Tags --- #
|
260
|
+
|
261
|
+
|
262
|
+
def eval_with_defs(dryml)
|
263
|
+
eval_dryml(<<-END + dryml).strip
|
264
|
+
<def tag="t">plain tag</def>
|
265
|
+
|
266
|
+
<def tag="t_attr" attrs="x">it is <%= x %></def>
|
267
|
+
|
268
|
+
<def tag="t_body">( <tagbody>hmm</tagbody> )</def>
|
269
|
+
|
270
|
+
<def tag="merge_attrs_example"><p merge_attrs>hi</p></def>
|
271
|
+
END
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should call block tags" do
|
275
|
+
eval_with_defs("<t/>").should == "plain tag"
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
it "should call block tags passing attributes" do
|
280
|
+
eval_with_defs("<t_attr x='10'/>").should == "it is 10"
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should call block tags with a body" do
|
284
|
+
eval_with_defs("<t_body>foo</t_body>").should == "( foo )"
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should allow tagbody to have a default" do
|
288
|
+
eval_with_defs("<t_body/>").should == "( hmm )"
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should provide access to the default tagbody when overriding the body" do
|
292
|
+
eval_with_defs("<t_body>[<default_tagbody/>]</t_body>").should == "( [hmm] )"
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should support merge_attrs on static tags" do
|
296
|
+
eval_with_defs('<merge_attrs_example class="x"/>').should == '<p class="x">hi</p>'
|
297
|
+
end
|
298
|
+
|
299
|
+
it "should make the declared attributes available via the 'attrs_for' method" do
|
300
|
+
eval_with_defs('<%= attrs_for(:t_attr).inspect %>').should == '[:x]'
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
|
305
|
+
# --- Template Tags --- #
|
306
|
+
|
307
|
+
def eval_with_templates(dryml)
|
308
|
+
eval_dryml(<<-END + dryml).strip
|
309
|
+
<def tag="defined" attrs="a, b">a is <%= a %>, b is <%= b %>, body is <tagbody/></def>
|
310
|
+
|
311
|
+
<def tag="T">plain template</def>
|
312
|
+
|
313
|
+
<def tag="StaticMerge"><p>a <b name="big" param>bold</b> word</p></def>
|
314
|
+
|
315
|
+
<def tag="EmptyStaticMerge"><img name="big" src="..." param/></def>
|
316
|
+
|
317
|
+
<def tag="DefTagMerge">foo <defined param b="3">baa</defined>!</def>
|
318
|
+
|
319
|
+
<def tag="NestedStaticMerge">merge StaticMerge: <StaticMerge param/></def>
|
320
|
+
|
321
|
+
<def tag="ParameterMerge">parameter merge: <StaticMerge><b param/></StaticMerge></def>
|
322
|
+
END
|
323
|
+
end
|
324
|
+
|
325
|
+
it "should call template tags" do
|
326
|
+
eval_with_templates("<T/>").should == "plain template"
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should add attributes to static tags when merging" do
|
330
|
+
eval_with_templates("<StaticMerge><b onclick='alert()'/></StaticMerge>").should ==
|
331
|
+
'<p>a <b name="big" onclick="alert()">bold</b> word</p>'
|
332
|
+
end
|
333
|
+
|
334
|
+
it "should override attributes on static tags when merging" do
|
335
|
+
eval_with_templates("<StaticMerge><b name='small'/></StaticMerge>").should ==
|
336
|
+
'<p>a <b name="small">bold</b> word</p>'
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should replace tag bodies on static tags when merging" do
|
340
|
+
eval_with_templates('<StaticMerge><b>BOLD</b></StaticMerge>').should ==
|
341
|
+
'<p>a <b name="big">BOLD</b> word</p>'
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should add attributes to defined tags when merging" do
|
345
|
+
eval_with_templates('<DefTagMerge><defined a="2"/></DefTagMerge>').should ==
|
346
|
+
'foo a is 2, b is 3, body is baa!'
|
347
|
+
end
|
348
|
+
|
349
|
+
it "should override attributes on defined tags when merging" do
|
350
|
+
eval_with_templates('<DefTagMerge><defined b="2"/></DefTagMerge>').should ==
|
351
|
+
'foo a is , b is 2, body is baa!'
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should replace tag bodies on defined tags when merging" do
|
355
|
+
eval_with_templates('<DefTagMerge><defined>zip</defined></DefTagMerge>').should ==
|
356
|
+
'foo a is , b is 3, body is zip!'
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should leave non-merged tags unchanged" do
|
360
|
+
eval_with_templates('<StaticMerge></StaticMerge>').should ==
|
361
|
+
'<p>a <b name="big">bold</b> word</p>'
|
362
|
+
end
|
363
|
+
|
364
|
+
it "should merge into static tags with no body" do
|
365
|
+
eval_with_templates("<EmptyStaticMerge><img name='small'/></EmptyStaticMerge>").should ==
|
366
|
+
'<img name="small" src="..." />'
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should merge template parameters into nested templates" do
|
370
|
+
eval_with_templates('<NestedStaticMerge><StaticMerge><b name="small"/></StaticMerge></NestedStaticMerge>').should ==
|
371
|
+
'merge StaticMerge: <p>a <b name="small">bold</b> word</p>'
|
372
|
+
end
|
373
|
+
|
374
|
+
it "should merge the body of template parameters into nested templates" do
|
375
|
+
eval_with_templates('<NestedStaticMerge><StaticMerge><b>BOLD</b></StaticMerge></NestedStaticMerge>').should ==
|
376
|
+
'merge StaticMerge: <p>a <b name="big">BOLD</b> word</p>'
|
377
|
+
end
|
378
|
+
|
379
|
+
it "should allow param names to be defined dynamically" do
|
380
|
+
eval_dryml('<def tag="T"><p param="& :a.to_s + :b.to_s"/></def>' +
|
381
|
+
'<T><ab x="1"/></T>').should == '<p x="1" />'
|
382
|
+
end
|
383
|
+
|
384
|
+
it "should allow params to be defined on other params" do
|
385
|
+
eval_with_templates('<ParameterMerge><b name="small">foo</b></ParameterMerge>').should ==
|
386
|
+
'parameter merge: <p>a <b name="small">foo</b> word</p>'
|
387
|
+
|
388
|
+
end
|
389
|
+
|
390
|
+
it "should allow parameter bodies to be restored with static tag params" do
|
391
|
+
eval_with_templates("<StaticMerge><b>very <default_tagbody/></b></StaticMerge>").should ==
|
392
|
+
'<p>a <b name="big">very bold</b> word</p>'
|
393
|
+
end
|
394
|
+
|
395
|
+
it "should allow parameter bodies to be restored with defined tag params" do
|
396
|
+
eval_with_templates("<DefTagMerge><defined>hum<default_tagbody/></defined></DefTagMerge>").should ==
|
397
|
+
'foo a is , b is 3, body is humbaa!'
|
398
|
+
end
|
399
|
+
|
400
|
+
|
401
|
+
|
402
|
+
# --- Replacing Template Parameters --- #
|
403
|
+
|
404
|
+
it "should allow template parameters to be replaced" do
|
405
|
+
eval_with_templates('<StaticMerge><b replace>short</b></StaticMerge>').should ==
|
406
|
+
'<p>a short word</p>'
|
407
|
+
end
|
408
|
+
|
409
|
+
it "should allow template parameters to be replaced and then re-instated" do
|
410
|
+
eval_with_templates('<StaticMerge><b replace>short <b restore/></b></StaticMerge>').should ==
|
411
|
+
'<p>a short <b name="big">bold</b> word</p>'
|
412
|
+
end
|
413
|
+
|
414
|
+
it "should allow template parameters to be replaced and then re-instated with different attributes" do
|
415
|
+
eval_with_templates('<StaticMerge><b replace>short <b restore name="small"/></b></StaticMerge>').should ==
|
416
|
+
'<p>a short <b name="small">bold</b> word</p>'
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should allow template parameters to be replaced and then re-instated with a different tagbody" do
|
420
|
+
eval_with_templates('<StaticMerge><b replace>short <b restore>big</b></b></StaticMerge>').should ==
|
421
|
+
'<p>a short <b name="big">big</b> word</p>'
|
422
|
+
end
|
423
|
+
|
424
|
+
# --- Merge Params --- #
|
425
|
+
|
426
|
+
|
427
|
+
it "should support merge_param on template calls" do
|
428
|
+
tags = %(<def tag="Page"><h1 param='title'/><div param='footer'/></def>
|
429
|
+
<def tag="MyPage"><Page merge_params><footer>the footer</footer></Page></def>)
|
430
|
+
|
431
|
+
eval_dryml(tags + "<MyPage><title>Hi!</title></MyPage>").should == '<h1>Hi!</h1><div>the footer</div>'
|
432
|
+
end
|
433
|
+
|
434
|
+
|
435
|
+
# --- Polymorphic Tags --- #
|
436
|
+
|
437
|
+
it "should allow tags to be selected based on types" do
|
438
|
+
tags = %(<def tag="do"><tagbody/></def>
|
439
|
+
<def tag="t" for="String">A string</def>
|
440
|
+
<def tag="t" for="TrueClass">A boolean</def>)
|
441
|
+
|
442
|
+
eval_dryml(tags + '<do with="&\'foo\'"><%= call_polymorphic_tag(:t) %></do>').should == "A string"
|
443
|
+
eval_dryml(tags + '<do with="&false"><%= call_polymorphic_tag(:t) %></do>').should == "A boolean"
|
444
|
+
end
|
445
|
+
|
446
|
+
# --- The Context --- #
|
447
|
+
|
448
|
+
def context_eval(context, src)
|
449
|
+
eval_dryml(src, :context => context)
|
450
|
+
end
|
451
|
+
|
452
|
+
def a_user
|
453
|
+
Struct.new(:name, :email).new("Tom", "tom@foo.net")
|
454
|
+
end
|
455
|
+
|
456
|
+
def show_tag
|
457
|
+
'<def tag="show"><%= this %></def>'
|
458
|
+
end
|
459
|
+
|
460
|
+
it "should make the initial context available as `this`" do
|
461
|
+
context_eval('hello', "<%= this %>").should == "hello"
|
462
|
+
end
|
463
|
+
|
464
|
+
it "should allow the context to be changed with the :<field-name> syntax" do
|
465
|
+
context_eval(a_user, show_tag + '<show:name/>').should == "Tom"
|
466
|
+
end
|
467
|
+
|
468
|
+
it "should allow the :<field-name> to be ommitted from the close tag" do
|
469
|
+
context_eval(a_user, show_tag + '<show:name></show:name>').should == "Tom"
|
470
|
+
context_eval(a_user, show_tag + '<show:name></show>').should == "Tom"
|
471
|
+
end
|
472
|
+
|
473
|
+
|
474
|
+
it "should allow the context to be changed with a 'with' attribute" do
|
475
|
+
eval_dryml(show_tag + %(<show with="&'hello'"/>)).should == 'hello'
|
476
|
+
end
|
477
|
+
|
478
|
+
it "should allow the context to be changed with a 'field' attribute" do
|
479
|
+
context_eval(a_user, show_tag + '<show field="name"/>').should == "Tom"
|
480
|
+
end
|
481
|
+
|
482
|
+
it "should allow the context to be changed inside template parameters" do
|
483
|
+
tags = %(<def tag="do"><tagbody/></def>
|
484
|
+
<def tag="Template"><do:name><p param/></do></def>)
|
485
|
+
context_eval(a_user, tags + '<Template><p><%= this %></p></Template>').should == '<p>Tom</p>'
|
486
|
+
end
|
487
|
+
|
488
|
+
|
489
|
+
# --- <set> --- #
|
490
|
+
|
491
|
+
it "should provide <set> to create local variables" do
|
492
|
+
eval_dryml("<set x='&1' y='&2'/><%= x + y %>").should == '3'
|
493
|
+
end
|
494
|
+
|
495
|
+
it 'should interpolate #{...} blocks in attributes of any tag' do
|
496
|
+
tag = '<def tag="t" attrs="x"><%= x %></def>'
|
497
|
+
|
498
|
+
eval_dryml(tag + "<t x='#{1+2}'/>").should == '3'
|
499
|
+
eval_dryml(tag + "<t x='hey #{1+2} ho'/>").should == 'hey 3 ho'
|
500
|
+
|
501
|
+
eval_dryml(tag + "<p class='#{1+2}'/>").should == "<p class='3'/>"
|
502
|
+
eval_dryml(tag + "<p class='hey #{1+2} ho'/>").should == "<p class='hey 3 ho'/>"
|
503
|
+
end
|
504
|
+
|
505
|
+
|
506
|
+
# --- <set_scoped> --- #
|
507
|
+
|
508
|
+
it "should support scoped variables" do
|
509
|
+
tags =
|
510
|
+
"<def tag='t1'><set_scoped x='ping'><tagbody/></set_scoped></def>" +
|
511
|
+
"<def tag='t2'><set_scoped x='pong'><tagbody/></set_scoped></def>"
|
512
|
+
eval_dryml(tags + "<t1><%= scope.x %></t1>").should == 'ping'
|
513
|
+
eval_dryml(tags + "<t1><t2><%= scope.x %></t2> <%= scope.x %></t1>").should == 'pong ping'
|
514
|
+
end
|
515
|
+
|
516
|
+
|
517
|
+
# --- Taglibs --- #
|
518
|
+
|
519
|
+
it "should import tags from taglibs with the <include> tag" do
|
520
|
+
eval_dryml("<include src='taglibs/simple'/> <foo/>").should == "I am the foo tag"
|
521
|
+
end
|
522
|
+
|
523
|
+
it "should import tags from taglibs into a namespace with <include as/>" do
|
524
|
+
proc { eval_dryml("<include src='taglibs/simple' as='a'/> <foo/>") }.should raise_error
|
525
|
+
eval_dryml("<include src='taglibs/simple' as='a'/> <a.foo/>").should == "I am the foo tag"
|
526
|
+
end
|
527
|
+
|
528
|
+
|
529
|
+
# --- Control Attributes --- #
|
530
|
+
|
531
|
+
it "should alow static tags to be conditional with the 'if' attribute" do
|
532
|
+
eval_dryml("<p if='&false'/>").should == ""
|
533
|
+
eval_dryml("<p if='&true'/>").should == "<p />"
|
534
|
+
|
535
|
+
eval_dryml("<p if='&false'>hello</p>").should == ""
|
536
|
+
eval_dryml("<p if='&true'>hello</p>").should == "<p>hello</p>"
|
537
|
+
end
|
538
|
+
|
539
|
+
it "should alow static tags to be repeated with the 'repeat' attribute" do
|
540
|
+
eval_dryml('<img repeat="&[1,2,3]" src="#{this}" />').should ==
|
541
|
+
'<img src="1" /><img src="2" /><img src="3" />'
|
542
|
+
|
543
|
+
# Make sure <%= %> doesn't break
|
544
|
+
eval_dryml('<img repeat="&[1,2,3]" src="<%= this %>" />').should ==
|
545
|
+
'<img src="1" /><img src="2" /><img src="3" />'
|
546
|
+
end
|
547
|
+
|
548
|
+
it "should alow templates to be repeated with the 'repeat' attribute" do
|
549
|
+
eval_dryml('<def tag="T"><%= this %></def><T repeat="&[1,2,3]"/>').should == '123'
|
550
|
+
end
|
551
|
+
|
552
|
+
it "should allow <else> to be used with the if attribute" do
|
553
|
+
eval_dryml("<p if='&false'/><%= Hobo::Dryml.last_if %>").should == "false"
|
554
|
+
end
|
555
|
+
|
556
|
+
|
557
|
+
# --- Test Helpers --- #
|
558
|
+
|
559
|
+
def prepare_template(src, options)
|
560
|
+
options.reverse_merge!(:template_path => "TEST")
|
561
|
+
|
562
|
+
Hobo::Dryml::Template.clear_build_cache
|
563
|
+
@env = Class.new(Hobo::Dryml::TemplateEnvironment)
|
564
|
+
template = Template.new(src, @env, options[:template_path])
|
565
|
+
|
566
|
+
template.instance_variable_set("@builder", options[:builder]) if options[:builder]
|
567
|
+
template
|
568
|
+
end
|
569
|
+
|
570
|
+
|
571
|
+
def eval_dryml(src, options={})
|
572
|
+
options.reverse_merge!(:locals => {}, :implicit_imports => [])
|
573
|
+
|
574
|
+
template = prepare_template(src, options)
|
575
|
+
template.compile(options[:locals].keys, options[:implicit_imports])
|
576
|
+
new_renderer.render_page(options[:context], options[:locals]).strip
|
577
|
+
end
|
578
|
+
|
579
|
+
def compile_dryml(src, options={})
|
580
|
+
template = prepare_template(src, options)
|
581
|
+
CompiledDryml.new(template.process_src)
|
582
|
+
end
|
583
|
+
|
584
|
+
def new_renderer
|
585
|
+
@env.new("test-view", nil)
|
586
|
+
end
|
587
|
+
|
588
|
+
def compile_in_template(src)
|
589
|
+
builder = mock("builder", :null_object => true)
|
590
|
+
def_src = nil
|
591
|
+
builder.should_receive(:add_build_instruction) do |type, args|
|
592
|
+
def_src = args[:src]
|
593
|
+
end
|
594
|
+
compile_dryml("<def tag='MyTemplate'>#{src}</def>", :builder => builder)
|
595
|
+
|
596
|
+
# get rid of first and last scriptlets - they're to do with the method declaration
|
597
|
+
def_src.to_s.sub(/^\<\%.*?\%\>/, "").sub(/<% _erbout; end; end %><% _register_tag_attrs.*$/, "")
|
598
|
+
end
|
599
|
+
|
600
|
+
def compile_def(src)
|
601
|
+
builder = mock("builder", :null_object => true)
|
602
|
+
def_src = nil
|
603
|
+
builder.should_receive(:add_build_instruction) do |type, args|
|
604
|
+
def_src = args[:src]
|
605
|
+
end
|
606
|
+
compile_dryml(src, :builder => builder)
|
607
|
+
|
608
|
+
def_src.sub(/<% _register_tag_attrs.*/, "")
|
609
|
+
end
|
610
|
+
|
611
|
+
end
|
612
|
+
|
613
|
+
class CompiledDryml < String
|
614
|
+
|
615
|
+
def ==(other)
|
616
|
+
self.to_s.gsub(/\s+/, ' ').strip == other.gsub(/\s+/, ' ').strip
|
617
|
+
end
|
618
|
+
|
619
|
+
end
|
620
|
+
|
621
|
+
|
622
|
+
module Spec
|
623
|
+
module Matchers
|
624
|
+
|
625
|
+
class BeDomEqualTo #:nodoc:
|
626
|
+
def initialize(expected)
|
627
|
+
@expected = expected
|
628
|
+
end
|
629
|
+
|
630
|
+
def matches?(actual)
|
631
|
+
@actual = actual
|
632
|
+
expected_dom = HTML::Document.new(@expected).root
|
633
|
+
actual_dom = HTML::Document.new(@actual).root
|
634
|
+
expected_dom == actual_dom
|
635
|
+
end
|
636
|
+
|
637
|
+
def failure_message
|
638
|
+
"#{@expected}\nexpected to be == (by DOM) to\n#{@actual}"
|
639
|
+
end
|
640
|
+
|
641
|
+
def description
|
642
|
+
"be DOM equal to\n#{@expected}"
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
def be_dom_equal_to(expected)
|
647
|
+
Matchers::BeDomEqualTo.new(expected)
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|