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,527 @@
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_Complex < Test::Unit::TestCase
9
+
10
+ class ::Runo::Set::Dynamic
11
+ def _g_vegetable(arg)
12
+ "'potato'"
13
+ end
14
+ end
15
+
16
+ class ::Runo::Workflow::Pipco < ::Runo::Workflow
17
+ DEFAULT_SUB_ITEMS = {
18
+ '_owner' => {:klass => 'meta-owner'},
19
+ }
20
+ PERM = {
21
+ :create => 0b11000,
22
+ :read => 0b11110,
23
+ :update => 0b11100,
24
+ :delete => 0b10100,
25
+ }
26
+ def _g_submit(arg)
27
+ '[pipco]'
28
+ end
29
+ end
30
+
31
+ class ::Runo::Tomago < ::Runo::Field
32
+ def _get(arg)
33
+ args = arg.keys.collect {|k| "#{k}=#{arg[k]}" }.sort
34
+ "'#{val}'(#{args.join ', '})"
35
+ end
36
+ end
37
+
38
+ def setup
39
+ # Set::Dynamic of Set::Static of (Scalar and (Set::Dynamic of Set::Static of Scalar))
40
+ @sd = Runo::Set::Dynamic.new(
41
+ :id => 'main',
42
+ :klass => 'set-dynamic',
43
+ :workflow => 'pipco',
44
+ :group => ['roy', 'don'],
45
+ :tmpl => {
46
+ :index => <<'_tmpl'.chomp,
47
+ <ul id="@(name)" class="runo-pipco">
48
+ $()</ul>
49
+ $(.navi)$(.submit)$(.action_create)
50
+ _tmpl
51
+ },
52
+ :item => {
53
+ 'default' => Runo::Parser.parse_html(<<'_html')
54
+ <li id="@(name)">
55
+ $(name = tomago 32 :'nobody'): $(comment = tomago 64 :'hello.')
56
+ <ul id="files" class="runo-attachment">
57
+ <li id="@(name)">$(file = tomago :'foo.jpg')</li>
58
+ </ul>
59
+ <ul id="replies" class="runo-pipco">
60
+ <li id="@(name)">$(reply = tomago :'hi.')</li>
61
+ </ul>
62
+ $(replies.vegetable)
63
+ </li>
64
+ _html
65
+ }
66
+ )
67
+ @sd.load(
68
+ '20091123_0001' => {
69
+ '_owner' => 'carl',
70
+ 'name' => 'CZ',
71
+ 'comment' => 'oops',
72
+ 'files' => {
73
+ '20091123_0001' => {'file' => 'carl1.jpg'},
74
+ '20091123_0002' => {'file' => 'carl2.jpg'},
75
+ },
76
+ 'replies' => {
77
+ '20091125_0001' => {'_owner' => 'bobby', 'reply' => 'howdy.'},
78
+ },
79
+ },
80
+ '20091123_0002' => {
81
+ '_owner' => 'roy',
82
+ 'name' => 'RE',
83
+ 'comment' => 'wee',
84
+ 'files' => {
85
+ '20091123_0001' => {'file' => 'roy.png'},
86
+ },
87
+ 'replies' => {
88
+ '20091125_0001' => {'_owner' => 'don', 'reply' => 'ho ho.'},
89
+ '20091125_0002' => {'_owner' => 'roy', 'reply' => 'oops.'},
90
+ },
91
+ }
92
+ )
93
+
94
+ [
95
+ @sd,
96
+ @sd.item('20091123_0001', 'files'),
97
+ @sd.item('20091123_0001', 'replies'),
98
+ @sd.item('20091123_0002', 'files'),
99
+ @sd.item('20091123_0002', 'replies'),
100
+ ].each {|sd|
101
+ sd[:tmpl][:action_create] = ''
102
+ sd[:tmpl][:navi] = ''
103
+ sd[:tmpl][:submit_create] = '[c]'
104
+ sd[:tmpl][:submit_delete] = '[d]'
105
+ def sd._g_submit(arg)
106
+ "[#{my[:id]}-#{arg[:orig_action]}]\n"
107
+ end
108
+
109
+ sd.each {|item|
110
+ item[:tmpl][:action_update] = ''
111
+ }
112
+ }
113
+ end
114
+
115
+ def teardown
116
+ Runo.client = nil
117
+ end
118
+
119
+ def test_get_default
120
+ Runo.client = 'root' #nil
121
+ result = @sd.get
122
+
123
+ assert_match(
124
+ /'potato'/,
125
+ result,
126
+ 'Set#get should include $(foo.baz) whenever the action :baz is permitted'
127
+ )
128
+ assert_equal(
129
+ <<'_html',
130
+ <ul id="main" class="runo-pipco">
131
+ <li id="main-20091123_0001">
132
+ 'CZ'(action=read, p_action=read): 'oops'(action=read, p_action=read)
133
+ <ul id="main-20091123_0001-files" class="runo-attachment">
134
+ <li id="main-20091123_0001-files-20091123_0001">'carl1.jpg'(action=read, p_action=read)</li>
135
+ <li id="main-20091123_0001-files-20091123_0002">'carl2.jpg'(action=read, p_action=read)</li>
136
+ </ul>
137
+ <ul id="main-20091123_0001-replies" class="runo-pipco">
138
+ <li id="main-20091123_0001-replies-20091125_0001"><a href="/20091123_0001/replies/20091125/1/update.html">'howdy.'(action=read, p_action=read)</a></li>
139
+ </ul>
140
+ 'potato'
141
+ </li>
142
+ <li id="main-20091123_0002">
143
+ 'RE'(action=read, p_action=read): 'wee'(action=read, p_action=read)
144
+ <ul id="main-20091123_0002-files" class="runo-attachment">
145
+ <li id="main-20091123_0002-files-20091123_0001">'roy.png'(action=read, p_action=read)</li>
146
+ </ul>
147
+ <ul id="main-20091123_0002-replies" class="runo-pipco">
148
+ <li id="main-20091123_0002-replies-20091125_0001"><a href="/20091123_0002/replies/20091125/1/update.html">'ho ho.'(action=read, p_action=read)</a></li>
149
+ <li id="main-20091123_0002-replies-20091125_0002"><a href="/20091123_0002/replies/20091125/2/update.html">'oops.'(action=read, p_action=read)</a></li>
150
+ </ul>
151
+ 'potato'
152
+ </li>
153
+ </ul>
154
+ _html
155
+ result,
156
+ 'Set#get should work recursively as a part of the complex'
157
+ )
158
+ end
159
+
160
+ def test_get_with_parent_action
161
+ Runo.client = 'root'
162
+ result = @sd.get(:action => :update)
163
+
164
+ assert_match(
165
+ /id="main-20091123_0001-files"/,
166
+ result,
167
+ 'Set::Dynamic#get(:action => :update) should include child attachments'
168
+ )
169
+ assert_no_match(
170
+ /id="main-20091123_0001-replies"/,
171
+ result,
172
+ 'Set::Dynamic#get(:action => :update) should not include child apps'
173
+ )
174
+ assert_no_match(
175
+ /'potato'/,
176
+ result,
177
+ 'Set::Dynamic#get(:action => :update) should not include any value of child apps'
178
+ )
179
+ assert_no_match(
180
+ /<form.+<form/m,
181
+ result,
182
+ 'Set::Dynamic#get(:action => :update) should not return nested forms'
183
+ )
184
+ assert_equal(
185
+ <<'_html',
186
+ <ul id="main" class="runo-pipco">
187
+ <li id="main-20091123_0001">
188
+ 'CZ'(action=update, p_action=update): 'oops'(action=update, p_action=update)
189
+ <ul id="main-20091123_0001-files" class="runo-attachment">
190
+ <li id="main-20091123_0001-files-20091123_0001">'carl1.jpg'(action=update, p_action=update)[d]</li>
191
+ <li id="main-20091123_0001-files-20091123_0002">'carl2.jpg'(action=update, p_action=update)[d]</li>
192
+ <li id="main-20091123_0001-files-_001">'foo.jpg'(action=create, p_action=create)[c]</li>
193
+ </ul>
194
+ </li>
195
+ <li id="main-20091123_0002">
196
+ 'RE'(action=update, p_action=update): 'wee'(action=update, p_action=update)
197
+ <ul id="main-20091123_0002-files" class="runo-attachment">
198
+ <li id="main-20091123_0002-files-20091123_0001">'roy.png'(action=update, p_action=update)[d]</li>
199
+ <li id="main-20091123_0002-files-_001">'foo.jpg'(action=create, p_action=create)[c]</li>
200
+ </ul>
201
+ </li>
202
+ </ul>
203
+ [main-update]
204
+ _html
205
+ result,
206
+ 'Set#get should distribute the action to its items'
207
+ )
208
+ end
209
+
210
+ def test_get_with_partial_permission
211
+ Runo.client = 'carl' # can edit only his own item
212
+
213
+ assert_raise(
214
+ Runo::Error::Forbidden,
215
+ 'Field#get should raise Error::Forbidden when an action is given but forbidden'
216
+ ) {
217
+ @sd.get(:action => :update, :conds => {:id => '20091123_0002'})
218
+ }
219
+
220
+ @sd.item('20091123_0002', 'comment')[:owner] = 'carl' # enclave in roy's item
221
+
222
+ assert_raise(
223
+ Runo::Error::Forbidden,
224
+ 'Field#get should not allow partially permitted get'
225
+ ) {
226
+ @sd.get(:action => :update, :conds => {:id => '20091123_0002'})
227
+ }
228
+ end
229
+
230
+ def test_get_with_partial_action
231
+ Runo.client = 'root'
232
+
233
+ Runo.current[:base] = @sd.item('20091123_0002', 'replies')
234
+ Runo.base[:tid] = '123.45'
235
+
236
+ result = @sd.get(
237
+ '20091123_0002' => {
238
+ 'replies' => {
239
+ :action => :update,
240
+ :conds => {:id => '20091125_0002'},
241
+ },
242
+ }
243
+ )
244
+ assert_equal(
245
+ <<_html,
246
+ <ul id="main" class="runo-pipco">
247
+ <li id="main-20091123_0001">
248
+ 'CZ'(action=read, p_action=read): 'oops'(action=read, p_action=read)
249
+ <ul id="main-20091123_0001-files" class="runo-attachment">
250
+ <li id="main-20091123_0001-files-20091123_0001">'carl1.jpg'(action=read, p_action=read)</li>
251
+ <li id="main-20091123_0001-files-20091123_0002">'carl2.jpg'(action=read, p_action=read)</li>
252
+ </ul>
253
+ <ul id="main-20091123_0001-replies" class="runo-pipco">
254
+ <li id="main-20091123_0001-replies-20091125_0001"><a href="/20091123_0001/replies/20091125/1/update.html">'howdy.'(action=read, p_action=read)</a></li>
255
+ </ul>
256
+ 'potato'
257
+ </li>
258
+ <li id="main-20091123_0002">
259
+ 'RE'(action=read, p_action=read): 'wee'(action=read, p_action=read)
260
+ <ul id="main-20091123_0002-files" class="runo-attachment">
261
+ <li id="main-20091123_0002-files-20091123_0001">'roy.png'(action=read, p_action=read)</li>
262
+ </ul>
263
+ <form id="form_main-20091123_0002-replies" method="post" enctype="multipart/form-data" action="/20091123_0002/replies/123.45/update.html">
264
+ <input name="_token" type="hidden" value="#{Runo.token}" />
265
+ <ul id="main-20091123_0002-replies" class="runo-pipco">
266
+ <li id="main-20091123_0002-replies-20091125_0002"><a>'oops.'(action=update, p_action=update)</a></li>
267
+ </ul>
268
+ [replies-update]
269
+ </form>
270
+ 'potato'
271
+ </li>
272
+ </ul>
273
+ _html
274
+ result,
275
+ 'Field#get should be able to handle a partial action'
276
+ )
277
+
278
+ result = @sd.get(
279
+ :conds => {:id => '20091123_0002'},
280
+ '20091123_0002' => {
281
+ 'replies' => {
282
+ :action => :update,
283
+ :conds => {:id => '20091125_0002'},
284
+ },
285
+ }
286
+ )
287
+ assert_equal(
288
+ <<_html,
289
+ <ul id="main" class="runo-pipco">
290
+ <li id="main-20091123_0002">
291
+ 'RE'(action=read, p_action=read): 'wee'(action=read, p_action=read)
292
+ <ul id="main-20091123_0002-files" class="runo-attachment">
293
+ <li id="main-20091123_0002-files-20091123_0001">'roy.png'(action=read, p_action=read)</li>
294
+ </ul>
295
+ <form id="form_main-20091123_0002-replies" method="post" enctype="multipart/form-data" action="/20091123_0002/replies/123.45/update.html">
296
+ <input name="_token" type="hidden" value="#{Runo.token}" />
297
+ <ul id="main-20091123_0002-replies" class="runo-pipco">
298
+ <li id="main-20091123_0002-replies-20091125_0002"><a>'oops.'(action=update, p_action=update)</a></li>
299
+ </ul>
300
+ [replies-update]
301
+ </form>
302
+ 'potato'
303
+ </li>
304
+ </ul>
305
+ _html
306
+ result,
307
+ 'Field#get should be able to handle a partial action'
308
+ )
309
+ end
310
+
311
+ def test_get_partial_forbidden
312
+ Runo.client = 'carl'
313
+ assert_match(
314
+ /\(action=update/,
315
+ @sd.item('20091123_0001', 'files').get(:action => :update)
316
+ )
317
+ assert_match(
318
+ /\(action=update/,
319
+ @sd.item('20091123_0001', 'files', '20091123_0001').get(:action => :update)
320
+ )
321
+
322
+ @sd.instance_variable_set(:@item_object, {}) # remove item('_001')
323
+
324
+ Runo.client = nil
325
+ assert_raise(
326
+ Runo::Error::Forbidden,
327
+ 'Field#get should not show an inner attachment when the parent is forbidden'
328
+ ) {
329
+ @sd.item('20091123_0001', 'files').get(:action => :update)
330
+ }
331
+ assert_raise(
332
+ Runo::Error::Forbidden,
333
+ 'Field#get should not show an inner attachment when the parent is forbidden'
334
+ ) {
335
+ @sd.item('20091123_0001', 'files', '20091123_0001').get(:action => :update)
336
+ }
337
+ end
338
+
339
+ def test_post_partial
340
+ Runo.client = 'don'
341
+ original_val = YAML.load @sd.val.to_yaml
342
+ @sd.update(
343
+ '20091123_0002' => {
344
+ 'replies' => {
345
+ '_0001' => {'reply' => 'yum.'},
346
+ },
347
+ }
348
+ )
349
+ assert_equal(
350
+ original_val,
351
+ @sd.val,
352
+ 'Field#val should not change before the commit'
353
+ )
354
+ @sd.commit
355
+ assert_not_equal(
356
+ original_val,
357
+ @sd.val,
358
+ 'Field#val should change after the commit'
359
+ )
360
+ end
361
+
362
+ def test_post_attachment_forbidden
363
+ Runo.client = nil
364
+ assert_raise(
365
+ Runo::Error::Forbidden,
366
+ 'Field#post to an inner attachment w/o the perm of the parent should be forbidden'
367
+ ) {
368
+ @sd.update(
369
+ '20091123_0002' => {
370
+ 'files' => {
371
+ '_0001' => {'file' => 'evil.jpg'},
372
+ },
373
+ }
374
+ )
375
+ }
376
+ assert_raise(
377
+ Runo::Error::Forbidden,
378
+ 'Field#post to an inner attachment w/o the perm of the parent should be forbidden'
379
+ ) {
380
+ @sd.update(
381
+ '20091123_0002' => {
382
+ 'files' => {
383
+ '20091123_0001' => {'file' => 'evil.png'},
384
+ }
385
+ }
386
+ )
387
+ }
388
+ assert_raise(
389
+ Runo::Error::Forbidden,
390
+ 'Field#post to an inner attachment w/o the perm of the parent should be forbidden'
391
+ ) {
392
+ @sd.item('20091123_0002', 'files', '20091123_0001').update('file' => 'evil.gif')
393
+ }
394
+ end
395
+
396
+ def test_commit_partial
397
+ Runo.client = 'don'
398
+ @sd.update(
399
+ '20091123_0002' => {
400
+ 'replies' => {
401
+ '_0001' => {'reply' => 'yum.'},
402
+ },
403
+ }
404
+ )
405
+ orig_val = @sd.val('20091123_0002', 'replies').dup
406
+
407
+ @sd.commit :temp
408
+ new_val = @sd.val('20091123_0002', 'replies').dup
409
+ assert_equal(
410
+ orig_val.size + 1,
411
+ new_val.size,
412
+ 'Field#val should change after the commit :temp'
413
+ )
414
+
415
+ new_id = new_val.keys.find {|id| new_val[id] == {'_owner' => 'don', 'reply' => 'yum.'} }
416
+ @sd.update(
417
+ '20091123_0002' => {
418
+ 'replies' => {
419
+ new_id => {
420
+ :action => :delete,
421
+ 'reply' => 'yum.',
422
+ },
423
+ },
424
+ }
425
+ )
426
+
427
+ @sd.commit :temp
428
+ new_val = @sd.val('20091123_0002', 'replies').dup
429
+ assert_equal(
430
+ orig_val,
431
+ new_val,
432
+ 'Field#val should change after the commit :temp'
433
+ )
434
+ end
435
+
436
+ def test_post_mixed
437
+ Runo.client = 'don'
438
+
439
+ # create a sub-item on the pending item
440
+ @sd.update(
441
+ '_1234' => {
442
+ '_owner' => 'don',
443
+ 'replies' => {
444
+ '_0001' => {
445
+ '_owner' => 'don',
446
+ 'reply' => 'yum.',
447
+ },
448
+ },
449
+ }
450
+ )
451
+ orig_val = @sd.val('_1234', 'replies').dup
452
+ assert_equal(
453
+ {},
454
+ orig_val,
455
+ 'Field#val should change after the commit :temp'
456
+ )
457
+
458
+ orig_storage = @sd.storage
459
+ @sd.instance_variable_set(:@storage, nil) # pretend persistent
460
+ @sd.commit :temp
461
+ @sd.instance_variable_set(:@storage, orig_storage)
462
+
463
+ new_val = @sd.val('_1234', 'replies').dup
464
+ assert_equal(
465
+ {'_owner' => 'don', 'reply' => 'yum.'},
466
+ new_val.values.first,
467
+ 'Field#val should change after the commit :temp'
468
+ )
469
+
470
+ # delete the sub-item
471
+ new_id = new_val.keys.find {|id| new_val[id] == {'_owner' => 'don', 'reply' => 'yum.'} }
472
+ @sd.update(
473
+ '_1234' => {
474
+ 'replies' => {
475
+ new_id => {
476
+ :action => :delete,
477
+ '_owner' => 'don',
478
+ 'reply' => 'yum.',
479
+ },
480
+ },
481
+ }
482
+ )
483
+ assert_equal(
484
+ :delete,
485
+ @sd.item('_1234', 'replies', new_id).action,
486
+ 'Set::Dynamic#post should not overwrite the action of descendant'
487
+ )
488
+
489
+ orig_storage = @sd.storage
490
+ @sd.instance_variable_set(:@storage, nil) # pretend persistent
491
+ @sd.commit :temp
492
+ @sd.instance_variable_set(:@storage, orig_storage)
493
+
494
+ new_val = @sd.val('_1234', 'replies').dup
495
+ assert_equal(
496
+ {},
497
+ new_val,
498
+ 'Field#val should change after the commit :temp'
499
+ )
500
+
501
+ # create an another sub-item
502
+ @sd.update(
503
+ '_1234' => {
504
+ '_owner' => 'don',
505
+ 'replies' => {
506
+ '_0001' => {
507
+ '_owner' => 'don',
508
+ 'reply' => 'yuck.',
509
+ },
510
+ },
511
+ }
512
+ )
513
+
514
+ orig_storage = @sd.storage
515
+ @sd.instance_variable_set(:@storage, nil) # pretend persistent
516
+ @sd.commit :temp
517
+ @sd.instance_variable_set(:@storage, orig_storage)
518
+
519
+ new_val = @sd.val('_1234', 'replies').dup
520
+ assert_equal(
521
+ {'_owner' => 'don', 'reply' => 'yuck.'},
522
+ new_val.values.first,
523
+ 'Field#val should change after the commit :temp'
524
+ )
525
+ end
526
+
527
+ end