runo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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