runo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +120 -0
  3. data/bin/runo +35 -0
  4. data/lib/_error.rb +14 -0
  5. data/lib/_field.rb +260 -0
  6. data/lib/_i18n.rb +141 -0
  7. data/lib/_parser.rb +243 -0
  8. data/lib/_path.rb +86 -0
  9. data/lib/_storage/_storage.rb +213 -0
  10. data/lib/_storage/file.rb +200 -0
  11. data/lib/_storage/sequel.rb +174 -0
  12. data/lib/_storage/temp.rb +73 -0
  13. data/lib/_widget/action_create.rb +23 -0
  14. data/lib/_widget/action_login.rb +22 -0
  15. data/lib/_widget/action_signup.rb +16 -0
  16. data/lib/_widget/action_update.rb +16 -0
  17. data/lib/_widget/crumb.rb +24 -0
  18. data/lib/_widget/done.rb +16 -0
  19. data/lib/_widget/login.rb +25 -0
  20. data/lib/_widget/me.rb +31 -0
  21. data/lib/_widget/message.rb +51 -0
  22. data/lib/_widget/navi.rb +88 -0
  23. data/lib/_widget/submit.rb +49 -0
  24. data/lib/_widget/view_ym.rb +77 -0
  25. data/lib/_workflow/_workflow.rb +89 -0
  26. data/lib/_workflow/attachment.rb +50 -0
  27. data/lib/_workflow/blog.rb +28 -0
  28. data/lib/_workflow/contact.rb +23 -0
  29. data/lib/_workflow/forum.rb +26 -0
  30. data/lib/_workflow/register.rb +39 -0
  31. data/lib/meta/_meta.rb +20 -0
  32. data/lib/meta/group.rb +19 -0
  33. data/lib/meta/id.rb +59 -0
  34. data/lib/meta/owner.rb +21 -0
  35. data/lib/meta/timestamp.rb +118 -0
  36. data/lib/runo.rb +396 -0
  37. data/lib/scalar/checkbox.rb +68 -0
  38. data/lib/scalar/file.rb +144 -0
  39. data/lib/scalar/img.rb +112 -0
  40. data/lib/scalar/password.rb +58 -0
  41. data/lib/scalar/radio.rb +47 -0
  42. data/lib/scalar/select.rb +47 -0
  43. data/lib/scalar/text.rb +38 -0
  44. data/lib/scalar/textarea.rb +35 -0
  45. data/lib/scalar/textarea_pre.rb +14 -0
  46. data/lib/scalar/textarea_wiki.rb +173 -0
  47. data/lib/set/_set.rb +195 -0
  48. data/lib/set/dynamic.rb +177 -0
  49. data/lib/set/static.rb +102 -0
  50. data/lib/set/static_folder.rb +96 -0
  51. data/locale/en/index.po +242 -0
  52. data/locale/index.pot +243 -0
  53. data/locale/ja/index.po +242 -0
  54. data/locale/lazy_parser.rb +54 -0
  55. data/skel/config.ru +27 -0
  56. data/skel/skin/_users/00000000_frank-avatar.jpg +0 -0
  57. data/skel/skin/_users/00000000_frank-avatar_small.jpg +0 -0
  58. data/skel/skin/_users/00000000_frank.yaml +12 -0
  59. data/skel/skin/_users/00000000_root-avatar.jpg +0 -0
  60. data/skel/skin/_users/00000000_root-avatar_small.jpg +0 -0
  61. data/skel/skin/_users/00000000_root.yaml +11 -0
  62. data/skel/skin/_users/css/users.css +21 -0
  63. data/skel/skin/_users/css/users.less +25 -0
  64. data/skel/skin/_users/done.html +42 -0
  65. data/skel/skin/_users/index.html +46 -0
  66. data/skel/skin/_users/index.yaml +3 -0
  67. data/skel/skin/_users/summary.html +40 -0
  68. data/skel/skin/css/base.css +93 -0
  69. data/skel/skin/css/base.less +139 -0
  70. data/skel/skin/css/coax.css +199 -0
  71. data/skel/skin/css/coax.less +244 -0
  72. data/skel/skin/examples/blog/20091214_0001.yaml +8 -0
  73. data/skel/skin/examples/blog/20100630_0001.yaml +8 -0
  74. data/skel/skin/examples/blog/20100630_0002.yaml +14 -0
  75. data/skel/skin/examples/blog/20100701_0001.yaml +8 -0
  76. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f.jpg +0 -0
  77. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f_small.jpg +0 -0
  78. data/skel/skin/examples/blog/20100701_0002.yaml +19 -0
  79. data/skel/skin/examples/blog/frank/20100701_0001.yaml +10 -0
  80. data/skel/skin/examples/blog/frank/index.yaml +4 -0
  81. data/skel/skin/examples/blog/index.html +51 -0
  82. data/skel/skin/examples/blog/rss.xml +18 -0
  83. data/skel/skin/examples/contact/20100701_0001-file.txt +1 -0
  84. data/skel/skin/examples/contact/20100701_0001.yaml +15 -0
  85. data/skel/skin/examples/contact/20100701_0002.yaml +8 -0
  86. data/skel/skin/examples/contact/20100701_0003.yaml +9 -0
  87. data/skel/skin/examples/contact/index.html +47 -0
  88. data/skel/skin/examples/contact/js/contact.js +13 -0
  89. data/skel/skin/examples/contact/summary.html +54 -0
  90. data/skel/skin/examples/forum/20100701_0001.yaml +41 -0
  91. data/skel/skin/examples/forum/20100701_0002.yaml +25 -0
  92. data/skel/skin/examples/forum/index.html +68 -0
  93. data/skel/skin/examples/forum/summary.html +47 -0
  94. data/skel/skin/examples/index.html +75 -0
  95. data/skel/skin/index.html +41 -0
  96. data/skel/skin/js/base.js +50 -0
  97. data/t/locale/de/index.po +19 -0
  98. data/t/locale/en-GB/index.po +25 -0
  99. data/t/locale/ja/index.po +30 -0
  100. data/t/skin/_users/00000000_test.yaml +3 -0
  101. data/t/skin/_users/index.html +13 -0
  102. data/t/skin/foo/20091120_0001.yaml +7 -0
  103. data/t/skin/foo/bar/20091120_0001.yaml +5 -0
  104. data/t/skin/foo/bar/index.yaml +5 -0
  105. data/t/skin/foo/baz/css/baz.css +1 -0
  106. data/t/skin/foo/css/foo.css +1 -0
  107. data/t/skin/foo/index.html +14 -0
  108. data/t/skin/foo/index.yaml +7 -0
  109. data/t/skin/foo/not_css/foo.css +1 -0
  110. data/t/skin/foo/sub-20100306_0001.yaml +3 -0
  111. data/t/skin/index.yaml +3 -0
  112. data/t/skin/t_attachment/index.html +13 -0
  113. data/t/skin/t_contact/done.html +6 -0
  114. data/t/skin/t_contact/index.html +9 -0
  115. data/t/skin/t_file/index.html +16 -0
  116. data/t/skin/t_img/index.html +14 -0
  117. data/t/skin/t_img/test.jpg +0 -0
  118. data/t/skin/t_select/index.html +9 -0
  119. data/t/skin/t_store/index.html +9 -0
  120. data/t/skin/t_summary/20100326_0001.yaml +3 -0
  121. data/t/skin/t_summary/create.html +9 -0
  122. data/t/skin/t_summary/index.html +9 -0
  123. data/t/skin/t_summary/summary.html +9 -0
  124. data/t/t.rb +27 -0
  125. data/t/test_checkbox.rb +273 -0
  126. data/t/test_field.rb +330 -0
  127. data/t/test_file.rb +900 -0
  128. data/t/test_id.rb +215 -0
  129. data/t/test_img.rb +328 -0
  130. data/t/test_meta.rb +57 -0
  131. data/t/test_parser.rb +1266 -0
  132. data/t/test_password.rb +188 -0
  133. data/t/test_radio.rb +226 -0
  134. data/t/test_role.rb +249 -0
  135. data/t/test_runo.rb +742 -0
  136. data/t/test_runo_call.rb +1286 -0
  137. data/t/test_runo_i18n.rb +318 -0
  138. data/t/test_select.rb +182 -0
  139. data/t/test_set_complex.rb +527 -0
  140. data/t/test_set_dynamic.rb +1504 -0
  141. data/t/test_set_folder.rb +515 -0
  142. data/t/test_set_permit.rb +246 -0
  143. data/t/test_set_static.rb +445 -0
  144. data/t/test_storage.rb +915 -0
  145. data/t/test_text.rb +125 -0
  146. data/t/test_textarea.rb +138 -0
  147. data/t/test_timestamp.rb +473 -0
  148. data/t/test_workflow.rb +367 -0
  149. metadata +345 -0
