runo 0.2.1 → 0.2.2

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/README.rdoc +1 -118
  2. data/bin/runo +4 -29
  3. data/lib/dummy.rb +0 -0
  4. metadata +12 -278
  5. data/lib/_error.rb +0 -14
  6. data/lib/_field.rb +0 -260
  7. data/lib/_i18n.rb +0 -144
  8. data/lib/_parser.rb +0 -256
  9. data/lib/_path.rb +0 -86
  10. data/lib/_storage/_storage.rb +0 -215
  11. data/lib/_storage/file.rb +0 -201
  12. data/lib/_storage/sequel.rb +0 -174
  13. data/lib/_storage/temp.rb +0 -73
  14. data/lib/_widget/action_create.rb +0 -23
  15. data/lib/_widget/action_login.rb +0 -22
  16. data/lib/_widget/action_signup.rb +0 -16
  17. data/lib/_widget/action_update.rb +0 -16
  18. data/lib/_widget/crumb.rb +0 -24
  19. data/lib/_widget/done.rb +0 -16
  20. data/lib/_widget/login.rb +0 -25
  21. data/lib/_widget/me.rb +0 -31
  22. data/lib/_widget/message.rb +0 -51
  23. data/lib/_widget/navi.rb +0 -88
  24. data/lib/_widget/submit.rb +0 -49
  25. data/lib/_widget/view_ym.rb +0 -77
  26. data/lib/_workflow/_workflow.rb +0 -89
  27. data/lib/_workflow/attachment.rb +0 -50
  28. data/lib/_workflow/blog.rb +0 -28
  29. data/lib/_workflow/contact.rb +0 -23
  30. data/lib/_workflow/forum.rb +0 -26
  31. data/lib/_workflow/register.rb +0 -39
  32. data/lib/meta/_meta.rb +0 -20
  33. data/lib/meta/group.rb +0 -19
  34. data/lib/meta/id.rb +0 -59
  35. data/lib/meta/owner.rb +0 -21
  36. data/lib/meta/timestamp.rb +0 -118
  37. data/lib/runo.rb +0 -396
  38. data/lib/scalar/checkbox.rb +0 -68
  39. data/lib/scalar/file.rb +0 -144
  40. data/lib/scalar/img.rb +0 -112
  41. data/lib/scalar/password.rb +0 -58
  42. data/lib/scalar/radio.rb +0 -47
  43. data/lib/scalar/select.rb +0 -47
  44. data/lib/scalar/text.rb +0 -38
  45. data/lib/scalar/textarea.rb +0 -35
  46. data/lib/scalar/textarea_pre.rb +0 -14
  47. data/lib/scalar/textarea_wiki.rb +0 -173
  48. data/lib/set/_set.rb +0 -196
  49. data/lib/set/dynamic.rb +0 -177
  50. data/lib/set/static.rb +0 -102
  51. data/lib/set/static_folder.rb +0 -96
  52. data/locale/en/index.po +0 -242
  53. data/locale/index.pot +0 -243
  54. data/locale/ja/index.po +0 -242
  55. data/locale/lazy_parser.rb +0 -54
  56. data/skel/config.ru +0 -27
  57. data/skel/skin/_users/00000000_frank-avatar.jpg +0 -0
  58. data/skel/skin/_users/00000000_frank-avatar_small.jpg +0 -0
  59. data/skel/skin/_users/00000000_frank.yaml +0 -12
  60. data/skel/skin/_users/00000000_root-avatar.jpg +0 -0
  61. data/skel/skin/_users/00000000_root-avatar_small.jpg +0 -0
  62. data/skel/skin/_users/00000000_root.yaml +0 -11
  63. data/skel/skin/_users/css/users.css +0 -21
  64. data/skel/skin/_users/css/users.less +0 -25
  65. data/skel/skin/_users/done.html +0 -42
  66. data/skel/skin/_users/index.html +0 -46
  67. data/skel/skin/_users/index.yaml +0 -3
  68. data/skel/skin/_users/summary.html +0 -40
  69. data/skel/skin/css/base.css +0 -93
  70. data/skel/skin/css/base.less +0 -139
  71. data/skel/skin/css/coax.css +0 -199
  72. data/skel/skin/css/coax.less +0 -244
  73. data/skel/skin/examples/blog/20091214_0001.yaml +0 -8
  74. data/skel/skin/examples/blog/20100630_0001.yaml +0 -8
  75. data/skel/skin/examples/blog/20100630_0002.yaml +0 -14
  76. data/skel/skin/examples/blog/20100701_0001.yaml +0 -8
  77. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f.jpg +0 -0
  78. data/skel/skin/examples/blog/20100701_0002-a-20100701_0001-f_small.jpg +0 -0
  79. data/skel/skin/examples/blog/20100701_0002.yaml +0 -19
  80. data/skel/skin/examples/blog/frank/20100701_0001.yaml +0 -10
  81. data/skel/skin/examples/blog/frank/index.yaml +0 -4
  82. data/skel/skin/examples/blog/index.html +0 -51
  83. data/skel/skin/examples/blog/rss.xml +0 -18
  84. data/skel/skin/examples/contact/20100701_0001-file.txt +0 -1
  85. data/skel/skin/examples/contact/20100701_0001.yaml +0 -15
  86. data/skel/skin/examples/contact/20100701_0002.yaml +0 -8
  87. data/skel/skin/examples/contact/20100701_0003.yaml +0 -9
  88. data/skel/skin/examples/contact/index.html +0 -47
  89. data/skel/skin/examples/contact/js/contact.js +0 -13
  90. data/skel/skin/examples/contact/summary.html +0 -54
  91. data/skel/skin/examples/forum/20100701_0001.yaml +0 -41
  92. data/skel/skin/examples/forum/20100701_0002.yaml +0 -25
  93. data/skel/skin/examples/forum/index.html +0 -68
  94. data/skel/skin/examples/forum/summary.html +0 -47
  95. data/skel/skin/examples/index.html +0 -73
  96. data/skel/skin/index.html +0 -39
  97. data/skel/skin/js/base.js +0 -50
  98. data/t/locale/de/index.po +0 -19
  99. data/t/locale/en-GB/index.po +0 -25
  100. data/t/locale/ja/index.po +0 -30
  101. data/t/skin/_users/00000000_test.yaml +0 -3
  102. data/t/skin/_users/index.html +0 -13
  103. data/t/skin/foo/20091120_0001.yaml +0 -7
  104. data/t/skin/foo/bar/20091120_0001.yaml +0 -5
  105. data/t/skin/foo/bar/index.yaml +0 -5
  106. data/t/skin/foo/baz/css/baz.css +0 -1
  107. data/t/skin/foo/css/foo.css +0 -1
  108. data/t/skin/foo/index.html +0 -14
  109. data/t/skin/foo/index.yaml +0 -7
  110. data/t/skin/foo/not_css/foo.css +0 -1
  111. data/t/skin/foo/qux/index.html +0 -8
  112. data/t/skin/foo/qux/moo/index.html +0 -6
  113. data/t/skin/foo/sub-20100306_0001.yaml +0 -3
  114. data/t/skin/index.yaml +0 -3
  115. data/t/skin/t_attachment/index.html +0 -13
  116. data/t/skin/t_contact/done.html +0 -6
  117. data/t/skin/t_contact/index.html +0 -9
  118. data/t/skin/t_file/index.html +0 -16
  119. data/t/skin/t_img/index.html +0 -14
  120. data/t/skin/t_img/test.jpg +0 -0
  121. data/t/skin/t_select/index.html +0 -9
  122. data/t/skin/t_store/index.html +0 -9
  123. data/t/skin/t_summary/20100326_0001.yaml +0 -3
  124. data/t/skin/t_summary/create.html +0 -9
  125. data/t/skin/t_summary/index.html +0 -9
  126. data/t/skin/t_summary/summary.html +0 -9
  127. data/t/t.rb +0 -27
  128. data/t/test_checkbox.rb +0 -273
  129. data/t/test_field.rb +0 -330
  130. data/t/test_file.rb +0 -900
  131. data/t/test_id.rb +0 -215
  132. data/t/test_img.rb +0 -328
  133. data/t/test_meta.rb +0 -57
  134. data/t/test_parser.rb +0 -1516
  135. data/t/test_password.rb +0 -188
  136. data/t/test_radio.rb +0 -226
  137. data/t/test_role.rb +0 -249
  138. data/t/test_runo.rb +0 -768
  139. data/t/test_runo_call.rb +0 -1281
  140. data/t/test_runo_i18n.rb +0 -325
  141. data/t/test_select.rb +0 -182
  142. data/t/test_set_complex.rb +0 -527
  143. data/t/test_set_dynamic.rb +0 -1504
  144. data/t/test_set_folder.rb +0 -515
  145. data/t/test_set_permit.rb +0 -246
  146. data/t/test_set_static.rb +0 -468
  147. data/t/test_storage.rb +0 -915
  148. data/t/test_text.rb +0 -125
  149. data/t/test_textarea.rb +0 -138
  150. data/t/test_timestamp.rb +0 -473
  151. data/t/test_workflow.rb +0 -367
