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,1504 @@
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_Set_Dynamic < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @sd = Runo::Set::Dynamic.new(
12
+ :id => 'foo',
13
+ :klass => 'set-dynamic',
14
+ :workflow => 'blog',
15
+ :group => ['roy', 'don'],
16
+ :tmpl => {
17
+ :index => <<'_tmpl'.chomp,
18
+ <ul id="foo" class="runo-blog">
19
+ $()</ul>
20
+ $(.submit)
21
+ _tmpl
22
+ },
23
+ :item => {
24
+ 'default' => Runo::Parser.parse_html(<<'_html')
25
+ <li>$(name = text 16 0..16 :'nobody'): $(comment = text 64 :'hi.')$(.hidden)</li>
26
+ _html
27
+ }
28
+ )
29
+ @sd[:conds] = {}
30
+ @sd[:order] = nil
31
+ @sd[:tmpl][:action_create] = ''
32
+ def @sd._g_submit(arg)
33
+ "[#{my[:id]}-#{arg[:orig_action]}#{arg[:sub_action] && ('.' + arg[:sub_action].to_s)}]\n"
34
+ end
35
+ @sd[:item]['default'][:item].delete '_timestamp'
36
+ Runo.client = 'root'
37
+ Runo.current[:base] = nil
38
+ end
39
+
40
+ def teardown
41
+ Runo.client = nil
42
+ end
43
+
44
+ def test_storage
45
+ assert_kind_of(
46
+ Runo::Storage,
47
+ @sd.storage,
48
+ 'Set::Dynamic#instance should load an apropriate storage for the list'
49
+ )
50
+ assert_instance_of(
51
+ Runo::Storage::Temp,
52
+ @sd.storage,
53
+ 'Set::Dynamic#instance should load an apropriate storage for the list'
54
+ )
55
+ end
56
+
57
+ class Runo::Workflow::Default_meta < Runo::Workflow
58
+ DEFAULT_META = {:foo => 'FOO'}
59
+ end
60
+ def test_default_meta
61
+ sd = Runo::Set::Dynamic.new(:workflow => 'default_meta')
62
+ assert_equal(
63
+ 'FOO',
64
+ sd[:foo],
65
+ 'Set::Dynamic#[] should look for the default value in the workflow'
66
+ )
67
+
68
+ sd = Runo::Set::Dynamic.new(:workflow => 'default_meta', :foo => 'BAR')
69
+ assert_equal(
70
+ 'BAR',
71
+ sd[:foo],
72
+ 'workflow.DEFAULT_META should be eclipsed by @meta'
73
+ )
74
+
75
+ sd = Runo::Set::Dynamic.new(:workflow => 'default_meta')
76
+ def sd.meta_foo
77
+ 'abc'
78
+ end
79
+ assert_equal(
80
+ 'abc',
81
+ sd[:foo],
82
+ 'workflow.DEFAULT_META should be eclipsed by meta_*()'
83
+ )
84
+ end
85
+
86
+ def test_meta_tid
87
+ tid = @sd[:tid]
88
+ assert_match(
89
+ Runo::REX::TID,
90
+ tid,
91
+ 'Set::Dynamic#meta_tid should return an unique id per an instance'
92
+ )
93
+ assert_equal(
94
+ tid,
95
+ @sd[:tid],
96
+ 'Set::Dynamic#meta_tid should return the same id throughout the lifecycle of the item'
97
+ )
98
+ assert_not_equal(
99
+ tid,
100
+ Runo::Set::Dynamic.new[:tid],
101
+ 'Set::Dynamic#meta_tid should be unique to an item'
102
+ )
103
+ end
104
+
105
+ def test_meta_base_path
106
+ item = Runo::Set::Static::Folder.root.item('foo', 'main')
107
+
108
+ Runo.current[:base] = Runo::Set::Static::Folder.root.item('foo', 'bar', 'sub')
109
+ assert_equal(
110
+ '/foo/bar/sub',
111
+ item[:base_path],
112
+ 'Field#[:base_path] should return the path name of the base SD'
113
+ )
114
+
115
+ Runo.current[:base] = Runo::Set::Static::Folder.root.item('foo', 'bar', 'main')
116
+ assert_equal(
117
+ '/foo/bar',
118
+ item[:base_path],
119
+ "Field#[:base_path] should omit 'main' in the path"
120
+ )
121
+ end
122
+
123
+ def test_meta_order
124
+ sd = Runo::Set::Dynamic.new(
125
+ :id => 'foo',
126
+ :klass => 'set-dynamic',
127
+ :workflow => 'contact'
128
+ )
129
+ assert_nil(
130
+ sd[:order],
131
+ 'Set::Dynamic#[:order] should be nil by default'
132
+ )
133
+
134
+ sd = Runo::Set::Dynamic.new(
135
+ :id => 'foo',
136
+ :klass => 'set-dynamic',
137
+ :workflow => 'blog'
138
+ )
139
+ assert_equal(
140
+ '-id',
141
+ sd[:order],
142
+ 'Set::Dynamic#[:order] should refer to the default_meta[:order]'
143
+ )
144
+
145
+ sd = Runo::Set::Dynamic.new(
146
+ :id => 'foo',
147
+ :klass => 'set-dynamic',
148
+ :workflow => 'blog',
149
+ :tokens => ['asc']
150
+ )
151
+ assert_equal(
152
+ 'id',
153
+ sd[:order],
154
+ 'Set::Dynamic#[:order] should be overriden by meta[:order]'
155
+ )
156
+ end
157
+
158
+ def test_meta_href
159
+ Runo.current[:uri] = nil
160
+
161
+ sd = Runo::Set::Static::Folder.root.item('foo','main')
162
+ assert_equal(
163
+ '/foo/',
164
+ sd[:href],
165
+ 'Set::Dynamic#meta_href should return the uri to itself'
166
+ )
167
+
168
+ sd = Runo::Set::Static::Folder.root.item('foo','sub')
169
+ assert_equal(
170
+ '/foo/sub/',
171
+ sd[:href],
172
+ "Set::Dynamic#meta_href should not omit steps other than 'main'"
173
+ )
174
+
175
+ sd = Runo::Set::Static::Folder.root.item('foo','main','20091120_0001','replies')
176
+ assert_equal(
177
+ '/foo/20091120_0001/replies/',
178
+ sd[:href],
179
+ 'Set::Dynamic#meta_href should return the proper uri for a nested SD'
180
+ )
181
+ end
182
+
183
+ def test_item
184
+ @sd.load('20100131_1234' => {'name' => 'frank'})
185
+ assert_instance_of(
186
+ Runo::Set::Static,
187
+ @sd.item('20100131_1234'),
188
+ 'Set::Dynamic#item should return the child set in the storage'
189
+ )
190
+
191
+ assert_nil(
192
+ @sd.item('non-existent'),
193
+ 'Set::Dynamic#item should return nil when the item is not in the storage'
194
+ )
195
+ assert_nil(
196
+ @sd.item(''),
197
+ 'Set::Dynamic#item should return nil when the item is not in the storage'
198
+ )
199
+ end
200
+
201
+ def test_val
202
+ @sd.load(
203
+ '20100131_1234' => {'name' => 'frank'},
204
+ '20100131_1235' => {'name' => 'carl'}
205
+ )
206
+ assert_equal(
207
+ {
208
+ '20100131_1234' => {'name' => 'frank'},
209
+ '20100131_1235' => {'name' => 'carl'},
210
+ },
211
+ @sd.val,
212
+ 'Set::Dynamic#val without arg should return values of all items in the storage'
213
+ )
214
+ assert_equal(
215
+ {'name' => 'frank'},
216
+ @sd.val('20100131_1234'),
217
+ 'Set::Dynamic#val with an item id should return the value of the item in the storage'
218
+ )
219
+ assert_nil(
220
+ @sd.val('non-existent'),
221
+ 'Set::Dynamic#val with an item id should return nil when the item is not in the storage'
222
+ )
223
+ end
224
+
225
+ def test_get
226
+ @sd.load(
227
+ '20100131_1234' => {'name' => 'frank', 'comment' => 'bar'},
228
+ '20100131_1235' => {'name' => 'carl', 'comment' => 'baz'}
229
+ )
230
+ @sd[:tmpl][:navi] = ''
231
+ @sd.each {|item| item[:tmpl][:action_update] = '' }
232
+ assert_equal(
233
+ <<'_html',
234
+ <ul id="foo" class="runo-blog">
235
+ <li>frank: bar</li>
236
+ <li>carl: baz</li>
237
+ </ul>
238
+ _html
239
+ @sd.get,
240
+ 'Set::Dynamic#get should return the html by [:tmpl]'
241
+ )
242
+ assert_equal(
243
+ <<'_html',
244
+ <ul id="foo" class="runo-blog">
245
+ <li>carl: baz</li>
246
+ </ul>
247
+ _html
248
+ @sd.get(:conds => {:id => '20100131_1235'}),
249
+ 'Set::Dynamic#get should return the html by [:tmpl]'
250
+ )
251
+
252
+ @sd.each {|ss|
253
+ ss.each {|item|
254
+ def item._g_update(arg)
255
+ 'moo!'
256
+ end
257
+ }
258
+ }
259
+ assert_equal(
260
+ <<'_html',
261
+ <ul id="foo" class="runo-blog">
262
+ <li>moo!: moo!</li>
263
+ </ul>
264
+ [foo-update]
265
+ _html
266
+ @sd.get(:conds => {:id => '20100131_1235'}, :action => :update),
267
+ 'Set::Dynamic#get should pass the given action to lower items'
268
+ )
269
+ end
270
+
271
+ def test_get_create
272
+ @sd.load(
273
+ '1234' => {'name' => 'frank', 'comment' => 'bar'},
274
+ '1235' => {'name' => 'carl', 'comment' => 'baz'}
275
+ )
276
+ result = @sd.get(:action => :create)
277
+ assert_match(
278
+ /<input/,
279
+ result,
280
+ 'Set::Dynamic#_g_create should return the _g_create() of a newly created item'
281
+ )
282
+ assert_no_match(
283
+ /bar/,
284
+ result,
285
+ 'Set::Dynamic#_g_create should not include the _g_create() of existing items'
286
+ )
287
+ end
288
+
289
+ def test_get_empty_item
290
+ @sd.load(
291
+ '1234' => {}
292
+ )
293
+
294
+ assert_equal(
295
+ <<'_html',
296
+ <ul id="foo" class="runo-blog">
297
+ </ul>
298
+ _html
299
+ @sd.get(:action => :read),
300
+ 'Set#_g_default should skip empty items'
301
+ )
302
+
303
+ assert_equal(
304
+ <<'_html',
305
+ <ul id="foo" class="runo-blog">
306
+ <li><span class="text"><input type="text" name="name" value="" size="16" /></span>: <span class="text"><input type="text" name="comment" value="" size="64" /></span></li>
307
+ </ul>
308
+ [foo-update]
309
+ _html
310
+ @sd.get(:action => :update),
311
+ 'Set#_g_default should not skip empty items when the action is :create or :update'
312
+ )
313
+ end
314
+
315
+ def test_get_preview
316
+ Runo.current[:base] = nil
317
+ @sd.load(
318
+ '20100330_1234' => {'name' => 'frank', 'comment' => 'bar'},
319
+ '20100330_1235' => {'name' => 'carl', 'comment' => 'baz'}
320
+ )
321
+ assert_equal(
322
+ <<'_html',
323
+ <ul id="foo" class="runo-blog">
324
+ <li>frank: bar<input type="hidden" name="20100330_1234.action" value="howdy" /></li>
325
+ </ul>
326
+ [foo-preview.howdy]
327
+ _html
328
+ @sd.get(:action => :preview, :sub_action => :howdy, :conds => {:id => '20100330_1234'}),
329
+ 'Set::Dynamic#_g_preview should return _g_read + _g_submit'
330
+ )
331
+ end
332
+
333
+ def test_get_by_self_reference
334
+ ss = Runo::Set::Static.new(
335
+ :html => '<ul class="runo-attachment"><li class="body"></li>$(.pipco)</ul>'
336
+ )
337
+ sd = ss.item('main')
338
+ def sd._g_submit(arg)
339
+ ''
340
+ end
341
+ def sd._g_pipco(arg)
342
+ _get_by_action_tmpl(arg) || 'PIPCO'
343
+ end
344
+ def sd._g_jawaka(arg)
345
+ 'JAWAKA'
346
+ end
347
+ sd[:tmpl][:navi] = ''
348
+
349
+ sd[:tmpl][:pipco] = '<foo>$(.jawaka)</foo>'
350
+ sd[:tmpl][:jawaka] = nil
351
+ assert_equal(
352
+ '<ul class="runo-attachment"><foo>JAWAKA</foo></ul>',
353
+ ss.get,
354
+ 'Set::Dynamic#_get_by_self_reference should work via [:parent]._get_by_tmpl()'
355
+ )
356
+
357
+ sd[:tmpl][:pipco] = '<foo>$(.jawaka)</foo>'
358
+ sd[:tmpl][:jawaka] = 'via tmpl'
359
+ assert_equal(
360
+ '<ul class="runo-attachment"><foo>JAWAKA</foo></ul>',
361
+ ss.get,
362
+ 'Set::Dynamic#_get_by_self_reference should not recur'
363
+ )
364
+
365
+ sd[:tmpl][:pipco] = '<foo>$(.pipco)</foo>'
366
+ sd[:tmpl][:jawaka] = nil
367
+ assert_nothing_raised(
368
+ 'Set::Dynamic#_get_by_self_reference should not cause an infinite reference'
369
+ ) {
370
+ ss.get
371
+ }
372
+
373
+ sd[:tmpl][:pipco] = '<foo>$()</foo>'
374
+ assert_nothing_raised(
375
+ 'Set::Dynamic#_get_by_self_reference should not cause an infinite reference'
376
+ ) {
377
+ ss.get
378
+ }
379
+
380
+ sd[:tmpl][:pipco] = '<foo>$(.jawaka)</foo>'
381
+ sd[:tmpl][:jawaka] = '<bar>$(.pipco)</bar>'
382
+ assert_nothing_raised(
383
+ 'Set::Dynamic#_get_by_self_reference should not cause an infinite reference'
384
+ ) {
385
+ ss.get
386
+ }
387
+ end
388
+
389
+ def test_get_by_self_reference_via_parent_tmpl
390
+ ss = Runo::Set::Static.new(
391
+ :html => '$(main.action_pipco)<ul class="runo-attachment"></ul>'
392
+ )
393
+ sd = ss.item('main')
394
+ def sd._g_submit(arg)
395
+ end
396
+ def sd._g_action_pipco(arg)
397
+ 'PIPCO'
398
+ end
399
+ sd[:tmpl][:navi] = ''
400
+
401
+ assert_equal(
402
+ 'PIPCO<ul class="runo-attachment"></ul>',
403
+ ss.get,
404
+ 'Set::Dynamic#_get_by_self_reference should work via [:parent]._get_by_tmpl()'
405
+ )
406
+ assert_equal(
407
+ '<ul class="runo-attachment"></ul>',
408
+ ss.get('main' => {:action => :create}),
409
+ 'Set::Dynamic#_get_by_self_reference should hide unused items via [:parent]._get_by_tmpl()'
410
+ )
411
+ end
412
+
413
+ def test_get_by_self_reference_multiple_vars
414
+ ss = Runo::Set::Static.new(
415
+ :html => '<ul class="runo-attachment">$(.pipco)<li class="body">$(foo=text)</li></ul>'
416
+ )
417
+ sd = ss.item('main')
418
+ def sd._g_pipco(arg)
419
+ 'PIPCO'
420
+ end
421
+ sd[:tmpl][:navi] = ''
422
+
423
+ assert_equal(
424
+ '<ul class="runo-attachment">PIPCO</ul>',
425
+ ss.get,
426
+ 'Set::Dynamic#_get_by_self_reference should not be affected by previous $(.action)'
427
+ )
428
+ end
429
+
430
+ def test_get_uri_prev_next
431
+ @sd[:p_size] = 2
432
+ @sd.load(
433
+ '20091128_0001' => {'name' => 'frank', 'comment' => 'bar'},
434
+ '20091129_0001' => {'name' => 'frank', 'comment' => 'bar'},
435
+ '20091130_0001' => {'name' => 'frank', 'comment' => 'bar'},
436
+ '20091201_0001' => {'name' => 'frank', 'comment' => 'bar'},
437
+ '20091202_0001' => {'name' => 'frank', 'comment' => 'bar'},
438
+ '20091203_0001' => {'name' => 'frank', 'comment' => 'bar'}
439
+ )
440
+
441
+ assert_equal(
442
+ '200912/',
443
+ @sd.send(
444
+ :_g_uri_prev,
445
+ :conds => {:d => '200912', :p => '2'}
446
+ ),
447
+ 'Set::Dynamic#_g_uri_prev should return the previous uri for the given conds'
448
+ )
449
+ assert_nil(
450
+ @sd.send(
451
+ :_g_uri_next,
452
+ :conds => {:d => '200912', :p => '2'}
453
+ ),
454
+ 'Set::Dynamic#_g_uri_next should return nil if there is no next conds'
455
+ )
456
+
457
+ assert_equal(
458
+ '200911/p=2/',
459
+ @sd.send(
460
+ :_g_uri_prev,
461
+ :conds => {:d => '200912', :p => '1'}
462
+ ),
463
+ 'Set::Dynamic#_g_uri_prev should return the previous uri for the given conds'
464
+ )
465
+ assert_equal(
466
+ '200911/',
467
+ @sd.send(
468
+ :_g_uri_prev,
469
+ :conds => {:d => '200911', :p => '2'}
470
+ ),
471
+ 'Set::Dynamic#_g_uri_prev should return the previous uri for the given conds'
472
+ )
473
+ assert_equal(
474
+ '200912/',
475
+ @sd.send(
476
+ :_g_uri_next,
477
+ :conds => {:d => '200911', :p => '2'}
478
+ ),
479
+ 'Set::Dynamic#_g_uri_next should return the next uri for the given conds'
480
+ )
481
+ assert_nil(
482
+ @sd.send(
483
+ :_g_uri_prev,
484
+ :conds => {:d => '200911', :p => '1'}
485
+ ),
486
+ 'Set::Dynamic#_g_uri_prev should return nil if there is no previous conds'
487
+ )
488
+
489
+ assert_equal(
490
+ '200911/p=2/read_detail.html',
491
+ @sd.send(
492
+ :_g_uri_prev,
493
+ {
494
+ :conds => {:d => '200912', :p => '1'},
495
+ :orig_action => :read,
496
+ :sub_action => :detail,
497
+ }
498
+ ),
499
+ 'Set::Dynamic#_g_uri_prev should include the original action if the action is special'
500
+ )
501
+ assert_equal(
502
+ '200912/read_detail.html',
503
+ @sd.send(
504
+ :_g_uri_next,
505
+ {
506
+ :conds => {:d => '200911', :p => '2'},
507
+ :orig_action => :read,
508
+ :sub_action => :detail,
509
+ }
510
+ ),
511
+ 'Set::Dynamic#_g_uri_next should include the original action if the action is special'
512
+ )
513
+
514
+ @sd[:tmpl][:navi] = '$(.uri_prev)'
515
+ assert_equal(
516
+ '200911/',
517
+ @sd.send(
518
+ :_get_by_self_reference,
519
+ :action => :navi,
520
+ :conds => {:d => '200911', :p => '2'},
521
+ :orig_action => :read
522
+ ),
523
+ 'Set::Dynamic#_g_navi should pass the conds to the subsequent calls to _g_*()'
524
+ )
525
+ end
526
+
527
+ def test_recurring_action_tmpl
528
+ @sd.load(
529
+ '20091128_0001' => {'name' => 'frank', 'comment' => 'bar'},
530
+ '20091129_0001' => {'name' => 'frank', 'comment' => 'bar'}
531
+ )
532
+ @sd[:tmpl][:navi] = '$(.navi)'
533
+
534
+ result = nil
535
+ assert_nothing_raised(
536
+ 'Set::Dynamic#_g_navi should not call itself recursively'
537
+ ) {
538
+ result = @sd.send(
539
+ :_get_by_self_reference,
540
+ :action => :navi,
541
+ :conds => {:d => '20091128'},
542
+ :orig_action => :read
543
+ )
544
+ }
545
+ assert_equal(
546
+ '$(.navi)',
547
+ result,
548
+ 'Set::Dynamic#_g_navi should ignore $(.navi)'
549
+ )
550
+
551
+ @sd[:tmpl][:navi] = nil
552
+ @sd[:tmpl][:navi_next] = '$(.navi)'
553
+ assert_nothing_raised(
554
+ 'Set::Dynamic#_g_navi should not call itself recursively'
555
+ ) {
556
+ result = @sd.send(
557
+ :_get_by_self_reference,
558
+ :action => :navi,
559
+ :conds => {:d => '20091128'},
560
+ :orig_action => :read
561
+ )
562
+ }
563
+ assert_match(
564
+ /\$\(\.navi\)/,
565
+ result,
566
+ 'Set::Dynamic#_g_navi_next should ignore $(.navi)'
567
+ )
568
+ end
569
+
570
+ def test_item_arg
571
+ root_arg = {
572
+ :action => :read,
573
+ 'foo' => {
574
+ :action => :update,
575
+ :conds => {:d => '2010'},
576
+ 'bar' => {
577
+ :action => :delete,
578
+ },
579
+ },
580
+ 'baz' => {
581
+ 'qux' => {:action => :create},
582
+ },
583
+ }
584
+
585
+ assert_equal(
586
+ {
587
+ :p_action => :read,
588
+ :action => :update,
589
+ :conds => {:d => '2010'},
590
+ 'bar' => {
591
+ :action => :delete,
592
+ },
593
+ },
594
+ @sd.send(
595
+ :item_arg,
596
+ root_arg,
597
+ 'foo'
598
+ ),
599
+ 'Set#item_arg should return a partial hash of the root arg'
600
+ )
601
+ assert_equal(
602
+ {
603
+ :p_action => :update,
604
+ :action => :delete,
605
+ },
606
+ @sd.send(
607
+ :item_arg,
608
+ root_arg,
609
+ ['foo', 'bar']
610
+ ),
611
+ 'Set#item_arg should dig into multiple steps'
612
+ )
613
+ assert_equal(
614
+ {
615
+ :p_action => :read,
616
+ :action => :read,
617
+ 'qux' => {:action => :create},
618
+ },
619
+ @sd.send(
620
+ :item_arg,
621
+ root_arg,
622
+ ['baz']
623
+ ),
624
+ 'Set#item_arg should supplement item_arg[:action]'
625
+ )
626
+ assert_equal(
627
+ {
628
+ :p_action => :read,
629
+ :action => :create,
630
+ },
631
+ @sd.send(
632
+ :item_arg,
633
+ root_arg,
634
+ ['baz', 'qux']
635
+ ),
636
+ 'Set#item_arg should supplement item_arg[:p_action]'
637
+ )
638
+ assert_equal(
639
+ {
640
+ :p_action => :read,
641
+ :action => :read,
642
+ },
643
+ @sd.send(
644
+ :item_arg,
645
+ root_arg,
646
+ ['baz', 'non-existent']
647
+ ),
648
+ 'Set#item_arg should supplement item_arg[:action] & item_arg[:p_action]'
649
+ )
650
+ end
651
+
652
+ def test_uri_p
653
+ @sd.load(
654
+ '20091128_0001' => {'name' => 'frank', 'comment' => 'bar'},
655
+ '20091129_0001' => {'name' => 'frank', 'comment' => 'bar'},
656
+ '20091130_0001' => {'name' => 'frank', 'comment' => 'bar'},
657
+ '20091201_0001' => {'name' => 'frank', 'comment' => 'bar'}
658
+ )
659
+
660
+ @sd[:p_size] = 2
661
+ assert_equal(
662
+ ['200911/', '200911/p=2/'],
663
+ @sd.send(
664
+ :_uri_p,
665
+ :conds => {:d => '200911', :p => '1'}
666
+ ),
667
+ 'Set::Dynamic#_uri_p should return the array of the sibling conds'
668
+ )
669
+
670
+ @sd[:p_size] = nil
671
+ assert_nil(
672
+ @sd.send(
673
+ :_uri_p,
674
+ :conds => {:d => '200911'}
675
+ ),
676
+ 'Set::Dynamic#_uri_p should return nil if the siblings are not :p'
677
+ )
678
+
679
+ @sd[:p_size] = 2
680
+ assert_nil(
681
+ @sd.send(
682
+ :_uri_p,
683
+ :conds => {:d => '200911', :id => '20091129_0001'}
684
+ ),
685
+ 'Set::Dynamic#_uri_p should return nil if the siblings are not :p'
686
+ )
687
+ assert_nil(
688
+ @sd.send(
689
+ :_uri_p,
690
+ :conds => {:d => '200911', :p => '1', :id => '20091129_0001'}
691
+ ),
692
+ 'Set::Dynamic#_uri_p should return nil if the siblings are not :p'
693
+ )
694
+ end
695
+
696
+ def test_g_view_ym
697
+ @sd[:order] = 'id'
698
+ @sd.load(
699
+ '20091128_0001' => {'name' => 'frank', 'comment' => 'bar'},
700
+ '20091129_0001' => {'name' => 'frank', 'comment' => 'bar'},
701
+ '20091130_0001' => {'name' => 'frank', 'comment' => 'bar'},
702
+ '20091201_0001' => {'name' => 'frank', 'comment' => 'bar'},
703
+ '20100111_0001' => {'name' => 'frank', 'comment' => 'bar'}
704
+ )
705
+ assert_equal(
706
+ <<'_html',
707
+ <div class="view_ym">
708
+ <span class="y">
709
+ 2009 |
710
+ <span class="m"><a href="/foo/200911/">Nov</a></span>
711
+ <span class="m"><a href="/foo/200912/">Dec</a></span>
712
+ <br/>
713
+ </span>
714
+ <span class="y">
715
+ 2010 |
716
+ <span class="m"><a href="/foo/201001/">Jan</a></span>
717
+ <br/>
718
+ </span>
719
+ </div>
720
+ _html
721
+ @sd.send(
722
+ :_g_view_ym,
723
+ {:conds => {}}
724
+ ),
725
+ 'Set::Dynamic#_g_view_ym should return the available ym conds'
726
+ )
727
+
728
+ @sd[:order] = '-id'
729
+ @sd.load(
730
+ '20091128_0001' => {'name' => 'frank', 'comment' => 'bar'},
731
+ '20091129_0001' => {'name' => 'frank', 'comment' => 'bar'},
732
+ '20091130_0001' => {'name' => 'frank', 'comment' => 'bar'},
733
+ '20091201_0001' => {'name' => 'frank', 'comment' => 'bar'},
734
+ '20100111_0001' => {'name' => 'frank', 'comment' => 'bar'}
735
+ )
736
+ assert_equal(
737
+ <<'_html',
738
+ <div class="view_ym">
739
+ <span class="y">
740
+ 2009 |
741
+ <span class="m"><a href="/foo/200911/p=last/">Nov</a></span>
742
+ <span class="m"><a href="/foo/200912/p=last/">Dec</a></span>
743
+ <br/>
744
+ </span>
745
+ <span class="y">
746
+ 2010 |
747
+ <span class="m"><a href="/foo/201001/p=last/">Jan</a></span>
748
+ <br/>
749
+ </span>
750
+ </div>
751
+ _html
752
+ @sd.send(
753
+ :_g_view_ym,
754
+ {:conds => {}}
755
+ ),
756
+ 'Set::Dynamic#_g_view_ym should refer to [:order]'
757
+ )
758
+
759
+ @sd[:order] = 'id'
760
+ assert_equal(
761
+ <<'_html',
762
+ <div class="view_ym">
763
+ <span class="y">
764
+ 2009 |
765
+ <span class="m"><a href="/foo/200911/">Nov</a></span>
766
+ <span class="m"><span class="current">Dec</span></span>
767
+ <br/>
768
+ </span>
769
+ <span class="y">
770
+ 2010 |
771
+ <span class="m"><a href="/foo/201001/">Jan</a></span>
772
+ <br/>
773
+ </span>
774
+ </div>
775
+ _html
776
+ @sd.send(
777
+ :_g_view_ym,
778
+ {:conds => {:d => '200912'}}
779
+ ),
780
+ 'Set::Dynamic#_g_view_ym should distinguish the current cond[:d] if available'
781
+ )
782
+ end
783
+
784
+ def test_g_submit
785
+ Runo.client = nil
786
+ @sd = Runo::Set::Dynamic.new(
787
+ :klass => 'set-dynamic',
788
+ :workflow => 'blog',
789
+ :tmpl => {:index => '$(.submit)'},
790
+ :item => {
791
+ 'default' => Runo::Parser.parse_html(<<'_html')
792
+ <li>$(name = text 32 :'nobody'): $(comment = text 64 :'hi.')$(.hidden)</li>
793
+ _html
794
+ }
795
+ ).load(
796
+ '_001' => {'name' => 'frank', 'comment' => 'bar'},
797
+ '20100401_0001' => {'name' => 'frank', 'comment' => 'bar'}
798
+ )
799
+ wf = @sd.workflow
800
+
801
+ def wf.permit?(roles, action)
802
+ true
803
+ end
804
+ @sd[:preview] = nil
805
+
806
+ assert_equal(
807
+ <<'_html',
808
+ <div class="submit">
809
+ <input name=".status-public" type="submit" value="create" />
810
+ </div>
811
+ _html
812
+ @sd.get(:action => :update, :conds => {:id => '_001'}),
813
+ 'Set#_g_submit should not return preview_delete when there is only new items'
814
+ )
815
+ @sd[:preview] = nil
816
+ assert_equal(
817
+ <<'_html',
818
+ <div class="submit">
819
+ <input name=".status-public" type="submit" value="update" />
820
+ <input name=".action-preview_delete" type="submit" value="delete..." />
821
+ </div>
822
+ _html
823
+ @sd.get(:action => :update, :conds => {:id => '20100401_0001'}),
824
+ 'Set#_g_submit should return buttons according to the permission, meta and orig_action'
825
+ )
826
+ @sd[:preview] = :optional
827
+ assert_equal(
828
+ <<'_html',
829
+ <div class="submit">
830
+ <input name=".status-public" type="submit" value="update" />
831
+ <input name=".action-preview_update" type="submit" value="preview" />
832
+ <input name=".action-preview_delete" type="submit" value="delete..." />
833
+ </div>
834
+ _html
835
+ @sd.get(:action => :update, :conds => {:id => '20100401_0001'}),
836
+ 'Set#_g_submit should return buttons according to the permission, meta and orig_action'
837
+ )
838
+ @sd[:preview] = :mandatory
839
+ assert_equal(
840
+ <<'_html',
841
+ <div class="submit">
842
+ <input name=".action-preview_update" type="submit" value="preview" />
843
+ <input name=".action-preview_delete" type="submit" value="delete..." />
844
+ </div>
845
+ _html
846
+ @sd.get(:action => :update, :conds => {:id => '20100401_0001'}),
847
+ 'Set#_g_submit should return buttons according to the permission, meta and orig_action'
848
+ )
849
+ assert_equal(
850
+ <<'_html',
851
+ <div class="submit">
852
+ <input name=".status-public" type="submit" value="update" />
853
+ </div>
854
+ _html
855
+ @sd.get(:action => :preview, :sub_action => :update),
856
+ 'Set#_g_submit should not show preview buttons when the orig_action is :preview'
857
+ )
858
+ assert_equal(
859
+ <<'_html',
860
+ <div class="submit">
861
+ <input name=".status-public" type="submit" value="delete" />
862
+ </div>
863
+ _html
864
+ @sd.get(:action => :preview, :sub_action => :delete),
865
+ 'Set#_g_submit should not show preview buttons when the orig_action is :preview'
866
+ )
867
+
868
+ def wf.permit?(roles, action)
869
+ true unless action == :delete
870
+ end
871
+ @sd[:preview] = nil
872
+ assert_equal(
873
+ <<'_html',
874
+ <div class="submit">
875
+ <input name=".status-public" type="submit" value="update" />
876
+ </div>
877
+ _html
878
+ @sd.get(:action => :update, :conds => {:id => '20100401_0001'}),
879
+ 'Set#_g_submit should return buttons according to the permission, meta and orig_action'
880
+ )
881
+ @sd[:preview] = :optional
882
+ assert_equal(
883
+ <<'_html',
884
+ <div class="submit">
885
+ <input name=".status-public" type="submit" value="update" />
886
+ <input name=".action-preview_update" type="submit" value="preview" />
887
+ </div>
888
+ _html
889
+ @sd.get(:action => :update, :conds => {:id => '20100401_0001'}),
890
+ 'Set#_g_submit should return buttons according to the permission, meta and orig_action'
891
+ )
892
+ @sd[:preview] = :mandatory
893
+ assert_equal(
894
+ <<'_html',
895
+ <div class="submit">
896
+ <input name=".action-preview_update" type="submit" value="preview" />
897
+ </div>
898
+ _html
899
+ @sd.get(:action => :update, :conds => {:id => '20100401_0001'}),
900
+ 'Set#_g_submit should return buttons according to the permission, meta and orig_action'
901
+ )
902
+ assert_equal(
903
+ <<'_html',
904
+ <div class="submit">
905
+ <input name=".status-public" type="submit" value="update" />
906
+ </div>
907
+ _html
908
+ @sd.get(:action => :preview, :sub_action => :update),
909
+ 'Set#_g_submit should not show preview buttons when the orig_action is :preview'
910
+ )
911
+
912
+ def wf.permit?(roles, action)
913
+ true unless action == :update
914
+ end
915
+ @sd[:preview] = nil
916
+ assert_equal(
917
+ <<'_html',
918
+ <div class="submit">
919
+ <input name=".status-public" type="submit" value="delete" />
920
+ </div>
921
+ _html
922
+ @sd.get(:action => :preview, :sub_action => :delete),
923
+ 'Set#_g_submit should not show preview buttons when the orig_action is :preview'
924
+ )
925
+ end
926
+
927
+ def test_post
928
+ @sd.post(:create, '1234' => {'name' => 'carl'})
929
+ assert_equal(
930
+ :create,
931
+ @sd.action,
932
+ 'Set::Dynamic#post should set @action'
933
+ )
934
+
935
+ @sd.commit
936
+ assert_equal(
937
+ :create,
938
+ @sd.result,
939
+ 'Set::Dynamic#commit should set @result'
940
+ )
941
+
942
+ @sd.post(:update, '1234' => {'name' => 'frank'})
943
+ assert_nil(
944
+ @sd.result,
945
+ 'Set::Dynamic#post should reset @result'
946
+ )
947
+ end
948
+
949
+ def test_post_multiple_attachments
950
+ Runo.client = 'root'
951
+ sd = Runo::Set::Static::Folder.root.item('t_attachment', 'main')
952
+ sd.storage.clear
953
+
954
+ # create an attachment item
955
+ sd.update(
956
+ '_1' => {
957
+ 'files' => {'_1' => {:action => :create, 'file' => 'foo'}},
958
+ }
959
+ )
960
+ sd.commit :temp
961
+ first_id = sd.result.values.first.item('files').val.keys.sort.first
962
+ assert_equal(
963
+ {
964
+ first_id => {'file' => 'foo'},
965
+ },
966
+ sd.result.values.first.item('files').val,
967
+ 'Workflow::Attachment should keep the first item'
968
+ )
969
+
970
+ # create the second attachment
971
+ sd.update(
972
+ '_1' => {
973
+ 'files' => {'_1' => {:action => :create, 'file' => 'bar'}},
974
+ }
975
+ )
976
+ sd.commit :temp
977
+ second_id = sd.result.values.first.item('files').val.keys.sort.last
978
+
979
+ assert_equal(
980
+ first_id.succ,
981
+ second_id,
982
+ 'Workflow::Attachment should not overwrite the first file item'
983
+ )
984
+ assert_equal(
985
+ {
986
+ first_id => {'file' => 'foo'},
987
+ second_id => {'file' => 'bar'},
988
+ },
989
+ sd.result.values.first.item('files').val,
990
+ 'Workflow::Attachment should keep both the first item and the second item'
991
+ )
992
+
993
+ sd.commit :persistent
994
+ baz_id = sd.result.values.first[:id]
995
+
996
+ item = Runo::Set::Static::Folder.root.item('t_attachment', 'main', baz_id, 'files', first_id, 'file')
997
+ assert_equal(
998
+ 'foo',
999
+ item.val,
1000
+ 'Workflow::Attachment should store the body of the first file item'
1001
+ )
1002
+
1003
+ item = Runo::Set::Static::Folder.root.item('t_attachment', 'main', baz_id, 'files', second_id, 'file')
1004
+ assert_equal(
1005
+ 'bar',
1006
+ item.val,
1007
+ 'Workflow::Attachment should store the body of the second file item'
1008
+ )
1009
+ end
1010
+
1011
+ def test_load_default
1012
+ end
1013
+
1014
+ def test_load
1015
+ @sd.load('1235' => {'name' => 'carl'})
1016
+ assert_equal(
1017
+ {'1235' => {'name' => 'carl'}},
1018
+ @sd.val,
1019
+ 'Set::Dynamic#load should load the storage with the given values'
1020
+ )
1021
+ @sd.load('1234' => {'name' => 'frank'})
1022
+ assert_equal(
1023
+ {'1234' => {'name' => 'frank'}},
1024
+ @sd.val,
1025
+ 'Set::Dynamic#load should overwrite all values in the storage'
1026
+ )
1027
+ end
1028
+
1029
+ def test_create
1030
+ s = @sd.storage
1031
+ def s.new_id(v = {})
1032
+ @c ||= 0
1033
+ (@c += 1).to_s
1034
+ end
1035
+
1036
+ @sd.create({})
1037
+ assert_equal(
1038
+ {},
1039
+ @sd.val,
1040
+ 'Set::Dynamic#create should build the empty storage by default'
1041
+ )
1042
+
1043
+ @sd.create('_1235' => {'name' => 'carl'})
1044
+ assert_equal(
1045
+ {'_owner' => 'root', 'name' => 'carl', 'comment' => 'hi.'},
1046
+ @sd.item('_1235').val,
1047
+ 'Set::Dynamic#create should create the new items in the empty storage'
1048
+ )
1049
+ @sd.commit
1050
+ assert_equal(
1051
+ {'1' => {'_owner' => 'root', 'name' => 'carl', 'comment' => 'hi.'}},
1052
+ @sd.val,
1053
+ 'Set::Dynamic#create should create the new items in the empty storage'
1054
+ )
1055
+
1056
+ @sd.create('_1234' => {'name' => 'frank'})
1057
+ assert_equal(
1058
+ {'_owner' => 'root', 'name' => 'frank', 'comment' => 'hi.'},
1059
+ @sd.item('_1234').val,
1060
+ 'Set::Dynamic#create should create the new items in the empty storage'
1061
+ )
1062
+ assert_equal(
1063
+ {},
1064
+ @sd.val,
1065
+ 'Set::Dynamic#val should be empty before the commit'
1066
+ )
1067
+ @sd.commit
1068
+ assert_equal(
1069
+ {'2' => {'_owner' => 'root', 'name' => 'frank', 'comment' => 'hi.'}},
1070
+ @sd.val,
1071
+ 'Set::Dynamic#create should overwrite all items in the storage'
1072
+ )
1073
+
1074
+ @sd.create('_2' => {'name' => 'frank'}, '_1' => {'name' => 'bobby'})
1075
+ assert_equal(
1076
+ {},
1077
+ @sd.val,
1078
+ 'Set::Dynamic#val should be empty before the commit'
1079
+ )
1080
+ @sd.commit
1081
+ assert_equal(
1082
+ {
1083
+ '4' => {'_owner' => 'root', 'name' => 'frank', 'comment' => 'hi.'},
1084
+ '3' => {'_owner' => 'root', 'name' => 'bobby', 'comment' => 'hi.'},
1085
+ },
1086
+ @sd.val,
1087
+ 'Set::Dynamic#create should create multiple items in the empty storage'
1088
+ )
1089
+ end
1090
+
1091
+ def test_update
1092
+ @sd.load(
1093
+ '20091122_1234' => {'name' => 'frank', 'comment' => 'bar'},
1094
+ '20091122_1235' => {'name' => 'carl', 'comment' => 'baz'}
1095
+ )
1096
+ s = @sd.storage
1097
+ def s.new_id(v = {})
1098
+ 'new!'
1099
+ end
1100
+
1101
+ # update an item
1102
+ @sd.update('20091122_1234' => {'comment' => 'qux'})
1103
+ assert_equal(
1104
+ {'name' => 'frank', 'comment' => 'qux'},
1105
+ @sd.item('20091122_1234').val,
1106
+ 'Set::Dynamic#update should update the values of the item instance'
1107
+ )
1108
+ assert_equal(
1109
+ :update,
1110
+ @sd.item('20091122_1234').action,
1111
+ 'Set::Dynamic#update should set a proper action on the item'
1112
+ )
1113
+ assert_equal(
1114
+ nil,
1115
+ @sd.item('20091122_1234', 'name').action,
1116
+ 'Set::Dynamic#update should set a proper action on the item'
1117
+ )
1118
+ assert_equal(
1119
+ :update,
1120
+ @sd.item('20091122_1234', 'comment').action,
1121
+ 'Set::Dynamic#update should set a proper action on the item'
1122
+ )
1123
+
1124
+ # create an item
1125
+ @sd.update('_1236' => {'name' => 'roy'})
1126
+ assert_equal(
1127
+ {'_owner' => 'root', 'name' => 'roy', 'comment' => 'hi.'},
1128
+ @sd.item('_1236').val,
1129
+ 'Set::Dynamic#update should update the values of the item instance'
1130
+ )
1131
+ assert_equal(
1132
+ :create,
1133
+ @sd.item('_1236').action,
1134
+ 'Set::Dynamic#update should set a proper action on the item'
1135
+ )
1136
+ assert_equal(
1137
+ :create,
1138
+ @sd.item('_1236', 'name').action,
1139
+ 'Set::Dynamic#update should set a proper action on the item'
1140
+ )
1141
+ assert_equal(
1142
+ nil,
1143
+ @sd.item('_1236', 'comment').action,
1144
+ 'Set::Dynamic#update should set a proper action on the item'
1145
+ )
1146
+
1147
+ # delete an item
1148
+ @sd.update('20091122_1235' => {:action => :delete})
1149
+ assert_equal(
1150
+ {'name' => 'carl', 'comment' => 'baz'},
1151
+ @sd.item('20091122_1235').val,
1152
+ 'Set::Dynamic#update should not update the values of the item when deleting'
1153
+ )
1154
+ assert_equal(
1155
+ :delete,
1156
+ @sd.item('20091122_1235').action,
1157
+ 'Set::Dynamic#update should set a proper action on the item'
1158
+ )
1159
+
1160
+ # before the commit
1161
+ assert_equal(
1162
+ :update,
1163
+ @sd.action,
1164
+ 'Set::Dynamic#update should set a proper action'
1165
+ )
1166
+ assert_nil(
1167
+ @sd.result,
1168
+ 'Set::Dynamic#result should return nil before the commit'
1169
+ )
1170
+ assert_equal(
1171
+ {
1172
+ '20091122_1234' => {'name' => 'frank', 'comment' => 'bar'},
1173
+ '20091122_1235' => {'name' => 'carl', 'comment' => 'baz'},
1174
+ },
1175
+ @sd.val,
1176
+ 'Set::Dynamic#update should not touch the original values in the storage'
1177
+ )
1178
+
1179
+ @sd.commit :temp
1180
+
1181
+ # after the commit
1182
+ assert(
1183
+ @sd.pending?,
1184
+ 'Set::Dynamic#commit(:temp) should keep the pending status of the items'
1185
+ )
1186
+ assert_equal(
1187
+ {
1188
+ '20091122_1234' => {'name' => 'frank', 'comment' => 'qux'},
1189
+ 'new!' => {'name' => 'roy', 'comment' => 'hi.', '_owner' => 'root'},
1190
+ },
1191
+ @sd.val,
1192
+ 'Set::Dynamic#commit should update the original values in the storage'
1193
+ )
1194
+ assert_equal(
1195
+ {
1196
+ '20091122_1234' => @sd.item('20091122_1234'),
1197
+ '20091122_1235' => @sd.instance_eval { @item_object['20091122_1235'] },
1198
+ '_1236' => @sd.item('_1236'),
1199
+ },
1200
+ @sd.result,
1201
+ 'Set::Dynamic#result should return a hash of the committed items when :update'
1202
+ ) if nil
1203
+ assert_equal(
1204
+ {'comment' => @sd.item('20091122_1234', 'comment')},
1205
+ @sd.item('20091122_1234').result,
1206
+ 'Set::Static#result should return a hash of the committed items when :update'
1207
+ )
1208
+ assert_equal(
1209
+ :delete,
1210
+ @sd.result['20091122_1235'].result,
1211
+ 'Set::Static#result should return the committed action unless :update'
1212
+ )
1213
+ assert_equal(
1214
+ :create,
1215
+ @sd.result['_1236'].result,
1216
+ 'Set::Static#result should return the committed action unless :update'
1217
+ )
1218
+ end
1219
+
1220
+ def test_update_with_eclectic_val
1221
+ @sd.load(
1222
+ '20091122_1234' => {'name' => 'frank', 'comment' => 'bar'},
1223
+ '20091122_1235' => {'name' => 'carl', 'comment' => 'baz'}
1224
+ )
1225
+ s = @sd.storage
1226
+
1227
+ assert_nothing_raised(
1228
+ 'Set::Dynamic#update should work with values other than sub-items'
1229
+ ) {
1230
+ @sd.update('20091122_1234' => {'comment' => 'qux'}, :conds => {}, :action => nil)
1231
+ }
1232
+ assert_equal(
1233
+ {'name' => 'frank', 'comment' => 'qux'},
1234
+ @sd.item('20091122_1234').val,
1235
+ 'Set::Dynamic#update should update the values of the item instance'
1236
+ )
1237
+ end
1238
+
1239
+ def test_delete
1240
+ @sd.delete
1241
+ assert_equal(
1242
+ :delete,
1243
+ @sd.action,
1244
+ 'Set::Dynamic#delete should set @action'
1245
+ )
1246
+ end
1247
+
1248
+ def test_delete_invalid_item
1249
+ @sd.load(
1250
+ '20091122_1234' => {'name' => 'frank', 'comment' => 'bar'}
1251
+ )
1252
+
1253
+ # update with invalid value
1254
+ @sd.update(
1255
+ '20091122_1234' => {'name' => 'too looooooooooooooooooooong'}
1256
+ )
1257
+ assert(!@sd.valid?)
1258
+
1259
+ # delete the invalid item
1260
+ @sd.update(
1261
+ '20091122_1234' => {:action => :delete}
1262
+ )
1263
+ assert_equal(
1264
+ {},
1265
+ @sd.errors,
1266
+ 'Set::Dynamic#errors should ignore items with :delete action'
1267
+ )
1268
+
1269
+ @sd.commit
1270
+ assert(
1271
+ @sd.valid?,
1272
+ 'Set::Dynamic#commit should be able to delete an invalid item'
1273
+ )
1274
+ assert_equal(
1275
+ {},
1276
+ @sd.val,
1277
+ 'Set::Dynamic#commit should be able to delete an invalid item'
1278
+ )
1279
+ end
1280
+
1281
+ def test_get_by_nobody
1282
+ @sd.load(
1283
+ '20091122_0001' => {'_owner' => 'frank', 'comment' => 'bar'},
1284
+ '20091122_0002' => {'_owner' => 'carl', 'comment' => 'baz'}
1285
+ )
1286
+ Runo.client = nil
1287
+
1288
+ arg = {:action => :update, :conds => {:d => '2009'}}
1289
+ assert_raise(
1290
+ Runo::Error::Forbidden,
1291
+ 'Set::Dynamic#get should raise Error::Forbidden when sd[:client] is nobody'
1292
+ ) {
1293
+ @sd.get arg
1294
+ }
1295
+ end
1296
+
1297
+ def test_post_by_nobody
1298
+ @sd.load(
1299
+ '20091122_0001' => {'_owner' => 'frank', 'comment' => 'bar'},
1300
+ '20091122_0002' => {'_owner' => 'carl', 'comment' => 'baz'}
1301
+ )
1302
+ Runo.client = nil
1303
+
1304
+ assert_raise(
1305
+ Runo::Error::Forbidden,
1306
+ "'nobody' should not create a new item"
1307
+ ) {
1308
+ @sd.update('_0001' => {'comment' => 'qux'})
1309
+ }
1310
+ assert_raise(
1311
+ Runo::Error::Forbidden,
1312
+ "'nobody' should not update frank's item"
1313
+ ) {
1314
+ @sd.update('20091122_0001' => {'comment' => 'qux'})
1315
+ }
1316
+ assert_raise(
1317
+ Runo::Error::Forbidden,
1318
+ "'nobody' should not delete frank's item"
1319
+ ) {
1320
+ @sd.update('20091122_0001' => {:action => :delete})
1321
+ }
1322
+ end
1323
+
1324
+ def test_get_by_carl
1325
+ @sd.load(
1326
+ '20091122_0001' => {'_owner' => 'frank', 'comment' => 'bar'},
1327
+ '20091122_0002' => {'_owner' => 'carl', 'comment' => 'baz'}
1328
+ )
1329
+ Runo.client = 'carl' # carl is not the member of the group
1330
+
1331
+ arg = {}
1332
+ @sd.get arg
1333
+ assert_equal(
1334
+ :read,
1335
+ arg[:action],
1336
+ 'Set::Dynamic#get should set the default action'
1337
+ )
1338
+
1339
+ arg = {:action => :create}
1340
+ assert_raise(
1341
+ Runo::Error::Forbidden,
1342
+ 'Set::Dynamic#get should raise Error::Forbidden when an action is given but forbidden'
1343
+ ) {
1344
+ @sd.get arg
1345
+ }
1346
+
1347
+ arg = {:action => :update, :conds => {:d => '2009'}}
1348
+ assert_raise(
1349
+ Runo::Error::Forbidden,
1350
+ 'Set::Dynamic#get should not keep the partially-permitted action'
1351
+ ) {
1352
+ @sd.get arg
1353
+ }
1354
+
1355
+ arg = {:action => :update, :conds => {:id => '20091122_0002'}}
1356
+ @sd.get arg
1357
+ assert_equal(
1358
+ :update,
1359
+ arg[:action],
1360
+ 'Set::Dynamic#get should keep the permitted action'
1361
+ )
1362
+ end
1363
+
1364
+ def test_post_by_carl
1365
+ @sd.load(
1366
+ '20091122_0001' => {'_owner' => 'frank', 'comment' => 'bar'},
1367
+ '20091122_0002' => {'_owner' => 'carl', 'comment' => 'baz'}
1368
+ )
1369
+ Runo.client = 'carl' # carl is not the member of the group
1370
+
1371
+ assert_raise(
1372
+ Runo::Error::Forbidden,
1373
+ 'carl should not create a new item'
1374
+ ) {
1375
+ @sd.update('_0001' => {'comment' => 'qux'})
1376
+ }
1377
+ assert_raise(
1378
+ Runo::Error::Forbidden,
1379
+ "carl should not update frank's item"
1380
+ ) {
1381
+ @sd.update('20091122_0001' => {'comment' => 'qux'})
1382
+ }
1383
+ assert_nothing_raised(
1384
+ 'carl should be able to update his own item'
1385
+ ) {
1386
+ @sd.update('20091122_0002' => {'comment' => 'qux'})
1387
+ }
1388
+ assert_raise(
1389
+ Runo::Error::Forbidden,
1390
+ "carl should not delete frank's item"
1391
+ ) {
1392
+ @sd.update('20091122_0001' => {:action => :delete})
1393
+ }
1394
+ end
1395
+
1396
+ def test_get_by_roy
1397
+ @sd.load(
1398
+ '20091122_0001' => {'_owner' => 'frank', 'comment' => 'bar'},
1399
+ '20091122_0002' => {'_owner' => 'carl', 'comment' => 'baz'}
1400
+ )
1401
+ Runo.client = 'roy' # roy belongs to the group
1402
+
1403
+ arg = {:action => :create}
1404
+ @sd.get arg
1405
+ assert_equal(
1406
+ :create,
1407
+ arg[:action],
1408
+ 'Set::Dynamic#get should keep the permitted action'
1409
+ )
1410
+
1411
+ arg = {:action => :delete, :conds => {:d => '2009'}}
1412
+ assert_raise(
1413
+ Runo::Error::Forbidden,
1414
+ 'Set::Dynamic#get should raise Error::Forbidden when an action is given but forbidden'
1415
+ ) {
1416
+ @sd.get arg
1417
+ }
1418
+ end
1419
+
1420
+ def test_post_by_roy
1421
+ @sd.load(
1422
+ '20091122_0001' => {'_owner' => 'frank', 'comment' => 'bar'},
1423
+ '20091122_0002' => {'_owner' => 'carl', 'comment' => 'baz'}
1424
+ )
1425
+ Runo.client = 'roy' # roy belongs to the group
1426
+
1427
+ assert_nothing_raised(
1428
+ 'roy should be able to create a new item'
1429
+ ) {
1430
+ @sd.update('_0001' => {'comment' => 'qux'})
1431
+ }
1432
+ assert_raise(
1433
+ Runo::Error::Forbidden,
1434
+ "roy should not update frank's item"
1435
+ ) {
1436
+ @sd.update('20091122_0001' => {'comment' => 'qux'})
1437
+ }
1438
+ assert_raise(
1439
+ Runo::Error::Forbidden,
1440
+ "roy should not delete frank's item"
1441
+ ) {
1442
+ @sd.update('20091122_0001' => {:action => :delete})
1443
+ }
1444
+ end
1445
+
1446
+ def test_get_by_root
1447
+ @sd.load(
1448
+ '20091122_0001' => {'_owner' => 'frank', 'comment' => 'bar'},
1449
+ '20091122_0002' => {'_owner' => 'carl', 'comment' => 'baz'}
1450
+ )
1451
+ Runo.client = 'root' # root is the admin
1452
+
1453
+ arg = {:action => :create, :db => 1}
1454
+ @sd.get arg
1455
+ assert_equal(
1456
+ :create,
1457
+ arg[:action],
1458
+ 'Set::Dynamic#get should keep the permitted action'
1459
+ )
1460
+
1461
+ arg = {:action => :delete, :conds => {:d => '2009'}}
1462
+ @sd.get arg
1463
+ assert_equal(
1464
+ :delete,
1465
+ arg[:action],
1466
+ 'Set::Dynamic#get should keep the permitted action'
1467
+ )
1468
+ end
1469
+
1470
+ def test_post_by_root
1471
+ @sd.load(
1472
+ '20091122_0001' => {'_owner' => 'frank', 'comment' => 'bar'},
1473
+ '20091122_0002' => {'_owner' => 'carl', 'comment' => 'baz'}
1474
+ )
1475
+ Runo.client = 'root' # root is the admin
1476
+
1477
+ assert_nothing_raised(
1478
+ 'frank should be able to create a new item'
1479
+ ) {
1480
+ @sd.update('_0001' => {'comment' => 'qux'})
1481
+ }
1482
+ assert_nothing_raised(
1483
+ 'frank should be able to update his own item'
1484
+ ) {
1485
+ @sd.update('20091122_0001' => {'comment' => 'qux'})
1486
+ }
1487
+ assert_nothing_raised(
1488
+ "frank should be able to update carl's item"
1489
+ ) {
1490
+ @sd.update('20091122_0002' => {'comment' => 'qux'})
1491
+ }
1492
+ assert_nothing_raised(
1493
+ 'frank should be able to delete his own item'
1494
+ ) {
1495
+ @sd.update('20091122_0001' => {:action => :delete})
1496
+ }
1497
+ assert_nothing_raised(
1498
+ "frank should be able to delete carl's item"
1499
+ ) {
1500
+ @sd.update('20091122_0002' => {:action => :delete})
1501
+ }
1502
+ end
1503
+
1504
+ end