bike 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +124 -0
  3. data/bin/bike +35 -0
  4. data/lib/_error.rb +14 -0
  5. data/lib/_field.rb +260 -0
  6. data/lib/_i18n.rb +144 -0
  7. data/lib/_parser.rb +256 -0
  8. data/lib/_path.rb +86 -0
  9. data/lib/_storage/_storage.rb +215 -0
  10. data/lib/_storage/file.rb +201 -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/bike.rb +396 -0
  32. data/lib/meta/_meta.rb +20 -0
  33. data/lib/meta/group.rb +19 -0
  34. data/lib/meta/id.rb +59 -0
  35. data/lib/meta/owner.rb +21 -0
  36. data/lib/meta/timestamp.rb +118 -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 +196 -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 +73 -0
  95. data/skel/skin/index.html +39 -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/qux/index.html +8 -0
  111. data/t/skin/foo/qux/moo/index.html +6 -0
  112. data/t/skin/foo/sub-20100306_0001.yaml +3 -0
  113. data/t/skin/index.yaml +3 -0
  114. data/t/skin/t_attachment/index.html +13 -0
  115. data/t/skin/t_contact/done.html +6 -0
  116. data/t/skin/t_contact/index.html +9 -0
  117. data/t/skin/t_file/index.html +16 -0
  118. data/t/skin/t_img/index.html +14 -0
  119. data/t/skin/t_img/test.jpg +0 -0
  120. data/t/skin/t_select/index.html +9 -0
  121. data/t/skin/t_store/index.html +9 -0
  122. data/t/skin/t_summary/20100326_0001.yaml +3 -0
  123. data/t/skin/t_summary/create.html +9 -0
  124. data/t/skin/t_summary/index.html +9 -0
  125. data/t/skin/t_summary/summary.html +9 -0
  126. data/t/t.rb +27 -0
  127. data/t/test_bike.rb +768 -0
  128. data/t/test_call.rb +1281 -0
  129. data/t/test_checkbox.rb +273 -0
  130. data/t/test_field.rb +330 -0
  131. data/t/test_file.rb +900 -0
  132. data/t/test_i18n.rb +325 -0
  133. data/t/test_id.rb +215 -0
  134. data/t/test_img.rb +328 -0
  135. data/t/test_meta.rb +57 -0
  136. data/t/test_parser.rb +1516 -0
  137. data/t/test_password.rb +188 -0
  138. data/t/test_radio.rb +226 -0
  139. data/t/test_role.rb +249 -0
  140. data/t/test_select.rb +182 -0
  141. data/t/test_set_complex.rb +527 -0
  142. data/t/test_set_dynamic.rb +1504 -0
  143. data/t/test_set_folder.rb +515 -0
  144. data/t/test_set_permit.rb +246 -0
  145. data/t/test_set_static.rb +468 -0
  146. data/t/test_storage.rb +915 -0
  147. data/t/test_text.rb +125 -0
  148. data/t/test_textarea.rb +138 -0
  149. data/t/test_timestamp.rb +473 -0
  150. data/t/test_workflow.rb +367 -0
  151. metadata +347 -0