@@ -1,20 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009 Akira FUNAI
5
-
6
- module Runo::Meta
7
-
8
- def post(action, v = nil)
9
- super
10
- my[:parent][klass_id] = val if my[:parent]
11
- self
12
- end
13
-
14
- private
15
-
16
- def klass_id
17
- self.class.to_s[/\w+$/].downcase.intern
18
- end
19
-
20
- end
@@ -1,19 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009 Akira FUNAI
5
-
6
- class Runo::Meta::Group < Runo::Field
7
-
8
- include Runo::Meta
9
-
10
- private
11
-
12
- def _post(action, v)
13
- if action == :load
14
- @val = val_cast v
15
- end
16
- nil
17
- end
18
-
19
- end
@@ -1,59 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009-2010 Akira FUNAI
5
-
6
- class Runo::Meta::Id < Runo::Field
7
-
8
- # not include Runo::Meta as this is SHORT_ID, not a full id.
9
-
10
- def initialize(meta = {})
11
- meta[:size] = $&.to_i if meta[:tokens] && meta[:tokens].first =~ /^\d+$/
12
- super
13
- end
14
-
15
- def errors
16
- if (my[:max].to_i > 0) && (val.size > my[:max])
17
- [_('too long: %{max} characters maximum') % {:max => my[:max]}]
18
- elsif (my[:min].to_i == 1) && val.empty?
19
- [_('mandatory')]
20
- elsif (my[:min].to_i > 0) && (val.size < my[:min])
21
- [_('too short: %{min} characters minimum') % {:min => my[:min]}]
22
- elsif val !~ /\A#{Runo::REX::ID_SHORT}\z/
23
- [_('malformatted id')]
24
- elsif (
25
- my[:parent] &&
26
- my[:parent][:id] !~ /00000000_#{val}$/ &&
27
- my[:parent][:parent].is_a?(Runo::Set::Dynamic) &&
28
- my[:parent][:parent].item(val)
29
- )
30
- [_('duplicate id: %{id}') % {:id => val}]
31
- else
32
- []
33
- end
34
- end
35
-
36
- private
37
-
38
- def _g_create(arg)
39
- new_id? ? <<_html.chomp : _g_default(arg)
40
- <span class="#{_g_class arg}"><input type="text" name="#{my[:short_name]}" value="#{Runo::Field.h val}" size="#{my[:size]}" />#{_g_errors arg}</span>
41
- _html
42
- end
43
- alias :_g_update :_g_create
44
-
45
- def new_id?
46
- find_ancestor {|i| i[:id] =~ Runo::REX::ID_NEW } ? true : false
47
- end
48
-
49
- def _post(action, v)
50
- if action == :load || ([:create, :update].include?(action) && new_id?)
51
- @val = val_cast v
52
- end
53
- end
54
-
55
- def val_cast(v)
56
- v.to_s
57
- end
58
-
59
- end
@@ -1,21 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009 Akira FUNAI
5
-
6
- class Runo::Meta::Owner < Runo::Field
7
-
8
- include Runo::Meta
9
-
10
- private
11
-
12
- def _post(action, v)
13
- if action == :load
14
- @val = val_cast v
15
- elsif action == :create
16
- @val = Runo.client
17
- end
18
- nil
19
- end
20
-
21
- end
@@ -1,118 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009 Akira FUNAI
5
-
6
- require 'time'
7
-
8
- class Runo::Meta::Timestamp < Runo::Field
9
-
10
- include Runo::Meta
11
-
12
- REX_DATE = /\A(\d+).(\d+).(\d+)(?:[T\s](\d+):(\d+)(?::(\d+))?)?\z/
13
-
14
- def initialize(meta = {})
15
- meta[:size] = $&.to_i if meta[:tokens] && meta[:tokens].find {|t| t =~ /^\d+$/ }
16
- meta[:can_edit] = true if Array(meta[:tokens]).include? 'can_edit'
17
- meta[:can_update] = true if Array(meta[:tokens]).include? 'can_update'
18
- super
19
- end
20
-
21
- def errors
22
- if @date_str.nil?
23
- []
24
- elsif @date_str =~ REX_DATE
25
- (Time.local($1, $2, $3, $4, $5, $6) rescue nil) ? [] : ['out of range']
26
- else
27
- ['wrong format']
28
- end
29
- end
30
-
31
- private
32
-
33
- def _g_default(arg)
34
- _date val['published']
35
- end
36
- alias :_g_published :_g_default
37
-
38
- def _g_rfc2822(arg)
39
- val['published'].rfc2822
40
- end
41
-
42
- def _g_created(arg)
43
- _date val['created']
44
- end
45
-
46
- def _g_updated(arg)
47
- _date val['updated']
48
- end
49
-
50
- def _date(time)
51
- time.is_a?(::Time) ? time.strftime(_('%Y-%m-%dT%H:%M:%S')) : 'n/a'
52
- end
53
-
54
- def _g_create(arg)
55
- <<_html.chomp if my[:can_edit]
56
- <span class="#{_g_class arg}"><input type="text" name="#{my[:short_name]}" value="" size="#{my[:size]}" /></span>
57
- _html
58
- end
59
-
60
- def _g_update(arg)
61
- if my[:can_edit]
62
- v = @date_str
63
- v ||= val['published'].is_a?(::Time) ? val['published'].strftime('%Y-%m-%d %H:%M:%S') : ''
64
- <<_html.chomp
65
- <span class="#{_g_class arg}"><input type="text" name="#{my[:short_name]}" value="#{Runo::Field.h v}" size="#{my[:size]}" />#{_g_errors arg}</span>
66
- _html
67
- elsif my[:can_update] && !find_ancestor {|f| f[:id] =~ Runo::REX::ID_NEW }
68
- <<_html
69
- <span class="#{_g_class arg}">
70
- <input type="checkbox" id="timestamp_#{my[:short_name]}" name="#{my[:short_name]}" value="true" />
71
- <label for="timestamp_#{my[:short_name]}">#{_ 'update the timestamp'}</label>
72
- #{_g_errors arg}</span>
73
- _html
74
- end
75
- end
76
-
77
- def _post(action, v)
78
- case action
79
- when :load
80
- @val = val_cast v
81
- when :create
82
- now = Time.now
83
- @val = {
84
- 'created' => now,
85
- 'updated' => now,
86
- 'published' => now,
87
- }
88
- if my[:can_edit] && v['published'].is_a?(::Time)
89
- @val['published'] = v['published']
90
- else
91
- nil # do not set @action
92
- end
93
- when :update
94
- @val['updated'] = Time.now
95
- if my[:can_edit] && v['published'].is_a?(::Time)
96
- @val['published'] = v['published']
97
- elsif my[:can_update] && v['published'] == :same_as_updated
98
- @val['published'] = @val['updated']
99
- else
100
- nil # do not set @action
101
- end
102
- end
103
- end
104
-
105
- def val_cast(v)
106
- if v.is_a? ::Hash
107
- v
108
- elsif v == 'true'
109
- {'published' => :same_as_updated}
110
- elsif v.is_a?(::String) && !v.empty?
111
- @date_str = v
112
- (v =~ REX_DATE && t = (Time.local($1, $2, $3, $4, $5, $6) rescue nil)) ? {'published' => t} : {}
113
- else
114
- {}
115
- end
116
- end
117
-
118
- end
@@ -1,396 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # Author:: Akira FUNAI
4
- # Copyright:: Copyright (c) 2009 Akira FUNAI
5
-
6
- class Runo
7
-
8
- lib_dir = ::File.dirname __FILE__
9
-
10
- require "#{lib_dir}/_i18n.rb"
11
- I18n.bindtextdomain('index', ::File.expand_path('../locale', lib_dir))
12
-
13
- Dir["#{lib_dir}/_*.rb"].sort.each {|file| require file }
14
- Dir["#{lib_dir}/[a-z]*/*.rb"].sort.each {|file| require file }
15
- Dir["#{lib_dir}/_*/*.rb"].sort.each {|file| require file }
16
-
17
- module REX
18
- ID_SHORT = /[a-z][a-z0-9]*/
19
- ID = /^(\d{8})_(\d{4,}|#{ID_SHORT})/
20
- ID_NEW = /^_\d/
21
- COND = /^(.+?)=(.+)$/
22
- COND_D = /^(19\d\d|2\d\d\d|9999)\d{0,4}$/
23
- PATH_ID = /\/((?:19|2\d)\d{6})\/(\d+)/
24
- TID = /\d{10}\.\d+/
25
- DIR_STATIC = /css|js|img|imgs|image|images/
26
- end
27
-
28
- def self.config(config)
29
- @config = config
30
- end
31
-
32
- def self.[](name)
33
- @config ||= {}
34
- @config[name]
35
- end
36
-
37
- def self.current
38
- Thread.current
39
- end
40
-
41
- def self.session
42
- self.current[:session] || ($fake_session ||= {})
43
- end
44
-
45
- def self.transaction
46
- self.session[:transaction] ||= {}
47
- end
48
-
49
- def self.client
50
- self.session[:client] ||= 'nobody'
51
- end
52
-
53
- def self.client=(id)
54
- self.session[:client] = id
55
- end
56
-
57
- def self.token
58
- self.session[:token] ||= rand(36 ** 32).to_s(36)
59
- end
60
-
61
- def self.base
62
- self.current[:base]
63
- end
64
-
65
- def self.uri
66
- self.current[:uri]
67
- end
68
-
69
- def self.libdir
70
- ::File.dirname __FILE__
71
- end
72
-
73
- def self.static(env)
74
- @static ||= Rack::Directory.new Runo['skin_dir']
75
- response = @static.call env
76
-
77
- if response.first == 404
78
- until ::File.readable? ::File.join(
79
- Runo['skin_dir'],
80
- env['PATH_INFO'].sub(%r{(/#{Runo::REX::DIR_STATIC}/).*}, '\\1')
81
- )
82
- env['PATH_INFO'].sub!(%r{/[^/]+(?=/#{Runo::REX::DIR_STATIC}/)}, '') || break
83
- end
84
- @static.call env
85
- else
86
- response
87
- end
88
- end
89
-
90
- def call(env)
91
- uri = "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}#{env['SCRIPT_NAME']}"
92
- req = Rack::Request.new env
93
- method = req.request_method.downcase
94
- params = params_from_request req
95
- path = req.path_info
96
- tid = Runo::Path.tid_of path
97
-
98
- static_path = ::File.expand_path path
99
- return Runo.static(env) if (
100
- static_path =~ %r{/#{Runo::REX::DIR_STATIC}/} &&
101
- static_path !~ %r{/#{Runo::REX::TID}/} &&
102
- static_path !~ %r{/#{Runo::REX::ID.to_s.sub('^','')}/}
103
- )
104
-
105
- Runo::I18n.lang = env['HTTP_ACCEPT_LANGUAGE']
106
-
107
- Runo.current[:env] = env
108
- Runo.current[:uri] = uri
109
- Runo.current[:req] = req
110
- Runo.current[:session] = env['rack.session']
111
-
112
- if Runo.transaction[tid].is_a? Runo::Field
113
- base = Runo.transaction[tid].item Runo::Path.steps_of(path.sub(/\A.*#{Runo::REX::TID}/, ''))
114
- else
115
- base = Runo::Path.base_of path
116
- end
117
- return response_not_found unless base
118
-
119
- base[:tid] = tid
120
- Runo.current[:base] = base
121
-
122
- begin
123
- if params[:action] == :logout && params[:token] == Runo.token
124
- logout(base, params)
125
- elsif method == 'get'
126
- get(base, params)
127
- elsif params[:action] == :login
128
- login(base, params)
129
- elsif params[:action] == :preview
130
- preview(base, params)
131
- elsif params[:token] != Runo.token
132
- response_forbidden(:body => 'invalid token')
133
- elsif Runo.transaction[tid] && !Runo.transaction[tid].is_a?(Runo::Field)
134
- response_unprocessable_entity(:body => 'transaction expired')
135
- else
136
- begin
137
- post(base, params)
138
- rescue Runo::Error::Forbidden
139
- response_forbidden
140
- end
141
- end
142
- rescue Runo::Error::Forbidden
143
- if params[:action] && Runo.client == 'nobody'
144
- params[:dest_action] = (method == 'post') ? :index : params[:action]
145
- params[:action] = :login
146
- end
147
- response_unprocessable_entity(:body => _get(base, params)) rescue response_forbidden
148
- # TODO: rescue Error::System etc.
149
- end
150
- end
151
-
152
- private
153
-
154
- def login(base, params)
155
- user = Runo::Set::Static::Folder.root.item('_users', 'main', params['id'].to_s)
156
- if user && params['pw'].to_s.crypt(user.val('password')) == user.val('password')
157
- Runo.client = params['id']
158
- else
159
- Runo.client = nil
160
- raise Runo::Error::Forbidden
161
- end
162
- path = Runo::Path.path_of params[:conds]
163
- action = (params['dest_action'] =~ /\A\w+\z/) ? params['dest_action'] : 'index'
164
- response_see_other(
165
- :location => "#{Runo.uri}#{base[:path]}/#{path}#{action}.html"
166
- )
167
- end
168
-
169
- def logout(base, params)
170
- Runo.client = nil
171
- path = Runo::Path.path_of params[:conds]
172
- response_see_other(
173
- :location => "#{Runo.uri}#{base[:path]}/#{path}index.html"
174
- )
175
- end
176
-
177
- def get(base, params)
178
- if base.is_a? Runo::File
179
- body = (params[:sub_action] == :small) ? base.thumbnail : base.body
180
- response_ok(
181
- :headers => {
182
- 'Content-Type' => base.val['type'],
183
- 'Content-Length' => body.to_s.size.to_s,
184
- },
185
- :body => body
186
- )
187
- else
188
- response_ok :body => _get(base, params)
189
- end
190
- end
191
-
192
- def preview(base, params)
193
- Runo.transaction[base[:tid]] ||= base if base[:tid] =~ Runo::REX::TID
194
-
195
- base.update params
196
- if base.commit(:temp) || params[:sub_action] == :delete
197
- id_step = result_step(base, params)
198
- action = "preview_#{params[:sub_action]}"
199
- response_see_other(
200
- :location => "#{Runo.uri}/#{base[:tid]}/#{id_step}#{action}.html"
201
- )
202
- else
203
- params = {:action => :update}
204
- params[:conds] = {:id => base.errors.keys}
205
- return response_unprocessable_entity(:body => _get(base, params))
206
- end
207
- end
208
-
209
- def post(base, params)
210
- Runo.transaction[base[:tid]] ||= base if base[:tid] =~ Runo::REX::TID
211
-
212
- base.update params
213
- if params[:status]
214
- if base[:folder].commit :persistent
215
- Runo.transaction[base[:tid]] = result_summary base
216
- action = base.workflow.next_action base
217
- id_step = result_step(base, params) if base[:parent] == base[:folder] && action != :done
218
- response_see_other(
219
- :location => "#{Runo.uri}/#{base[:tid]}#{base[:path]}/#{id_step}#{action}.html"
220
- )
221
- else
222
- params = {:action => :update}
223
- params[:conds] = {:id => base.errors.keys}
224
- response_unprocessable_entity :body => _get(base, params)
225
- end
226
- else
227
- base.commit :temp
228
- id_step = result_step(base, params)
229
- response_see_other(
230
- :location => "#{Runo.uri}/#{base[:tid]}/#{id_step}update.html"
231
- )
232
- end
233
- end
234
-
235
- def result_summary(base)
236
- (base.result || {}).values.inject({}) {|summary, item|
237
- item_result = item.result.is_a?(::Symbol) ? item.result : :update
238
- summary[item_result] = summary[item_result].to_i + 1
239
- summary
240
- }
241
- end
242
-
243
- def result_step(base, params)
244
- if base.result
245
- id = base.result.values.collect {|item| item[:id] }
246
- else
247
- id = params.keys.select {|id|
248
- id.is_a?(::String) && (id[Runo::REX::ID] || id[Runo::REX::ID_NEW])
249
- }
250
- end
251
- Runo::Path.path_of(:id => id)
252
- end
253
-
254
- def _get(f, params)
255
- params[:action] ||= f.default_action
256
- until f.is_a? Runo::Set::Static::Folder
257
- params = {
258
- :action => (f.default_action == :read) ? :read : nil,
259
- :sub_action => f.send(:summary?, params) ? nil : (params[:sub_action] || :detail),
260
- f[:id] => params,
261
- }
262
- params[:conds] = {:id => f[:id]} if f[:parent].is_a? Runo::Set::Dynamic
263
- f = f[:parent]
264
- end if f.is_a? Runo::Set::Dynamic
265
-
266
- f.get params
267
- end
268
-
269
- def params_from_request(req)
270
- params = {
271
- :action => Runo::Path.action_of(req.path_info),
272
- :sub_action => Runo::Path.sub_action_of(req.path_info),
273
- }
274
- params.merge! rebuild_params(req.params)
275
-
276
- params[:conds] ||= {}
277
- params[:conds].merge! Runo::Path.conds_of(req.path_info)
278
-
279
- params
280
- end
281
-
282
- def rebuild_params(src)
283
- src.keys.sort.reverse.inject({}) {|params, key|
284
- name, special = key.split('.', 2)
285
- steps = name.split '-'
286
-
287
- if special
288
- special_id, special_val = special.split('-', 2)
289
- else
290
- item_id = steps.pop
291
- end
292
-
293
- hash = steps.inject(params) {|v, k| v[k] ||= {} }
294
- val = src[key]
295
-
296
- if special_id == 'action'
297
- action, sub_action = (special_val || val).split('_', 2)
298
- hash[:action] = action.intern
299
- hash[:sub_action] = sub_action.intern if sub_action
300
- elsif special_id == 'status'
301
- hash[:status] = (special_val || val).intern
302
- elsif special_id == 'conds'
303
- hash[:conds] ||= {}
304
- hash[:conds][special_val.intern] = val
305
- elsif hash[item_id].is_a? ::Hash
306
- hash[item_id][:self] = val
307
- elsif item_id == '_token'
308
- hash[:token] = val
309
- else
310
- hash[item_id] = val
311
- end
312
-
313
- params
314
- }
315
- end
316
-
317
- def response_ok(result = {})
318
- body = result[:body].to_s
319
- return response_not_found(result) if body.empty?
320
- [
321
- 200,
322
- (
323
- result[:headers] ||
324
- {
325
- 'Content-Type' => 'text/html',
326
- 'Content-Length' => body.size.to_s,
327
- }
328
- ),
329
- [body],
330
- ]
331
- end
332
-
333
- def response_no_content(result = {})
334
- [
335
- 204,
336
- (result[:headers] || {}),
337
- []
338
- ]
339
- end
340
-
341
- def response_see_other(result = {})
342
- body = <<_html
343
- <a href="#{result[:location]}">see other</a>
344
- _html
345
- [
346
- 303,
347
- {
348
- 'Content-Type' => 'text/html',
349
- 'Content-Length' => body.size.to_s,
350
- 'Location' => result[:location],
351
- },
352
- [body]
353
- ]
354
- end
355
-
356
- def response_forbidden(result = {})
357
- body = result[:body] || 'Forbidden'
358
- [
359
- 403,
360
- {
361
- 'Content-Type' => 'text/html',
362
- 'Content-Length' => body.size.to_s,
363
- },
364
- [body],
365
- ]
366
- end
367
-
368
- def response_not_found(result = {})
369
- body = result[:body] || 'Not Found'
370
- [
371
- 404,
372
- {
373
- 'Content-Type' => 'text/html',
374
- 'Content-Length' => body.size.to_s,
375
- },
376
- [body]
377
- ]
378
- end
379
-
380
- def response_unprocessable_entity(result = {})
381
- body = result[:body].to_s
382
- return response_not_found(result) if body.empty?
383
- [
384
- 422,
385
- (
386
- result[:headers] ||
387
- {
388
- 'Content-Type' => 'text/html',
389
- 'Content-Length' => body.size.to_s,
390
- }
391
- ),
392
- [body],
393
- ]
394
- end
395
-
396
- end