@@ -0,0 +1,57 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ require "#{::File.dirname __FILE__}/t"
7
+
8
+ class TC_Meta < Test::Unit::TestCase
9
+
10
+ def setup
11
+ end
12
+
13
+ def teardown
14
+ end
15
+
16
+ def test_owner
17
+ ss = Runo::Set::Static.new(
18
+ :item => {
19
+ '_owner' => {:klass => 'meta-owner'},
20
+ }
21
+ )
22
+ assert_instance_of(
23
+ Runo::Meta::Owner,
24
+ ss.item('_owner'),
25
+ "Set::Static#item('_owner') should be an instance of the meta field"
26
+ )
27
+
28
+ ss.item('_owner').load 'frank'
29
+ assert_equal(
30
+ 'frank',
31
+ ss.val('_owner'),
32
+ 'Meta::Owner#load should work like normal fields'
33
+ )
34
+ assert_equal(
35
+ 'frank',
36
+ ss[:owner],
37
+ 'Meta::Owner#load should update the [:owner] of the parent set'
38
+ )
39
+
40
+ ss.item('_owner').update 'carl'
41
+ assert_equal(
42
+ 'frank',
43
+ ss.val('_owner'),
44
+ 'Meta::Owner should not be updated'
45
+ )
46
+ assert(
47
+ !ss.item('_owner').pending?,
48
+ 'Meta::Owner should not be updated'
49
+ )
50
+ assert_equal(
51
+ 'frank',
52
+ ss[:owner],
53
+ 'Meta::Owner should not be updated'
54
+ )
55
+ end
56
+
57
+ end
@@ -0,0 +1,1266 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ require "#{::File.dirname __FILE__}/t"
7
+
8
+ class TC_Parser < Test::Unit::TestCase
9
+
10
+ def setup
11
+ end
12
+
13
+ def teardown
14
+ end
15
+
16
+ def test_scan_tokens
17
+ assert_equal(
18
+ {:tokens => ['foo', 'bar', 'baz']},
19
+ Runo::Parser.scan_tokens(StringScanner.new('foo bar baz')),
20
+ 'Parser.scan_tokens should be able to parse unquoted tokens into array'
21
+ )
22
+ assert_equal(
23
+ {:tokens => ['foo', 'bar', 'baz baz']},
24
+ Runo::Parser.scan_tokens(StringScanner.new('foo "bar" "baz baz"')),
25
+ 'Parser.scan_tokens should be able to parse quoted tokens'
26
+ )
27
+ assert_equal(
28
+ {:tokens => ['foo', 'bar', 'baz']},
29
+ Runo::Parser.scan_tokens(StringScanner.new("foo 'bar' baz")),
30
+ 'Parser.scan_tokens should be able to parse quoted tokens'
31
+ )
32
+
33
+ assert_equal(
34
+ {:tokens => ['foo', 'bar', 'baz']},
35
+ Runo::Parser.scan_tokens(StringScanner.new("foo 'bar' baz) qux")),
36
+ 'Parser.scan_tokens should stop scanning at an ending bracket'
37
+ )
38
+ assert_equal(
39
+ {:tokens => ['foo', 'bar (bar?)', 'baz']},
40
+ Runo::Parser.scan_tokens(StringScanner.new("foo 'bar (bar?)' baz) qux")),
41
+ 'Parser.scan_tokens should ignore brackets inside quoted tokens'
42
+ )
43
+ end
44
+
45
+ def test_parse_empty_tag
46
+ result = Runo::Parser.parse_html('hello $(foo = bar "baz baz") world')
47
+ assert_equal(
48
+ {'foo' => {:klass => 'bar', :tokens => ['baz baz']}},
49
+ result[:item],
50
+ 'Parser.parse_html should be able to parse empty runo tags'
51
+ )
52
+ assert_equal(
53
+ {:index => 'hello $(foo) world'},
54
+ result[:tmpl],
55
+ 'Parser.parse_html[:tmpl] should be a proper template'
56
+ )
57
+
58
+ result = Runo::Parser.parse_html <<'_html'
59
+ <h1>$(foo=bar "baz baz")</h1>
60
+ <p>$(bar=a b c)</p>
61
+ _html
62
+ assert_equal(
63
+ {
64
+ 'foo' => {:klass => 'bar', :tokens => ['baz baz']},
65
+ 'bar' => {:klass => 'a', :tokens => ['b', 'c']},
66
+ },
67
+ result[:item],
68
+ 'Parser.parse_html should be able to parse empty runo tags'
69
+ )
70
+ assert_equal(
71
+ {:index => <<'_html'},
72
+ <h1>$(foo)</h1>
73
+ <p>$(bar)</p>
74
+ _html
75
+ result[:tmpl],
76
+ 'Parser.parse_html[:tmpl] should be a proper template'
77
+ )
78
+ end
79
+
80
+ def test_obscure_markup
81
+ result = Runo::Parser.parse_html('hello $(foo = bar $(baz=1) baz) world')
82
+ assert_equal(
83
+ {'foo' => {:klass => 'bar', :tokens => ['$(baz=1']}},
84
+ result[:item],
85
+ 'Parser.parse_html should not parse nested empty tag'
86
+ )
87
+ assert_equal(
88
+ {:index => 'hello $(foo) baz) world'},
89
+ result[:tmpl],
90
+ 'Parser.parse_html[:tmpl] should be a proper template'
91
+ )
92
+
93
+ result = Runo::Parser.parse_html('hello $(foo = bar baz world')
94
+ assert_equal(
95
+ {'foo' => {:klass => 'bar', :tokens => ['baz', 'world']}},
96
+ result[:item],
97
+ 'Parser.parse_html should be able to parse a tag that is not closed'
98
+ )
99
+ assert_equal(
100
+ {:index => 'hello $(foo)'},
101
+ result[:tmpl],
102
+ 'Parser.parse_html should be able to parse a tag that is not closed'
103
+ )
104
+
105
+ result = Runo::Parser.parse_html('hello $(foo = bar "baz"world)')
106
+ assert_equal(
107
+ {'foo' => {:klass => 'bar', :tokens => ['baz', 'world']}},
108
+ result[:item],
109
+ 'Parser.parse_html should be able to parse tokens without a delimiter'
110
+ )
111
+ assert_equal(
112
+ {:index => 'hello $(foo)'},
113
+ result[:tmpl],
114
+ 'Parser.parse_html should be able to parse tokens without a delimiter'
115
+ )
116
+
117
+ result = Runo::Parser.parse_html('hello $(foo = bar, "baz")')
118
+ assert_equal(
119
+ {'foo' => {:klass => 'bar', :options => ['baz']}},
120
+ result[:item],
121
+ 'The first token should be regarded as [:klass]'
122
+ )
123
+ end
124
+
125
+ def test_parse_token
126
+ assert_equal(
127
+ {:width => 160, :height => 120},
128
+ Runo::Parser.parse_token(nil, '160*120', {}),
129
+ 'Parser.parse_token should be able to parse dimension tokens'
130
+ )
131
+ assert_equal(
132
+ {:min => 1, :max => 32},
133
+ Runo::Parser.parse_token(nil, '1..32', {}),
134
+ 'Parser.parse_token should be able to parse range tokens'
135
+ )
136
+ assert_equal(
137
+ {:max => 32},
138
+ Runo::Parser.parse_token(nil, '..32', {}),
139
+ 'Parser.parse_token should be able to parse partial range tokens'
140
+ )
141
+ assert_equal(
142
+ {:min => 1},
143
+ Runo::Parser.parse_token(nil, '1..', {}),
144
+ 'Parser.parse_token should be able to parse partial range tokens'
145
+ )
146
+ assert_equal(
147
+ {:min => -32, :max => -1},
148
+ Runo::Parser.parse_token(nil, '-32..-1', {}),
149
+ 'Parser.parse_token should be able to parse minus range tokens'
150
+ )
151
+
152
+ assert_equal(
153
+ {:options => ['foo']},
154
+ Runo::Parser.parse_token(',', 'foo', {}),
155
+ 'Parser.parse_token should be able to parse option tokens'
156
+ )
157
+ assert_equal(
158
+ {:options => ['foo', 'bar']},
159
+ Runo::Parser.parse_token(',', 'bar', {:options => ['foo']}),
160
+ 'Parser.parse_token should be able to parse option tokens'
161
+ )
162
+
163
+ assert_equal(
164
+ {:default => 'bar'},
165
+ Runo::Parser.parse_token(':', 'bar', {}),
166
+ 'Parser.parse_token should be able to parse default tokens'
167
+ )
168
+ assert_equal(
169
+ {:defaults => ['bar', 'baz']},
170
+ Runo::Parser.parse_token(';', 'baz', {:defaults => ['bar']}),
171
+ 'Parser.parse_token should be able to parse defaults tokens'
172
+ )
173
+ end
174
+
175
+ def test_parse_options
176
+ result = Runo::Parser.parse_html('hello $(foo = bar , "baz baz", "world", hi qux)')
177
+ assert_equal(
178
+ {'foo' => {:klass => 'bar', :options => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
179
+ result[:item],
180
+ 'Parser.parse_html should be able to parse a sequence of CSV'
181
+ )
182
+ result = Runo::Parser.parse_html('hello $(foo = bar "baz baz", "world", hi qux)')
183
+ assert_equal(
184
+ {'foo' => {:klass => 'bar', :options => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
185
+ result[:item],
186
+ 'Parser.parse_html should be able to parse a sequence of CSV'
187
+ )
188
+ end
189
+
190
+ def test_parse_options_with_spaces
191
+ result = Runo::Parser.parse_html('hello $(foo = bar world, qux)')
192
+ assert_equal(
193
+ {'foo' => {:klass => 'bar', :options => ['world', 'qux']}},
194
+ result[:item],
195
+ 'Parser.parse_html should allow spaces after the comma'
196
+ )
197
+ result = Runo::Parser.parse_html('hello $(foo = bar world , qux)')
198
+ assert_equal(
199
+ {'foo' => {:klass => 'bar', :options => ['qux'], :tokens => ['world']}},
200
+ result[:item],
201
+ 'Parser.parse_html should not allow spaces before the comma'
202
+ )
203
+ result = Runo::Parser.parse_html('hello $(foo = bar "baz baz", "world", hi qux)')
204
+ assert_equal(
205
+ {'foo' => {:klass => 'bar', :options => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
206
+ result[:item],
207
+ 'Parser.parse_html should allow spaces after the comma'
208
+ )
209
+
210
+ result = Runo::Parser.parse_html(<<'_eos')
211
+ hello $(foo =
212
+ bar
213
+ "baz baz",
214
+ "world",
215
+ hi
216
+ qux)
217
+ _eos
218
+ assert_equal(
219
+ {'foo' => {:klass => 'bar', :options => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
220
+ result[:item],
221
+ 'Parser.parse_html should allow spaces after the comma'
222
+ )
223
+ end
224
+
225
+ def test_parse_defaults
226
+ result = Runo::Parser.parse_html('hello $(foo = bar ;"baz baz";"world";hi qux)')
227
+ assert_equal(
228
+ {'foo' => {:klass => 'bar', :defaults => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
229
+ result[:item],
230
+ 'Parser.parse_html should be able to parse a sequence of CSV as [:defaults]'
231
+ )
232
+ result = Runo::Parser.parse_html('hello $(foo = bar "baz baz";"world";hi qux)')
233
+ assert_equal(
234
+ {'foo' => {:klass => 'bar', :defaults => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
235
+ result[:item],
236
+ 'Parser.parse_html should be able to parse a sequence of CSV as [:defaults]'
237
+ )
238
+ end
239
+
240
+ def test_parse_defaults_with_spaces
241
+ result = Runo::Parser.parse_html('hello $(foo=bar world; qux)')
242
+ assert_equal(
243
+ {'foo' => {:klass => 'bar', :defaults => ['world', 'qux']}},
244
+ result[:item],
245
+ 'Parser.parse_html should allow spaces after the semicolon'
246
+ )
247
+ result = Runo::Parser.parse_html('hello $(foo=bar world ;qux)')
248
+ assert_equal(
249
+ {'foo' => {:klass => 'bar', :defaults => ['qux'], :tokens => ['world']}},
250
+ result[:item],
251
+ 'Parser.parse_html should not allow spaces before the semicolon'
252
+ )
253
+ result = Runo::Parser.parse_html('hello $(foo=bar "baz baz"; "world"; hi qux)')
254
+ assert_equal(
255
+ {'foo' => {:klass => 'bar', :defaults => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
256
+ result[:item],
257
+ 'Parser.parse_html should allow spaces after the comma'
258
+ )
259
+
260
+ result = Runo::Parser.parse_html(<<'_eos')
261
+ hello $(foo =
262
+ bar
263
+ "baz baz";
264
+ "world";
265
+ hi
266
+ qux)
267
+ _eos
268
+ assert_equal(
269
+ {'foo' => {:klass => 'bar', :defaults => ['baz baz', 'world', 'hi'], :tokens => ['qux']}},
270
+ result[:item],
271
+ 'Parser.parse_html should allow spaces after the comma'
272
+ )
273
+ end
274
+
275
+ def test_parse_meta_tag
276
+ result = Runo::Parser.parse_html <<'_html'
277
+ <html>
278
+ <meta name="runo-owner" content="frank" />
279
+ </html>
280
+ _html
281
+ assert_equal(
282
+ {
283
+ :tmpl => {
284
+ :index => <<'_html',
285
+ <html>
286
+ </html>
287
+ _html
288
+ },
289
+ :item => {},
290
+ :owner => 'frank',
291
+ :label => nil,
292
+ },
293
+ result,
294
+ 'Parser.parse_html should scrape meta vals from <meta>'
295
+ )
296
+
297
+ result = Runo::Parser.parse_html <<'_html'
298
+ <html>
299
+ <meta name="runo-owner" content="frank" />
300
+ <meta name="runo-group" content="bob,carl" />
301
+ <meta name="runo-foo" content="bar, baz" />
302
+ <meta name="runo-label" content="Qux" />
303
+ </html>
304
+ _html
305
+ assert_equal(
306
+ {
307
+ :tmpl => {
308
+ :index => <<'_html',
309
+ <html>
310
+ </html>
311
+ _html
312
+ },
313
+ :item => {},
314
+ :owner => 'frank',
315
+ :group => %w(bob carl),
316
+ :foo => %w(bar baz),
317
+ :label => 'Qux',
318
+ },
319
+ result,
320
+ 'Parser.parse_html should scrape meta vals from <meta>'
321
+ )
322
+ end
323
+
324
+ def test_parse_duplicate_tag
325
+ result = Runo::Parser.parse_html('hello $(foo = bar "baz baz") world $(foo=boo) $(foo)!')
326
+ assert_equal(
327
+ {'foo' => {:klass => 'boo'}},
328
+ result[:item],
329
+ 'definition tags are overridden by a preceding definition'
330
+ )
331
+ assert_equal(
332
+ {:index => 'hello $(foo) world $(foo) $(foo)!'},
333
+ result[:tmpl],
334
+ 'Parser.parse_html[:tmpl] should be a proper template'
335
+ )
336
+ end
337
+
338
+ def test_scan_inner_html
339
+ s = StringScanner.new 'bar</foo>bar'
340
+ inner_html, close_tag = Runo::Parser.scan_inner_html(s, 'foo')
341
+ assert_equal(
342
+ 'bar',
343
+ inner_html,
344
+ 'Parser.scan_inner_html should extract the inner html from the scanner'
345
+ )
346
+ assert_equal(
347
+ '</foo>',
348
+ close_tag,
349
+ 'Parser.scan_inner_html should extract the inner html from the scanner'
350
+ )
351
+
352
+ s = StringScanner.new '<foo>bar</foo></foo>'
353
+ inner_html, close_tag = Runo::Parser.scan_inner_html(s, 'foo')
354
+ assert_equal(
355
+ '<foo>bar</foo>',
356
+ inner_html,
357
+ 'Parser.scan_inner_html should be aware of nested tags'
358
+ )
359
+
360
+ s = StringScanner.new "baz\n <foo>bar</foo>\n</foo>"
361
+ inner_html, close_tag = Runo::Parser.scan_inner_html(s, 'foo')
362
+ assert_equal(
363
+ "baz\n <foo>bar</foo>\n",
364
+ inner_html,
365
+ 'Parser.scan_inner_html should be aware of nested tags'
366
+ )
367
+ end
368
+
369
+ def test_parse_block_tag
370
+ result = Runo::Parser.parse_html <<'_html'
371
+ <ul class="runo-blog" id="foo"><li>hello</li></ul>
372
+ _html
373
+ assert_equal(
374
+ {
375
+ 'foo' => {
376
+ :klass => 'set-dynamic',
377
+ :workflow => 'blog',
378
+ :tmpl => {
379
+ :index => <<'_tmpl'.chomp,
380
+ <ul class="runo-blog" id="@(name)">$()</ul>
381
+ $(.navi)$(.submit)$(.action_create)
382
+ _tmpl
383
+ },
384
+ :item => {
385
+ 'default' => {
386
+ :label => nil,
387
+ :tmpl => {:index => '<li>hello</li>'},
388
+ :item => {},
389
+ },
390
+ },
391
+ },
392
+ },
393
+ result[:item],
394
+ 'Parser.parse_html should be able to parse block runo tags'
395
+ )
396
+ assert_equal(
397
+ {:index => '$(foo.message)$(foo)'},
398
+ result[:tmpl],
399
+ 'Parser.parse_html[:tmpl] should be a proper template'
400
+ )
401
+
402
+ result = Runo::Parser.parse_html <<'_html'
403
+ <ul class="runo-blog" id="foo">
404
+ <li>hello</li>
405
+ </ul>
406
+ _html
407
+ assert_equal(
408
+ {
409
+ 'foo' => {
410
+ :klass => 'set-dynamic',
411
+ :workflow => 'blog',
412
+ :tmpl => {
413
+ :index => <<'_tmpl'.chomp,
414
+ <ul class="runo-blog" id="@(name)">
415
+ $()</ul>
416
+ $(.navi)$(.submit)$(.action_create)
417
+ _tmpl
418
+ },
419
+ :item => {
420
+ 'default' => {
421
+ :label => nil,
422
+ :tmpl => {:index => " <li>hello</li>\n"},
423
+ :item => {},
424
+ },
425
+ }
426
+ },
427
+ },
428
+ result[:item],
429
+ 'Parser.parse_html should be able to parse block runo tags'
430
+ )
431
+ assert_equal(
432
+ {:index => '$(foo.message)$(foo)'},
433
+ result[:tmpl],
434
+ 'Parser.parse_html[:tmpl] should be a proper template'
435
+ )
436
+
437
+ result = Runo::Parser.parse_html <<'_html'
438
+ hello <ul class="runo-blog" id="foo"><li>hello</li></ul> world
439
+ _html
440
+ assert_equal(
441
+ {
442
+ 'foo' => {
443
+ :klass => 'set-dynamic',
444
+ :workflow => 'blog',
445
+ :tmpl => {
446
+ :index => <<'_tmpl'.chomp,
447
+ <ul class="runo-blog" id="@(name)">$()</ul>$(.navi)$(.submit)$(.action_create)
448
+ _tmpl
449
+ },
450
+ :item => {
451
+ 'default' => {
452
+ :label => nil,
453
+ :tmpl => {:index => '<li>hello</li>'},
454
+ :item => {},
455
+ },
456
+ },
457
+ },
458
+ },
459
+ result[:item],
460
+ 'Parser.parse_html should be able to parse block runo tags'
461
+ )
462
+ assert_equal(
463
+ {:index => <<'_html'},
464
+ hello$(foo.message)$(foo) world
465
+ _html
466
+ result[:tmpl],
467
+ 'Parser.parse_html[:tmpl] should be a proper template'
468
+ )
469
+ end
470
+
471
+ def test_look_a_like_block_tag
472
+ result = Runo::Parser.parse_html <<'_html'
473
+ hello <ul class="not-runo-blog" id="foo"><li>hello</li></ul> world
474
+ _html
475
+ assert_equal(
476
+ {:index => <<'_tmpl'},
477
+ hello <ul class="not-runo-blog" id="foo"><li>hello</li></ul> world
478
+ _tmpl
479
+ result[:tmpl],
480
+ "Parser.parse_html[:tmpl] should skip a class which does not start with 'runo'"
481
+ )
482
+ end
483
+
484
+ def test_block_tags_with_options
485
+ result = Runo::Parser.parse_html <<'_html'
486
+ hello
487
+ <table class="runo-blog" id="foo">
488
+ <!-- 1..20 barbaz -->
489
+ <tbody class="body"><!-- qux --><tr><th>$(bar=text)</th><th>$(baz=text)</th></tr></tbody>
490
+ </table>
491
+ world
492
+ _html
493
+ assert_equal(
494
+ {
495
+ 'foo' => {
496
+ :min => 1,
497
+ :max => 20,
498
+ :tokens => ['barbaz'],
499
+ :klass => 'set-dynamic',
500
+ :workflow => 'blog',
501
+ :tmpl => {
502
+ :index => <<'_tmpl'.chomp,
503
+ <table class="runo-blog" id="@(name)">
504
+ <!-- 1..20 barbaz -->
505
+ $() </table>
506
+ $(.navi)$(.submit)$(.action_create)
507
+ _tmpl
508
+ },
509
+ :item => {
510
+ 'default' => {
511
+ :label => nil,
512
+ :tmpl => {
513
+ :index => <<'_tmpl',
514
+ <tbody class="body"><!-- qux --><tr><th>$(.a_update)$(bar)</a></th><th>$(baz)$(.hidden)</th></tr></tbody>
515
+ _tmpl
516
+ },
517
+ :item => {
518
+ 'bar' => {:klass => 'text'},
519
+ 'baz' => {:klass => 'text'},
520
+ },
521
+ },
522
+ },
523
+ },
524
+ },
525
+ result[:item],
526
+ 'Parser.parse_html should aware of <tbody class="body">'
527
+ )
528
+ end
529
+
530
+ def test_block_tags_with_tbody
531
+ result = Runo::Parser.parse_html <<'_html'
532
+ hello
533
+ <table class="runo-blog" id="foo">
534
+ <thead><tr><th>BAR</th><th>BAZ</th></tr></thead>
535
+ <tbody class="body"><tr><th>$(bar=text)</th><th>$(baz=text)</th></tr></tbody>
536
+ </table>
537
+ world
538
+ _html
539
+ assert_equal(
540
+ {
541
+ 'foo' => {
542
+ :klass => 'set-dynamic',
543
+ :workflow => 'blog',
544
+ :tmpl => {
545
+ :index => <<'_tmpl'.chomp,
546
+ <table class="runo-blog" id="@(name)">
547
+ <thead><tr><th>BAR</th><th>BAZ</th></tr></thead>
548
+ $() </table>
549
+ $(.navi)$(.submit)$(.action_create)
550
+ _tmpl
551
+ },
552
+ :item => {
553
+ 'default' => {
554
+ :label => nil,
555
+ :tmpl => {
556
+ :index => <<'_tmpl',
557
+ <tbody class="body"><tr><th>$(.a_update)$(bar)</a></th><th>$(baz)$(.hidden)</th></tr></tbody>
558
+ _tmpl
559
+ },
560
+ :item => {
561
+ 'bar' => {:klass => 'text'},
562
+ 'baz' => {:klass => 'text'},
563
+ },
564
+ },
565
+ },
566
+ },
567
+ },
568
+ result[:item],
569
+ 'Parser.parse_html should aware of <tbody class="body">'
570
+ )
571
+ assert_equal(
572
+ {:index => <<'_tmpl'},
573
+ hello
574
+ $(foo.message)$(foo)world
575
+ _tmpl
576
+ result[:tmpl],
577
+ 'Parser.parse_html[:tmpl] should be a proper template'
578
+ )
579
+ end
580
+
581
+ def test_parse_xml
582
+ result = Runo::Parser.parse_xml <<'_html'
583
+ <channel class="runo-rss">
584
+ <link>@(href)</link>
585
+ <item class="body">
586
+ <title>$(title)</title>
587
+ </item>
588
+ </channel>
589
+ _html
590
+ assert_equal(
591
+ {
592
+ :label => nil,
593
+ :tmpl => {:index => '$(main)'},
594
+ :item => {
595
+ 'main' => {
596
+ :item => {
597
+ 'default' => {
598
+ :label => nil,
599
+ :item => {},
600
+ :tmpl => {
601
+ :index => <<'_xml'
602
+ <item>
603
+ <title>$(title)</title>
604
+ </item>
605
+ _xml
606
+ },
607
+ },
608
+ },
609
+ :tmpl => {
610
+ :index => <<'_xml',
611
+ <channel>
612
+ <link>@(href)</link>
613
+ $()</channel>
614
+ _xml
615
+ },
616
+ :klass => 'set-dynamic',
617
+ :workflow => 'rss',
618
+ }
619
+ },
620
+ },
621
+ result,
622
+ 'Parser.parse_html should aware of <item>'
623
+ )
624
+ end
625
+
626
+ def test_parse_item_label
627
+ result = Runo::Parser.parse_html <<'_html'
628
+ <ul class="runo-blog" id="foo"><li title="Greeting">hello</li></ul>
629
+ _html
630
+ assert_equal(
631
+ 'Greeting',
632
+ result[:item]['foo'][:item]['default'][:label],
633
+ 'Parser.parse_html should pick up item labels from title attrs'
634
+ )
635
+
636
+ result = Runo::Parser.parse_html <<'_html'
637
+ <ul class="runo-blog" id="foo"><!-- foo --><li title="Greeting">hello</li></ul>
638
+ _html
639
+ assert_equal(
640
+ 'Greeting',
641
+ result[:item]['foo'][:item]['default'][:label],
642
+ 'Parser.parse_html should pick up item labels from title attrs'
643
+ )
644
+
645
+ result = Runo::Parser.parse_html <<'_html'
646
+ <ul class="runo-blog" id="foo"><!-- foo --><li><div title="Foo">hello</div></li></ul>
647
+ _html
648
+ assert_nil(
649
+ result[:item]['foo'][:item]['default'][:label],
650
+ 'Parser.parse_html should pick up item labels only from the first tags'
651
+ )
652
+ end
653
+
654
+ def test_parse_item_label_plural
655
+ result = Runo::Parser.parse_html <<'_html'
656
+ <ul class="runo-blog" id="foo"><li title="tEntry, tEntries">hello</li></ul>
657
+ _html
658
+ assert_equal(
659
+ 'tEntry',
660
+ result[:item]['foo'][:item]['default'][:label],
661
+ 'Parser.parse_html should split plural item labels'
662
+ )
663
+ assert_equal(
664
+ ['tEntry', 'tEntries'],
665
+ Runo::I18n.msg['tEntry'],
666
+ 'Parser.parse_html should I18n.merge_msg! the plural item labels'
667
+ )
668
+
669
+ result = Runo::Parser.parse_html <<'_html'
670
+ <ul class="runo-blog" id="foo"><li title="tFooFoo, BarBar, BazBaz">hello</li></ul>
671
+ _html
672
+ assert_equal(
673
+ 'tFooFoo',
674
+ result[:item]['foo'][:item]['default'][:label],
675
+ 'Parser.parse_html should split plural item labels'
676
+ )
677
+ assert_equal(
678
+ ['tFooFoo', 'BarBar', 'BazBaz'],
679
+ Runo::I18n.msg['tFooFoo'],
680
+ 'Parser.parse_html should I18n.merge_msg! the plural item labels'
681
+ )
682
+
683
+ result = Runo::Parser.parse_html <<'_html'
684
+ <ul class="runo-blog" id="foo"><li title="tQux">hello</li></ul>
685
+ _html
686
+ assert_equal(
687
+ 'tQux',
688
+ result[:item]['foo'][:item]['default'][:label],
689
+ 'Parser.parse_html should split plural item labels'
690
+ )
691
+ assert_equal(
692
+ ['tQux', 'tQux', 'tQux', 'tQux'],
693
+ Runo::I18n.msg['tQux'],
694
+ 'Parser.parse_html should repeat a singular label to fill all possible plural forms'
695
+ )
696
+ end
697
+
698
+ def test_block_tags_with_nested_tbody
699
+ result = Runo::Parser.parse_html <<'_html'
700
+ hello
701
+ <table class="runo-blog" id="foo">
702
+ <thead><tr><th>BAR</th><th>BAZ</th></tr></thead>
703
+ <tbody class="body"><tbody><tr><th>$(bar=text)</th><th>$(baz=text)</th></tr></tbody></tbody>
704
+ </table>
705
+ world
706
+ _html
707
+ assert_equal(
708
+ {
709
+ 'foo' => {
710
+ :klass => 'set-dynamic',
711
+ :workflow => 'blog',
712
+ :tmpl => {
713
+ :index => <<'_tmpl'.chomp,
714
+ <table class="runo-blog" id="@(name)">
715
+ <thead><tr><th>BAR</th><th>BAZ</th></tr></thead>
716
+ $() </table>
717
+ $(.navi)$(.submit)$(.action_create)
718
+ _tmpl
719
+ },
720
+ :item => {
721
+ 'default' => {
722
+ :label => nil,
723
+ :tmpl => {
724
+ :index => <<'_tmpl',
725
+ <tbody class="body"><tbody><tr><th>$(.a_update)$(bar)</a></th><th>$(baz)$(.hidden)</th></tr></tbody></tbody>
726
+ _tmpl
727
+ },
728
+ :item => {
729
+ 'bar' => {:klass => 'text'},
730
+ 'baz' => {:klass => 'text'},
731
+ },
732
+ },
733
+ },
734
+ },
735
+ },
736
+ result[:item],
737
+ 'Parser.parse_html should aware of nested <tbody class="body">'
738
+ )
739
+ end
740
+
741
+ def test_nested_block_tags
742
+ result = Runo::Parser.parse_html <<'_html'
743
+ <ul class="runo-blog" id="foo">
744
+ <li>
745
+ <ul class="runo-blog" id="bar"><li>baz</li></ul>
746
+ </li>
747
+ </ul>
748
+ _html
749
+ assert_equal(
750
+ {
751
+ 'foo' => {
752
+ :klass => 'set-dynamic',
753
+ :workflow => 'blog',
754
+ :tmpl => {
755
+ :index => <<'_tmpl'.chomp,
756
+ <ul class="runo-blog" id="@(name)">
757
+ $()</ul>
758
+ $(.navi)$(.submit)$(.action_create)
759
+ _tmpl
760
+ },
761
+ :item => {
762
+ 'default' => {
763
+ :label => nil,
764
+ :tmpl => {
765
+ :index => <<'_tmpl',
766
+ <li>
767
+ $(bar.message)$(.a_update)$(bar)$(.hidden)</a> </li>
768
+ _tmpl
769
+ },
770
+ :item => {
771
+ 'bar' => {
772
+ :klass => 'set-dynamic',
773
+ :workflow => 'blog',
774
+ :tmpl => {
775
+ :index => <<'_tmpl'.chomp,
776
+ <ul class="runo-blog" id="@(name)">$()</ul>
777
+ $(.navi)$(.submit)$(.action_create)
778
+ _tmpl
779
+ },
780
+ :item => {
781
+ 'default' => {
782
+ :label => nil,
783
+ :tmpl => {:index => '<li>baz</li>'},
784
+ :item => {},
785
+ },
786
+ },
787
+ },
788
+ },
789
+ },
790
+ },
791
+ },
792
+ },
793
+ result[:item],
794
+ 'Parser.parse_html should be able to parse nested block runo tags'
795
+ )
796
+ assert_equal(
797
+ {:index => '$(foo.message)$(foo)'},
798
+ result[:tmpl],
799
+ 'Parser.parse_html[:tmpl] should be a proper template'
800
+ )
801
+ end
802
+
803
+ def test_combination
804
+ result = Runo::Parser.parse_html <<'_html'
805
+ <html>
806
+ <h1>$(title=text 32)</h1>
807
+ <ul id="foo" class="runo-blog">
808
+ <li>
809
+ $(subject=text 64)
810
+ $(body=textarea 72*10)
811
+ <ul><li>qux</li></ul>
812
+ </li>
813
+ </ul>
814
+ </html>
815
+ _html
816
+ assert_equal(
817
+ {
818
+ 'title' => {:klass => 'text', :tokens => ['32']},
819
+ 'foo' => {
820
+ :klass => 'set-dynamic',
821
+ :workflow => 'blog',
822
+ :tmpl => {
823
+ :index => <<'_tmpl'.chomp,
824
+ <ul id="@(name)" class="runo-blog">
825
+ $() </ul>
826
+ $(.navi)$(.submit)$(.action_create)
827
+ _tmpl
828
+ },
829
+ :item => {
830
+ 'default' => {
831
+ :label => nil,
832
+ :tmpl => {
833
+ :index => <<'_tmpl',
834
+ <li>
835
+ $(.a_update)$(subject)</a>
836
+ $(body)$(.hidden)
837
+ <ul><li>qux</li></ul>
838
+ </li>
839
+ _tmpl
840
+ },
841
+ :item => {
842
+ 'body' => {
843
+ :width => 72,
844
+ :height => 10,
845
+ :klass => 'textarea',
846
+ },
847
+ 'subject' => {
848
+ :tokens => ['64'],
849
+ :klass => 'text',
850
+ },
851
+ },
852
+ },
853
+ },
854
+ },
855
+ },
856
+ result[:item],
857
+ 'Parser.parse_html should be able to parse combination of mixed runo tags'
858
+ )
859
+ assert_equal(
860
+ {:index => <<'_tmpl'},
861
+ <html>
862
+ <h1>$(title)</h1>
863
+ $(foo.message)$(foo)</html>
864
+ _tmpl
865
+ result[:tmpl],
866
+ 'Parser.parse_html[:tmpl] should be a proper template'
867
+ )
868
+ end
869
+
870
+ def test_gsub_block
871
+ match = nil
872
+ result = Runo::Parser.gsub_block('a<div class="foo">bar</div>c', 'foo') {|open, inner, close|
873
+ match = [open, inner, close]
874
+ 'b'
875
+ }
876
+ assert_equal(
877
+ 'abc',
878
+ result,
879
+ 'Parser.gsub_block should replace tag blocks of the matching class with the given value'
880
+ )
881
+ assert_equal(
882
+ ['<div class="foo">', 'bar', '</div>'],
883
+ match,
884
+ 'Parser.gsub_block should pass the matching element to its block'
885
+ )
886
+
887
+ result = Runo::Parser.gsub_block('<p><div class="foo">bar</div></p>', 'foo') {|open, inner, close|
888
+ match = [open, inner, close]
889
+ 'b'
890
+ }
891
+ assert_equal(
892
+ '<p>b</p>',
893
+ result,
894
+ 'Parser.gsub_block should replace tag blocks of the matching class with the given value'
895
+ )
896
+ assert_equal(
897
+ ['<div class="foo">', 'bar', '</div>'],
898
+ match,
899
+ 'Parser.gsub_block should pass the matching element to its block'
900
+ )
901
+
902
+ result = Runo::Parser.gsub_block('a<p><div class="foo">bar</div></p>c', 'foo') {|open, inner, close|
903
+ match = [open, inner, close]
904
+ 'b'
905
+ }
906
+ assert_equal(
907
+ 'a<p>b</p>c',
908
+ result,
909
+ 'Parser.gsub_block should replace tag blocks of the matching class with the given value'
910
+ )
911
+ assert_equal(
912
+ ['<div class="foo">', 'bar', '</div>'],
913
+ match,
914
+ 'Parser.gsub_block should pass the matching element to its block'
915
+ )
916
+ end
917
+
918
+ def _test_gsub_action_tmpl(html)
919
+ result = {}
920
+ html = Runo::Parser.gsub_action_tmpl(html) {|id, action, *tmpl|
921
+ result[:id] = id
922
+ result[:action] = action
923
+ result[:tmpl] = tmpl.join
924
+ 'b'
925
+ }
926
+ [result, html]
927
+ end
928
+
929
+ def test_gsub_action_tmpl
930
+ result, html = _test_gsub_action_tmpl 'a<div class="foo-navi">Foo</div>c'
931
+ assert_equal(
932
+ {
933
+ :id => 'foo',
934
+ :action => :navi,
935
+ :tmpl => '<div class="foo-navi">Foo</div>',
936
+ },
937
+ result,
938
+ 'Parser.gsub_action_tmpl should yield action templates'
939
+ )
940
+ assert_equal(
941
+ 'abc',
942
+ html,
943
+ 'Parser.gsub_action_tmpl should replace the action template with a value from the block'
944
+ )
945
+
946
+ result, html = _test_gsub_action_tmpl 'a<div class="bar foo-navi">Foo</div>c'
947
+ assert_equal(
948
+ {
949
+ :id => 'foo',
950
+ :action => :navi,
951
+ :tmpl => '<div class="bar foo-navi">Foo</div>',
952
+ },
953
+ result,
954
+ 'Parser.gsub_action_tmpl should yield action templates'
955
+ )
956
+
957
+ result, html = _test_gsub_action_tmpl 'a<div class="bar foo-navi baz">Foo</div>c'
958
+ assert_equal(
959
+ {
960
+ :id => 'foo',
961
+ :action => :navi,
962
+ :tmpl => '<div class="bar foo-navi baz">Foo</div>',
963
+ },
964
+ result,
965
+ 'Parser.gsub_action_tmpl should yield action templates'
966
+ )
967
+
968
+ result, html = _test_gsub_action_tmpl 'a<div class="bar foo-done baz">Foo</div>c'
969
+ assert_equal(
970
+ {
971
+ :id => 'foo',
972
+ :action => :done,
973
+ :tmpl => '<div class="bar foo-done baz">Foo</div>',
974
+ },
975
+ result,
976
+ 'Parser.gsub_action_tmpl should yield action templates'
977
+ )
978
+ end
979
+
980
+ def test_gsub_action_tmpl_with_empty_id
981
+ result, html = _test_gsub_action_tmpl 'a<div class="navi">Foo</div>c'
982
+ assert_equal(
983
+ {
984
+ :id => nil,
985
+ :action => :navi,
986
+ :tmpl => '<div class="navi">Foo</div>',
987
+ },
988
+ result,
989
+ 'Parser.gsub_action_tmpl should yield action templates'
990
+ )
991
+
992
+ result, html = _test_gsub_action_tmpl 'a<div class="foo navi">Foo</div>c'
993
+ assert_equal(
994
+ {
995
+ :id => nil,
996
+ :action => :navi,
997
+ :tmpl => '<div class="foo navi">Foo</div>',
998
+ },
999
+ result,
1000
+ 'Parser.gsub_action_tmpl should yield action templates'
1001
+ )
1002
+
1003
+ result, html = _test_gsub_action_tmpl 'a<div class="foo navi baz">Foo</div>c'
1004
+ assert_equal(
1005
+ {
1006
+ :id => nil,
1007
+ :action => :navi,
1008
+ :tmpl => '<div class="foo navi baz">Foo</div>',
1009
+ },
1010
+ result,
1011
+ 'Parser.gsub_action_tmpl should yield action templates'
1012
+ )
1013
+ end
1014
+
1015
+ def test_gsub_action_tmpl_with_ambiguous_klass
1016
+ result, html = _test_gsub_action_tmpl 'a<div class="not_navi">Foo</div>c'
1017
+ assert_equal(
1018
+ {},
1019
+ result,
1020
+ 'Parser.gsub_action_tmpl should ignore classes other than action, view, navi or submit'
1021
+ )
1022
+
1023
+ result, html = _test_gsub_action_tmpl 'a<div class="navi_bar">Foo</div>c'
1024
+ assert_equal(
1025
+ {
1026
+ :id => nil,
1027
+ :action => :navi_bar,
1028
+ :tmpl => '<div class="navi_bar">Foo</div>',
1029
+ },
1030
+ result,
1031
+ 'Parser.gsub_action_tmpl should yield an action template if the klass looks like special'
1032
+ )
1033
+ end
1034
+
1035
+ def test_action_tmpl_in_ss
1036
+ result = Runo::Parser.parse_html <<'_html'
1037
+ <html>
1038
+ <ul id="foo" class="runo-blog">
1039
+ <li>$(subject=text)</li>
1040
+ </ul>
1041
+ <div class="foo-navi">bar</div>
1042
+ </html>
1043
+ _html
1044
+ assert_equal(
1045
+ <<'_tmpl',
1046
+ <div class="foo-navi">bar</div>
1047
+ _tmpl
1048
+ result[:item]['foo'][:tmpl][:navi],
1049
+ 'Parser.parse_html should parse action templates in the html'
1050
+ )
1051
+ assert_equal(
1052
+ {:index => <<'_tmpl'},
1053
+ <html>
1054
+ $(foo.message)$(foo)$(foo.navi)</html>
1055
+ _tmpl
1056
+ result[:tmpl],
1057
+ 'Parser.parse_html should replace action templates with proper tags'
1058
+ )
1059
+ end
1060
+
1061
+ def test_action_tmpl_in_ss_with_nil_id
1062
+ result = Runo::Parser.parse_html <<'_html'
1063
+ <html>
1064
+ <ul id="main" class="runo-blog">
1065
+ <li>$(subject=text)</li>
1066
+ </ul>
1067
+ <div class="navi">bar</div>
1068
+ </html>
1069
+ _html
1070
+ assert_equal(
1071
+ <<'_tmpl',
1072
+ <div class="navi">bar</div>
1073
+ _tmpl
1074
+ result[:item]['main'][:tmpl][:navi],
1075
+ "Parser.parse_html should set action templates to item['main'] by default"
1076
+ )
1077
+ assert_equal(
1078
+ {:index => <<'_tmpl'},
1079
+ <html>
1080
+ $(main.message)$(main)$(main.navi)</html>
1081
+ _tmpl
1082
+ result[:tmpl],
1083
+ "Parser.parse_html should set action templates to item['main'] by default"
1084
+ )
1085
+ end
1086
+
1087
+ def test_action_tmpl_in_ss_with_non_existent_id
1088
+ result = Runo::Parser.parse_html <<'_html'
1089
+ <html>
1090
+ <ul id="main" class="runo-blog">
1091
+ <li>$(subject=text)</li>
1092
+ </ul>
1093
+ <div class="non_existent-navi">bar</div>
1094
+ </html>
1095
+ _html
1096
+ assert_nil(
1097
+ result[:item]['non_existent'],
1098
+ 'Parser.parse_html should ignore the action template without a corresponding SD'
1099
+ )
1100
+ assert_equal(
1101
+ {:index => <<'_tmpl'},
1102
+ <html>
1103
+ $(main.message)$(main) <div class="non_existent-navi">bar</div>
1104
+ </html>
1105
+ _tmpl
1106
+ result[:tmpl],
1107
+ 'Parser.parse_html should ignore the action template without a corresponding SD'
1108
+ )
1109
+ end
1110
+
1111
+ def test_action_tmpl_in_ss_with_nested_action_tmpl
1112
+ result = Runo::Parser.parse_html <<'_html'
1113
+ <html>
1114
+ <ul id="foo" class="runo-blog">
1115
+ <li>$(subject=text)</li>
1116
+ </ul>
1117
+ <div class="foo-navi"><span class="navi_prev">prev</span></div>
1118
+ </html>
1119
+ _html
1120
+ assert_equal(
1121
+ <<'_html',
1122
+ <div class="foo-navi">$(.navi_prev)</div>
1123
+ _html
1124
+ result[:item]['foo'][:tmpl][:navi],
1125
+ 'Parser.parse_html should parse nested action templates'
1126
+ )
1127
+ assert_equal(
1128
+ '<span class="navi_prev">prev</span>',
1129
+ result[:item]['foo'][:tmpl][:navi_prev],
1130
+ 'Parser.parse_html should parse nested action templates'
1131
+ )
1132
+ assert_equal(
1133
+ {
1134
+ :index => <<'_html'.chomp,
1135
+ <ul id="@(name)" class="runo-blog">
1136
+ $() </ul>
1137
+ $(.submit)$(.action_create)
1138
+ _html
1139
+ :navi => <<'_html',
1140
+ <div class="foo-navi">$(.navi_prev)</div>
1141
+ _html
1142
+ :navi_prev => '<span class="navi_prev">prev</span>',
1143
+ },
1144
+ result[:item]['foo'][:tmpl],
1145
+ 'Parser.parse_html should parse nested action templates'
1146
+ )
1147
+
1148
+ result = Runo::Parser.parse_html <<'_html'
1149
+ <html>
1150
+ <ul id="foo" class="runo-blog">
1151
+ <li>$(subject=text)</li>
1152
+ </ul>
1153
+ <div class="foo-navi"><span class="bar-navi_prev">prev</span></div>
1154
+ </html>
1155
+ _html
1156
+ assert_equal(
1157
+ '<span class="bar-navi_prev">prev</span>',
1158
+ result[:item]['foo'][:tmpl][:navi_prev],
1159
+ 'Parser.parse_html should ignore the id of a nested action template'
1160
+ )
1161
+ end
1162
+
1163
+ def test_action_tmpl_in_sd
1164
+ result = Runo::Parser.parse_html <<'_html'
1165
+ <ul id="foo" class="runo-blog">
1166
+ <li class="body">$(text)</li>
1167
+ <div class="navi">bar</div>
1168
+ </ul>
1169
+ _html
1170
+ assert_equal(
1171
+ <<'_html',
1172
+ <div class="navi">bar</div>
1173
+ _html
1174
+ result[:item]['foo'][:tmpl][:navi],
1175
+ 'Parser.parse_html should parse action templates in sd[:tmpl]'
1176
+ )
1177
+ assert_match(
1178
+ %r{\$\(\.navi\)},
1179
+ result[:item]['foo'][:tmpl][:index],
1180
+ 'Parser.parse_html should parse action templates in sd[:tmpl]'
1181
+ )
1182
+ end
1183
+
1184
+ def test_action_tmpl_in_sd_with_nested_action_tmpl
1185
+ result = Runo::Parser.parse_html <<'_html'
1186
+ <ul id="foo" class="runo-blog">
1187
+ <li class="body">$(text)</li>
1188
+ <div class="navi"><span class="navi_prev">prev</span></div>
1189
+ </ul>
1190
+ _html
1191
+ assert_equal(
1192
+ <<'_html',
1193
+ <div class="navi">$(.navi_prev)</div>
1194
+ _html
1195
+ result[:item]['foo'][:tmpl][:navi],
1196
+ 'Parser.parse_html should parse nested action templates in sd[:tmpl]'
1197
+ )
1198
+ assert_equal(
1199
+ '<span class="navi_prev">prev</span>',
1200
+ result[:item]['foo'][:tmpl][:navi_prev],
1201
+ 'Parser.parse_html should parse nested action templates in sd[:tmpl]'
1202
+ )
1203
+ end
1204
+
1205
+ def test_supplement_sd
1206
+ result = Runo::Parser.parse_html <<'_html'
1207
+ <ul id="foo" class="runo-blog">
1208
+ <li class="body">$(text)</li>
1209
+ </ul>
1210
+ _html
1211
+ assert_match(
1212
+ /\$\(\.navi\)/,
1213
+ result[:item]['foo'][:tmpl][:index],
1214
+ 'Parser.supplement_sd should supplement sd[:tmpl] with default menus'
1215
+ )
1216
+
1217
+ result = Runo::Parser.parse_html <<'_html'
1218
+ <ul id="foo" class="runo-blog">
1219
+ <div class="navi">bar</div>
1220
+ <li class="body">$(text)</li>
1221
+ </ul>
1222
+ _html
1223
+ assert_no_match(
1224
+ /\$\(\.navi\).*\$\(\.navi\)/m,
1225
+ result[:item]['foo'][:tmpl][:index],
1226
+ 'Parser.supplement_sd should not supplement sd[:tmpl] when it already has the menu'
1227
+ )
1228
+
1229
+ result = Runo::Parser.parse_html <<'_html'
1230
+ <div class="foo-navi">bar</div>
1231
+ <ul id="foo" class="runo-blog">
1232
+ <li class="body">$(text)</li>
1233
+ </ul>
1234
+ _html
1235
+ assert_no_match(
1236
+ /\$\(\.navi\)/,
1237
+ result[:item]['foo'][:tmpl][:index],
1238
+ 'Parser.supplement_sd should not supplement sd[:tmpl] when it already has the menu'
1239
+ )
1240
+ end
1241
+
1242
+ def test_supplement_ss
1243
+ result = Runo::Parser.parse_html <<'_html'
1244
+ <ul id="foo" class="runo-blog">
1245
+ <li class="body">$(text)</li>
1246
+ </ul>
1247
+ _html
1248
+ assert_match(
1249
+ /\$\(\.a_update\)/,
1250
+ result[:item]['foo'][:item]['default'][:tmpl][:index],
1251
+ 'Parser.supplement_ss should supplement ss[:tmpl] with default menus'
1252
+ )
1253
+
1254
+ result = Runo::Parser.parse_html <<'_html'
1255
+ <ul id="foo" class="runo-blog">
1256
+ <li class="body">$(text) $(.action_update)</li>
1257
+ </ul>
1258
+ _html
1259
+ assert_no_match(
1260
+ /\$\(\.a_update\)/,
1261
+ result[:item]['foo'][:item]['default'][:tmpl][:index],
1262
+ 'Parser.supplement_ss should not supplement ss[:tmpl] when it already has the menu'
1263
+ )
1264
+ end
1265
+
1266
+ end