@@ -0,0 +1,196 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ module Bike::Set
7
+
8
+ include Enumerable
9
+
10
+ def meta_dir
11
+ my[:folder][:dir] if my[:folder]
12
+ end
13
+
14
+ def meta_path
15
+ ['main', nil].include?(my[:name]) ?
16
+ my[:dir] :
17
+ "#{my[:dir]}/#{my[:name].sub(/^main-?/, '').gsub('-', '/')}"
18
+ end
19
+
20
+ def meta_base_path
21
+ Bike.base ? Bike.base[:path] : my[:path]
22
+ end
23
+
24
+ def val(*steps)
25
+ if steps.empty?
26
+ _val
27
+ elsif i = item(steps)
28
+ i.val
29
+ end
30
+ end
31
+
32
+ def item(*item_steps)
33
+ item_steps = item_steps.first if item_steps.first.is_a? ::Array
34
+ return self if item_steps.empty?
35
+
36
+ id, *item_steps = item_steps
37
+
38
+ if id.is_a?(::String) && child = collect_item(:id => id).first
39
+ item = item_steps.empty? ? child : child.item(*item_steps)
40
+ block_given? ? yield(item) : item
41
+ end
42
+ end
43
+
44
+ def pending?
45
+ !pending_items.empty? || action
46
+ end
47
+
48
+ def errors
49
+ return {} if
50
+ my[:id] =~ Bike::REX::ID_NEW &&
51
+ my[:parent].is_a?(Bike::Set::Dynamic) &&
52
+ my[:parent].workflow.is_a?(Bike::Workflow::Attachment)
53
+
54
+ errors = {}
55
+ @item_object.each_pair {|id, item|
56
+ errors[id] = item.errors unless item.valid? || item.action == :delete
57
+ }
58
+ errors
59
+ end
60
+
61
+ def collect(&block)
62
+ collect_item({}, &block)
63
+ end
64
+
65
+ def each(&block)
66
+ collect_item.each &block
67
+ end
68
+
69
+ def inspect_items(indent = 0)
70
+ my_action = action.inspect
71
+ my_result = result.is_a?(::Hash) ?
72
+ result.keys.sort.inspect.sub(/\A\[([\w\W]*)\]\z/, '{\1}') :
73
+ result.inspect
74
+
75
+ ' ' * indent +
76
+ "<\"#{my[:id]}\" @action=#{my_action} @result=#{my_result}>\n" +
77
+ @item_object.keys.sort.collect {|id|
78
+ item = @item_object[id]
79
+ if item.respond_to? :inspect_items
80
+ item.inspect_items(indent + 1)
81
+ else
82
+ action = item.action.inspect
83
+ result = item.result.inspect
84
+ val = item.val.inspect
85
+ ' ' * (indent + 1) + "<\"#{id}\" @action=#{action} @result=#{result} @val=#{val}>\n"
86
+ end
87
+ }.join
88
+ end
89
+
90
+ private
91
+
92
+ def _get(arg)
93
+ if respond_to?("_g_#{arg[:action]}", true)
94
+ _get_by_method arg
95
+ elsif summary?(arg) && action_tmpl = my[:tmpl][:summary]
96
+ _get_by_tmpl(arg, action_tmpl)
97
+ elsif action_tmpl = my[:tmpl][arg[:sub_action]] || my[:tmpl][arg[:action]]
98
+ _get_by_tmpl(arg, action_tmpl)
99
+ else
100
+ _get_by_tmpl(arg, my[:tmpl][:index])
101
+ end
102
+ end
103
+
104
+ def _get_by_tmpl(arg, tmpl = '')
105
+ tmpl.gsub(/@\((.+?)\)/) {
106
+ tag = $&
107
+ steps = $1.split '-'
108
+ id = steps.pop
109
+ item = item steps
110
+ item ? item[id.intern] : tag
111
+ }.gsub(/_\((.+?)\)/) {
112
+ _ $1
113
+ }.gsub(/\$\((.*?)(?:\.([\w\-]+))?\)/) {
114
+ tag, name, action = $&, $1, $2
115
+ if name == ''
116
+ self_arg = action ?
117
+ arg.merge(:orig_action => arg[:action], :action => action.intern) : arg
118
+ _get_by_self_reference self_arg
119
+ else
120
+ steps = name.split '-'
121
+ item_arg = item_arg(arg, steps)
122
+ item = item steps
123
+ if item.nil?
124
+ tag
125
+ elsif action
126
+ item_arg = item_arg.merge(
127
+ :orig_action => item_arg[:action],
128
+ :action => action.intern
129
+ )
130
+ item.send(:_get_by_self_reference, item_arg) # skip the authorization
131
+ else
132
+ item.get(item_arg)
133
+ end
134
+ end
135
+ }.gsub(/^\s+\n/, '')
136
+ end
137
+
138
+ def _get_by_self_reference(arg)
139
+ return if arg[:action].to_s =~ /^action_/ && ![:read, nil].include?(arg[:orig_action])
140
+ _get_by_method(arg)
141
+ end
142
+
143
+ def _get_by_action_tmpl(arg)
144
+ return nil unless !arg[:recur] && action_tmpl = my[:tmpl][arg[:action]]
145
+ _get_by_tmpl(arg.merge(:action => nil, :sub_action => nil, :recur => true), action_tmpl)
146
+ end
147
+
148
+ def _g_default(arg, &block)
149
+ collect_item(arg[:conds] || {}) {|item|
150
+ item_arg = item_arg(arg, item[:id])
151
+ next if item.empty? && ![:create, :update].include?(item_arg[:action])
152
+ block ? block.call(item, item_arg) : item.get(item_arg)
153
+ }.join
154
+ end
155
+
156
+ def _g_errors(arg)
157
+ # errors are shown by scalars
158
+ end
159
+
160
+ def item_arg(arg, steps)
161
+ Array(steps).inject(arg) {|a, s|
162
+ i = a[s] || {}
163
+ i[:p_action] = a[:action]
164
+ unless i[:action]
165
+ i[:action] = a[:action]
166
+ i[:sub_action] = a[:sub_action] if a[:sub_action]
167
+ end
168
+ i
169
+ }
170
+ end
171
+
172
+ def summary?(arg)
173
+ [:read, nil].include?(arg[:action]) && !arg[:sub_action]
174
+ end
175
+
176
+ def permit_post?(action, val)
177
+ super || val.all? {|id, v|
178
+ if id.is_a? ::Symbol
179
+ true # not a item value
180
+ elsif id =~ Bike::REX::ID_NEW
181
+ permit? :create
182
+ else
183
+ item_action = (v.is_a?(::Hash) && v[:action]) || :update
184
+ item(id) && item(id).permit?(item_action)
185
+ end
186
+ }
187
+ end
188
+
189
+ def pending_items
190
+ @item_object.keys.sort.inject({}) {|h, id|
191
+ h[id] = @item_object[id] if @item_object[id].pending?
192
+ h
193
+ }
194
+ end
195
+
196
+ end
@@ -0,0 +1,177 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ class Bike::Set::Dynamic < Bike::Field
7
+
8
+ include Bike::Set
9
+
10
+ attr_reader :storage, :workflow
11
+
12
+ def initialize(meta = {})
13
+ @meta = meta
14
+ @storage = Bike::Storage.instance self
15
+ @workflow = Bike::Workflow.instance self
16
+ @meta = @workflow.class.const_get(:DEFAULT_META).merge @meta
17
+ @item_object = {}
18
+
19
+ my[:item] ||= {
20
+ 'default' => {:item => {}}
21
+ }
22
+ my[:item].each {|type, item_meta|
23
+ item_meta[:item] = @workflow.default_sub_items.merge item_meta[:item]
24
+ }
25
+
26
+ my[:p_size] = meta[:max] if meta[:max]
27
+ my[:preview] = :optional if Array(meta[:tokens]).include?('may_preview')
28
+ my[:preview] = :mandatory if Array(meta[:tokens]).include?('should_preview')
29
+ my[:order] = 'id' if Array(meta[:tokens]).include? 'asc'
30
+ my[:order] = '-id' if Array(meta[:tokens]).include? 'desc'
31
+ end
32
+
33
+ def meta_href
34
+ "#{Bike.uri}#{my[:path]}/"
35
+ end
36
+
37
+ def meta_tid
38
+ unless @meta[:tid]
39
+ t = Time.now
40
+ @meta[:tid] = t.strftime('%m%d%H%M%S.') + t.usec.to_s
41
+ end
42
+ @meta[:tid]
43
+ end
44
+
45
+ def get(arg = {})
46
+ if !arg[:conds].is_a?(::Hash) || arg[:conds].empty?
47
+ arg[:conds] = my[:conds].is_a?(::Hash) ? my[:conds].dup : {}
48
+ end
49
+ super
50
+ end
51
+
52
+ def commit(type = :temp)
53
+ @workflow.before_commit
54
+
55
+ items = pending_items
56
+ items.each {|id, item|
57
+ item.commit(:temp) || next
58
+ case type
59
+ when :temp
60
+ store(id, item) if @storage.is_a? Bike::Storage::Temp
61
+ when :persistent
62
+ store(id, item)
63
+ item.commit :persistent
64
+ end
65
+ }
66
+ if valid?
67
+ @result = (@action == :update) ? items : @action
68
+ @action = nil if type == :persistent
69
+ @workflow.after_commit
70
+ self
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def _val
77
+ @storage.val
78
+ end
79
+
80
+ def _get(arg)
81
+ (@workflow._get(arg) || super) unless @workflow._hide? arg
82
+ end
83
+
84
+ def _get_by_tmpl(arg, tmpl = '')
85
+ if arg[:action] == :read || self != Bike.base
86
+ super
87
+ else
88
+ base_path = Bike.transaction[my[:tid]].is_a?(Bike::Field) ? nil : my[:base_path]
89
+ action = "#{base_path}/#{my[:tid]}/update.html"
90
+ <<_html
91
+ <form id="form_#{my[:name]}" method="post" enctype="multipart/form-data" action="#{action}">
92
+ <input name="_token" type="hidden" value="#{Bike.token}" />
93
+ #{super}</form>
94
+ _html
95
+ end
96
+ end
97
+
98
+ def _get_by_self_reference(arg)
99
+ super unless @workflow._hide?(arg)
100
+ end
101
+
102
+ def permit_get?(arg)
103
+ permit?(arg[:action]) || collect_item(arg[:conds] || {}).all? {|item|
104
+ item[:id][Bike::REX::ID_NEW] ?
105
+ item.permit?(:create) :
106
+ item.send(:permit_get?, :action => arg[:action])
107
+ }
108
+ end
109
+
110
+ def _post(action, v = nil)
111
+ if action == :create
112
+ @storage.build({})
113
+ @item_object.clear
114
+ end
115
+
116
+ case action
117
+ when :create, :update
118
+ v.keys.sort_by {|id| id.to_s }.each {|id|
119
+ next unless id.is_a? ::String
120
+
121
+ v[id][:action] ||= id[Bike::REX::ID_NEW] ? :create : :update
122
+ item_instance(id).post(v[id][:action], v[id])
123
+ }
124
+ when :load, :load_default
125
+ @storage.build v
126
+ end
127
+
128
+ !pending_items.empty? || action == :delete
129
+ end
130
+
131
+ def store(id, item)
132
+ case item.action
133
+ when :create
134
+ return if id[Bike::REX::ID] || item.empty?
135
+ new_id = @storage.store(:new_id, item.val)
136
+ item[:id] = new_id
137
+ @item_object.delete id
138
+ @item_object[item[:id]] = item
139
+ when :update, nil
140
+ new_id = @storage.store(item[:id], item.val)
141
+ if new_id != item[:id]
142
+ @item_object[new_id] = @item_object.delete item[:id]
143
+ item[:id] = new_id
144
+ end
145
+ when :delete
146
+ @storage.delete id
147
+ @item_object.delete id
148
+ end
149
+ end
150
+
151
+ def collect_item(conds = {}, &block)
152
+ @storage.select(conds).collect {|id|
153
+ item = item_instance id
154
+ block ? block.call(item) : item
155
+ }
156
+ end
157
+
158
+ def item_instance(id, type = 'default')
159
+ unless @item_object[id]
160
+ item_meta = my[:item][type] || my[:item]['default']
161
+ @item_object[id] = Bike::Field.instance(
162
+ item_meta.merge(
163
+ :id => id,
164
+ :parent => self,
165
+ :klass => 'set-static'
166
+ )
167
+ )
168
+ if id[Bike::REX::ID_NEW]
169
+ @item_object[id].load_default
170
+ else
171
+ @item_object[id].load @storage.val(id)
172
+ end
173
+ end
174
+ @item_object[id]
175
+ end
176
+
177
+ end
@@ -0,0 +1,102 @@
1
+ # encoding: UTF-8
2
+
3
+ # Author:: Akira FUNAI
4
+ # Copyright:: Copyright (c) 2009 Akira FUNAI
5
+
6
+ require 'strscan'
7
+
8
+ class Bike::Set::Static < Bike::Field
9
+
10
+ include Bike::Set
11
+
12
+ def initialize(meta = {})
13
+ @meta = meta
14
+ @meta.merge!(Bike::Parser.parse_html(meta[:html])) if meta[:html]
15
+ @meta[:item] ||= {}
16
+ @item_object = {}
17
+ end
18
+
19
+ def meta_href
20
+ my[:sd] ? "#{my[:sd][:href]}id=#{my[:id]}/" : "#{Bike.uri}#{my[:dir]}/"
21
+ end
22
+
23
+ def commit(type = :temp)
24
+ items = pending_items
25
+ items.each {|id, item| item.commit type }
26
+ if valid?
27
+ @result = (@action == :update) ? items : @action
28
+ @action = nil if type == :persistent
29
+ self
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def _val
36
+ inject({}) {|v, item|
37
+ v[item[:id]] = item.val unless item.empty?
38
+ v
39
+ }
40
+ end
41
+
42
+ def _g_a_update(arg)
43
+ if arg[:orig_action] != :read
44
+ '<a>'
45
+ elsif permit_get?(:action => :update)
46
+ "<a href=\"#{_g_uri_update arg}\">"
47
+ elsif permit? :delete
48
+ "<a href=\"#{_g_uri_delete arg}\">"
49
+ else
50
+ '<a>'
51
+ end
52
+ end
53
+
54
+ def _g_uri_update(arg)
55
+ "#{my[:parent][:path]}/#{Bike::Path::path_of :id => my[:id]}update.html"
56
+ end
57
+
58
+ def _g_uri_delete(arg)
59
+ "#{my[:parent][:path]}/#{Bike::Path::path_of :id => my[:id]}preview_delete.html"
60
+ end
61
+
62
+ def _g_uri_detail(arg)
63
+ "#{my[:parent][:path]}/#{Bike::Path::path_of :id => my[:id]}read_detail.html"
64
+ end
65
+
66
+ def _g_hidden(arg)
67
+ if arg[:orig_action] == :preview
68
+ action = my[:id][Bike::REX::ID_NEW] ? :create : arg[:sub_action]
69
+ <<_html.chomp
70
+ <input type="hidden" name="#{my[:short_name]}.action" value="#{action}" />
71
+ _html
72
+ end
73
+ end
74
+
75
+ def _post(action, v = {})
76
+ each {|item|
77
+ id = item[:id]
78
+ item_action = (item.is_a?(Bike::Set) && action == :create) ? :update : action
79
+ item_action = v[id][:action] if v[id].is_a?(::Hash) && v[id][:action]
80
+ if [:load_default, :delete].include?(item_action) || v.key?(id) || item(id).is_a?(Bike::Meta)
81
+ item.post(item_action, v[id])
82
+ end
83
+ }
84
+ !pending_items.empty? || action == :delete
85
+ end
86
+
87
+ def collect_item(conds = {}, &block)
88
+ items = my[:item].keys
89
+ items &= Array(conds[:id]) if conds[:id] # select item(s) by id
90
+ items.sort.collect {|id|
91
+ item = @item_object[id] ||= Bike::Field.instance(
92
+ my[:item][id].merge(:id => id, :parent => self)
93
+ )
94
+ block ? block.call(item) : item
95
+ }
96
+ end
97
+
98
+ def val_cast(v)
99
+ v.is_a?(::Hash) ? v : {:self => v}
100
+ end
101
+
102
+ end