bike 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -2
- data/lib/_field.rb +4 -0
- data/lib/_response.rb +89 -0
- data/lib/_workflow/_workflow.rb +193 -20
- data/lib/_workflow/attachment.rb +19 -11
- data/lib/_workflow/register.rb +6 -3
- data/lib/bike.rb +4 -223
- data/lib/set/dynamic.rb +5 -15
- data/t/skin/_users/index.html +1 -1
- data/t/test_bike.rb +15 -109
- data/t/test_call.rb +34 -2
- data/t/test_workflow.rb +105 -2
- metadata +4 -3
data/README.rdoc
CHANGED
@@ -5,7 +5,7 @@ You can make various apps such like blog, forum, contact form by putting single
|
|
5
5
|
|
6
6
|
== Wiki
|
7
7
|
|
8
|
-
http://
|
8
|
+
http://github.com/afunai/bike/wiki
|
9
9
|
|
10
10
|
== Features
|
11
11
|
|
@@ -107,7 +107,7 @@ See the URL:
|
|
107
107
|
http://localhost:9292/fab/
|
108
108
|
|
109
109
|
You have just created your first app. Open a bottle of your favorite drink and make yourself comfortable.
|
110
|
-
When you need more complicated tricks, HTMLs under skin/examples/ will be your help. Also, you should check out the wiki: http://
|
110
|
+
When you need more complicated tricks, HTMLs under skin/examples/ will be your help. Also, you should check out the wiki: http://github.com/afunai/bike/wiki
|
111
111
|
|
112
112
|
== Note on Patches/Pull Requests
|
113
113
|
|
data/lib/_field.rb
CHANGED
data/lib/_response.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Author:: Akira FUNAI
|
4
|
+
# Copyright:: Copyright (c) 2009 Akira FUNAI
|
5
|
+
|
6
|
+
module Bike::Response
|
7
|
+
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def ok(result = {})
|
11
|
+
body = result[:body].to_s
|
12
|
+
return not_found(result) if body.empty?
|
13
|
+
[
|
14
|
+
200,
|
15
|
+
(
|
16
|
+
result[:headers] ||
|
17
|
+
{
|
18
|
+
'Content-Type' => 'text/html',
|
19
|
+
'Content-Length' => body.size.to_s,
|
20
|
+
}
|
21
|
+
),
|
22
|
+
[body],
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
def no_content(result = {})
|
27
|
+
[
|
28
|
+
204,
|
29
|
+
(result[:headers] || {}),
|
30
|
+
[]
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
def see_other(result = {})
|
35
|
+
body = <<_html
|
36
|
+
<a href="#{result[:location]}">see other</a>
|
37
|
+
_html
|
38
|
+
[
|
39
|
+
303,
|
40
|
+
{
|
41
|
+
'Content-Type' => 'text/html',
|
42
|
+
'Content-Length' => body.size.to_s,
|
43
|
+
'Location' => result[:location],
|
44
|
+
},
|
45
|
+
[body]
|
46
|
+
]
|
47
|
+
end
|
48
|
+
|
49
|
+
def forbidden(result = {})
|
50
|
+
body = result[:body] || 'Forbidden'
|
51
|
+
[
|
52
|
+
403,
|
53
|
+
{
|
54
|
+
'Content-Type' => 'text/html',
|
55
|
+
'Content-Length' => body.size.to_s,
|
56
|
+
},
|
57
|
+
[body],
|
58
|
+
]
|
59
|
+
end
|
60
|
+
|
61
|
+
def not_found(result = {})
|
62
|
+
body = result[:body] || 'Not Found'
|
63
|
+
[
|
64
|
+
404,
|
65
|
+
{
|
66
|
+
'Content-Type' => 'text/html',
|
67
|
+
'Content-Length' => body.size.to_s,
|
68
|
+
},
|
69
|
+
[body]
|
70
|
+
]
|
71
|
+
end
|
72
|
+
|
73
|
+
def unprocessable_entity(result = {})
|
74
|
+
body = result[:body].to_s
|
75
|
+
return not_found(result) if body.empty?
|
76
|
+
[
|
77
|
+
422,
|
78
|
+
(
|
79
|
+
result[:headers] ||
|
80
|
+
{
|
81
|
+
'Content-Type' => 'text/html',
|
82
|
+
'Content-Length' => body.size.to_s,
|
83
|
+
}
|
84
|
+
),
|
85
|
+
[body],
|
86
|
+
]
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
data/lib/_workflow/_workflow.rb
CHANGED
@@ -25,12 +25,34 @@ class Bike::Workflow
|
|
25
25
|
:delete => 0b11111,
|
26
26
|
}
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
module SD
|
29
|
+
def _get(arg)
|
30
|
+
if _hide? arg
|
31
|
+
# hidden
|
32
|
+
elsif arg[:action] == :create
|
33
|
+
item_instance '_001'
|
34
|
+
_get_by_tmpl({:action => :create, :conds => {:id => '_001'}}, my[:tmpl][:index])
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def _get_by_self_reference(arg)
|
41
|
+
super unless _hide? arg
|
42
|
+
end
|
43
|
+
|
44
|
+
def _hide?(arg)
|
45
|
+
(arg[:p_action] && arg[:p_action] != :read) ||
|
46
|
+
(arg[:orig_action] == :read && arg[:action] == :submit)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.instance(f)
|
51
|
+
klass = (f[:sd] && f[:sd][:workflow]).to_s.capitalize
|
30
52
|
if klass != ''
|
31
|
-
self.const_get(klass).new
|
53
|
+
self.const_get(klass).new f
|
32
54
|
else
|
33
|
-
self.new
|
55
|
+
self.new f
|
34
56
|
end
|
35
57
|
end
|
36
58
|
|
@@ -40,16 +62,56 @@ class Bike::Workflow
|
|
40
62
|
}.collect{|r| Bike::I18n._ r }
|
41
63
|
end
|
42
64
|
|
43
|
-
attr_reader :
|
65
|
+
attr_reader :f
|
44
66
|
|
45
|
-
def initialize(
|
46
|
-
@
|
67
|
+
def initialize(f)
|
68
|
+
@f = f
|
47
69
|
end
|
48
70
|
|
49
71
|
def default_sub_items
|
50
72
|
self.class.const_get :DEFAULT_SUB_ITEMS
|
51
73
|
end
|
52
74
|
|
75
|
+
def default_meta
|
76
|
+
self.class.const_get :DEFAULT_META
|
77
|
+
end
|
78
|
+
|
79
|
+
def sd_module
|
80
|
+
self.class.const_get :SD
|
81
|
+
end
|
82
|
+
|
83
|
+
def call(method, params)
|
84
|
+
(method == 'post') ? post(params) : get(params)
|
85
|
+
rescue Bike::Error::Forbidden
|
86
|
+
if params[:action] && Bike.client == 'nobody'
|
87
|
+
params[:dest_action] ||= (method == 'post') ? :index : params[:action]
|
88
|
+
params[:action] = :login
|
89
|
+
end
|
90
|
+
Bike::Response.unprocessable_entity(:body => __g_default(params)) rescue Bike::Response.forbidden
|
91
|
+
# TODO: rescue Error::System etc.
|
92
|
+
end
|
93
|
+
|
94
|
+
def get(params)
|
95
|
+
if @f.is_a? Bike::File
|
96
|
+
body = (params[:sub_action] == :small) ? @f.thumbnail : @f.body
|
97
|
+
Bike::Response.ok(
|
98
|
+
:headers => {
|
99
|
+
'Content-Type' => @f.val['type'],
|
100
|
+
'Content-Length' => body.to_s.size.to_s,
|
101
|
+
},
|
102
|
+
:body => body
|
103
|
+
)
|
104
|
+
else
|
105
|
+
m = "_g_#{params[:action]}"
|
106
|
+
respond_to?(m, true) ? __send__(m, params) : _g_default(params)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def post(params)
|
111
|
+
m = "_p_#{params[:action]}"
|
112
|
+
respond_to?(m, true) ? __send__(m, params) : _p_default(params)
|
113
|
+
end
|
114
|
+
|
53
115
|
def permit?(roles, action)
|
54
116
|
case action
|
55
117
|
when :login, :done, :message
|
@@ -62,28 +124,139 @@ class Bike::Workflow
|
|
62
124
|
end
|
63
125
|
end
|
64
126
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
127
|
+
private
|
128
|
+
|
129
|
+
def _g_default(params)
|
130
|
+
Bike::Response.ok :body => __g_default(params)
|
131
|
+
end
|
132
|
+
|
133
|
+
def __g_default(params)
|
134
|
+
f = @f
|
135
|
+
params[:action] ||= f.default_action
|
136
|
+
until f.is_a? Bike::Set::Static::Folder
|
137
|
+
params = {
|
138
|
+
:action => (f.default_action == :read) ? :read : nil,
|
139
|
+
:sub_action => f.send(:summary?, params) ? nil : (params[:sub_action] || :detail),
|
140
|
+
f[:id] => params,
|
141
|
+
}
|
142
|
+
params[:conds] = {:id => f[:id]} if f[:parent].is_a? Bike::Set::Dynamic
|
143
|
+
f = f[:parent]
|
144
|
+
end if f.is_a? Bike::Set::Dynamic
|
145
|
+
|
146
|
+
f.get(params)
|
147
|
+
end
|
148
|
+
|
149
|
+
def _p_default(params)
|
150
|
+
res = __p_validate params
|
151
|
+
return res if res
|
152
|
+
|
153
|
+
__p_set_transaction
|
154
|
+
__p_update params
|
155
|
+
|
156
|
+
if params[:status]
|
157
|
+
if @f[:folder].commit :persistent
|
158
|
+
Bike.transaction[@f[:tid]] = __p_result_summary
|
159
|
+
id_step = __p_result_step(params) if @f[:parent] == @f[:folder] && __p_next_action != :done
|
160
|
+
Bike::Response.see_other(
|
161
|
+
:location => "#{Bike.uri}/#{@f[:tid]}#{@f[:path]}/#{id_step}#{__p_next_action}.html"
|
162
|
+
)
|
163
|
+
else
|
164
|
+
params = {:action => :update}
|
165
|
+
params[:conds] = {:id => @f.errors.keys}
|
166
|
+
Bike::Response.unprocessable_entity :body => __g_default(params)
|
70
167
|
end
|
71
|
-
|
168
|
+
else
|
169
|
+
@f.commit :temp
|
170
|
+
id_step = __p_result_step(params)
|
171
|
+
Bike::Response.see_other(
|
172
|
+
:location => "#{Bike.uri}/#{@f[:tid]}/#{id_step}update.html"
|
173
|
+
)
|
174
|
+
end
|
72
175
|
end
|
73
176
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
177
|
+
def _p_preview(params)
|
178
|
+
res = __p_validate params
|
179
|
+
return res if res
|
180
|
+
|
181
|
+
__p_set_transaction
|
182
|
+
__p_update params
|
183
|
+
|
184
|
+
if @f.commit(:temp) || params[:sub_action] == :delete
|
185
|
+
id_step = __p_result_step(params)
|
186
|
+
action = "preview_#{params[:sub_action]}"
|
187
|
+
Bike::Response.see_other(
|
188
|
+
:location => "#{Bike.uri}/#{@f[:tid]}/#{id_step}#{action}.html"
|
189
|
+
)
|
190
|
+
else
|
191
|
+
params = {:action => :update}
|
192
|
+
params[:conds] = {:id => @f.errors.keys}
|
193
|
+
Bike::Response.unprocessable_entity(:body => __g_default(params))
|
194
|
+
end
|
77
195
|
end
|
78
196
|
|
79
|
-
def
|
197
|
+
def _p_login(params)
|
198
|
+
user = Bike::Set::Static::Folder.root.item('_users', 'main', params['id'].to_s)
|
199
|
+
if user && params['pw'].to_s.crypt(user.val('password')) == user.val('password')
|
200
|
+
Bike.client = params['id']
|
201
|
+
else
|
202
|
+
Bike.client = nil
|
203
|
+
raise Bike::Error::Forbidden
|
204
|
+
end
|
205
|
+
path = Bike::Path.path_of params[:conds]
|
206
|
+
action = (params[:dest_action] =~ /\A\w+\z/) ? params[:dest_action] : 'index'
|
207
|
+
Bike::Response.see_other(
|
208
|
+
:location => "#{Bike.uri}#{@f[:path]}/#{path}#{action}.html"
|
209
|
+
)
|
80
210
|
end
|
81
211
|
|
82
|
-
def
|
212
|
+
def _p_logout(params)
|
213
|
+
return Bike::Response.forbidden(:body => 'invalid token') unless params[:token] == Bike.token
|
214
|
+
|
215
|
+
Bike.client = nil
|
216
|
+
path = Bike::Path.path_of params[:conds]
|
217
|
+
Bike::Response.see_other(
|
218
|
+
:location => "#{Bike.uri}#{@f[:path]}/#{path}index.html"
|
219
|
+
)
|
220
|
+
end
|
221
|
+
alias :_g_logout :_p_logout
|
222
|
+
|
223
|
+
def __p_validate(params)
|
224
|
+
if params[:token] != Bike.token
|
225
|
+
Bike::Response.forbidden(:body => 'invalid token')
|
226
|
+
elsif Bike.transaction[@f[:tid]] && !Bike.transaction[@f[:tid]].is_a?(Bike::Field)
|
227
|
+
Bike::Response.unprocessable_entity(:body => 'transaction expired')
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def __p_set_transaction
|
232
|
+
Bike.transaction[@f[:tid]] ||= @f if @f[:tid] =~ Bike::REX::TID
|
233
|
+
end
|
234
|
+
|
235
|
+
def __p_update(params)
|
236
|
+
@f.update params
|
237
|
+
end
|
238
|
+
|
239
|
+
def __p_result_summary
|
240
|
+
(@f.result || {}).values.inject({}) {|summary, item|
|
241
|
+
item_result = item.result.is_a?(::Symbol) ? item.result : :update
|
242
|
+
summary[item_result] = summary[item_result].to_i + 1
|
243
|
+
summary
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
def __p_result_step(params)
|
248
|
+
if @f.result
|
249
|
+
id = @f.result.values.collect {|item| item[:id] }
|
250
|
+
else
|
251
|
+
id = params.keys.select {|id|
|
252
|
+
id.is_a?(::String) && (id[Bike::REX::ID] || id[Bike::REX::ID_NEW])
|
253
|
+
}
|
254
|
+
end
|
255
|
+
Bike::Path.path_of(:id => id)
|
83
256
|
end
|
84
257
|
|
85
|
-
def
|
86
|
-
(
|
258
|
+
def __p_next_action
|
259
|
+
(!@f.result || @f.result.values.all? {|item| item.permit? :read }) ? :read_detail : :done
|
87
260
|
end
|
88
261
|
|
89
262
|
end
|
data/lib/_workflow/attachment.rb
CHANGED
@@ -17,14 +17,11 @@ class Bike::Workflow::Attachment < Bike::Workflow
|
|
17
17
|
:delete => 0b00000,
|
18
18
|
}
|
19
19
|
|
20
|
-
|
21
|
-
(
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def _get(arg)
|
26
|
-
@sd.instance_eval {
|
27
|
-
if arg[:action] == :create || arg[:action] == :update
|
20
|
+
module SD
|
21
|
+
def _get(arg)
|
22
|
+
if _hide? arg
|
23
|
+
# hidden
|
24
|
+
elsif arg[:action] == :create || arg[:action] == :update
|
28
25
|
new_item = item_instance '_001'
|
29
26
|
|
30
27
|
item_outs = _g_default(arg) {|item, item_arg|
|
@@ -39,12 +36,23 @@ _html
|
|
39
36
|
}
|
40
37
|
tmpl = my[:tmpl][:index].gsub('$()', item_outs)
|
41
38
|
_get_by_tmpl({:p_action => arg[:p_action], :action => :update}, tmpl)
|
39
|
+
else
|
40
|
+
super
|
42
41
|
end
|
43
|
-
|
42
|
+
end
|
43
|
+
|
44
|
+
def _get_by_self_reference(arg)
|
45
|
+
super unless _hide? arg
|
46
|
+
end
|
47
|
+
|
48
|
+
def _hide?(arg)
|
49
|
+
arg[:action] == :submit
|
50
|
+
end
|
44
51
|
end
|
45
52
|
|
46
|
-
def
|
47
|
-
|
53
|
+
def permit?(roles, action)
|
54
|
+
(action == :login) ||
|
55
|
+
(@f[:parent] && @f[:parent].permit?(action))
|
48
56
|
end
|
49
57
|
|
50
58
|
end
|
data/lib/_workflow/register.rb
CHANGED
@@ -24,15 +24,18 @@ class Bike::Workflow::Register < Bike::Workflow
|
|
24
24
|
:delete => 0b11100,
|
25
25
|
}
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
private
|
28
|
+
|
29
|
+
def __p_update(params)
|
30
|
+
super
|
31
|
+
@f.send(:pending_items).each {|id, item|
|
29
32
|
if id =~ Bike::REX::ID_NEW
|
30
33
|
item.item('_owner').instance_variable_set(:@val, item.item('_id').val)
|
31
34
|
end
|
32
35
|
}
|
33
36
|
end
|
34
37
|
|
35
|
-
def
|
38
|
+
def __p_next_action
|
36
39
|
(Bike.client == 'nobody') ? :done : super
|
37
40
|
end
|
38
41
|
|
data/lib/bike.rb
CHANGED
@@ -114,158 +114,16 @@ class Bike
|
|
114
114
|
else
|
115
115
|
base = Bike::Path.base_of path
|
116
116
|
end
|
117
|
-
return
|
117
|
+
return Bike::Response.not_found unless base
|
118
118
|
|
119
119
|
base[:tid] = tid
|
120
120
|
Bike.current[:base] = base
|
121
121
|
|
122
|
-
|
123
|
-
if params[:action] == :logout && params[:token] == Bike.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] != Bike.token
|
132
|
-
response_forbidden(:body => 'invalid token')
|
133
|
-
elsif Bike.transaction[tid] && !Bike.transaction[tid].is_a?(Bike::Field)
|
134
|
-
response_unprocessable_entity(:body => 'transaction expired')
|
135
|
-
else
|
136
|
-
begin
|
137
|
-
post(base, params)
|
138
|
-
rescue Bike::Error::Forbidden
|
139
|
-
response_forbidden
|
140
|
-
end
|
141
|
-
end
|
142
|
-
rescue Bike::Error::Forbidden
|
143
|
-
if params[:action] && Bike.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
|
122
|
+
base.workflow.call(method, params)
|
150
123
|
end
|
151
124
|
|
152
125
|
private
|
153
126
|
|
154
|
-
def login(base, params)
|
155
|
-
user = Bike::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
|
-
Bike.client = params['id']
|
158
|
-
else
|
159
|
-
Bike.client = nil
|
160
|
-
raise Bike::Error::Forbidden
|
161
|
-
end
|
162
|
-
path = Bike::Path.path_of params[:conds]
|
163
|
-
action = (params['dest_action'] =~ /\A\w+\z/) ? params['dest_action'] : 'index'
|
164
|
-
response_see_other(
|
165
|
-
:location => "#{Bike.uri}#{base[:path]}/#{path}#{action}.html"
|
166
|
-
)
|
167
|
-
end
|
168
|
-
|
169
|
-
def logout(base, params)
|
170
|
-
Bike.client = nil
|
171
|
-
path = Bike::Path.path_of params[:conds]
|
172
|
-
response_see_other(
|
173
|
-
:location => "#{Bike.uri}#{base[:path]}/#{path}index.html"
|
174
|
-
)
|
175
|
-
end
|
176
|
-
|
177
|
-
def get(base, params)
|
178
|
-
if base.is_a? Bike::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
|
-
Bike.transaction[base[:tid]] ||= base if base[:tid] =~ Bike::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 => "#{Bike.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
|
-
Bike.transaction[base[:tid]] ||= base if base[:tid] =~ Bike::REX::TID
|
211
|
-
|
212
|
-
base.update params
|
213
|
-
if params[:status]
|
214
|
-
if base[:folder].commit :persistent
|
215
|
-
Bike.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 => "#{Bike.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 => "#{Bike.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[Bike::REX::ID] || id[Bike::REX::ID_NEW])
|
249
|
-
}
|
250
|
-
end
|
251
|
-
Bike::Path.path_of(:id => id)
|
252
|
-
end
|
253
|
-
|
254
|
-
def _get(f, params)
|
255
|
-
params[:action] ||= f.default_action
|
256
|
-
until f.is_a? Bike::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? Bike::Set::Dynamic
|
263
|
-
f = f[:parent]
|
264
|
-
end if f.is_a? Bike::Set::Dynamic
|
265
|
-
|
266
|
-
f.get params
|
267
|
-
end
|
268
|
-
|
269
127
|
def params_from_request(req)
|
270
128
|
params = {
|
271
129
|
:action => Bike::Path.action_of(req.path_info),
|
@@ -306,6 +164,8 @@ class Bike
|
|
306
164
|
hash[item_id][:self] = val
|
307
165
|
elsif item_id == '_token'
|
308
166
|
hash[:token] = val
|
167
|
+
elsif item_id == 'dest_action'
|
168
|
+
hash[:dest_action] = val
|
309
169
|
else
|
310
170
|
hash[item_id] = val
|
311
171
|
end
|
@@ -314,83 +174,4 @@ class Bike
|
|
314
174
|
}
|
315
175
|
end
|
316
176
|
|
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
177
|
end
|
data/lib/set/dynamic.rb
CHANGED
@@ -7,20 +7,21 @@ class Bike::Set::Dynamic < Bike::Field
|
|
7
7
|
|
8
8
|
include Bike::Set
|
9
9
|
|
10
|
-
attr_reader :storage
|
10
|
+
attr_reader :storage
|
11
11
|
|
12
12
|
def initialize(meta = {})
|
13
13
|
@meta = meta
|
14
14
|
@storage = Bike::Storage.instance self
|
15
|
-
@
|
16
|
-
@meta = @workflow.class.const_get(:DEFAULT_META).merge @meta
|
15
|
+
@meta = workflow.default_meta.merge @meta
|
17
16
|
@item_object = {}
|
18
17
|
|
18
|
+
self.extend workflow.sd_module
|
19
|
+
|
19
20
|
my[:item] ||= {
|
20
21
|
'default' => {:item => {}}
|
21
22
|
}
|
22
23
|
my[:item].each {|type, item_meta|
|
23
|
-
item_meta[:item] =
|
24
|
+
item_meta[:item] = workflow.default_sub_items.merge item_meta[:item]
|
24
25
|
}
|
25
26
|
|
26
27
|
my[:p_size] = meta[:max] if meta[:max]
|
@@ -50,8 +51,6 @@ class Bike::Set::Dynamic < Bike::Field
|
|
50
51
|
end
|
51
52
|
|
52
53
|
def commit(type = :temp)
|
53
|
-
@workflow.before_commit
|
54
|
-
|
55
54
|
items = pending_items
|
56
55
|
items.each {|id, item|
|
57
56
|
item.commit(:temp) || next
|
@@ -66,7 +65,6 @@ class Bike::Set::Dynamic < Bike::Field
|
|
66
65
|
if valid?
|
67
66
|
@result = (@action == :update) ? items : @action
|
68
67
|
@action = nil if type == :persistent
|
69
|
-
@workflow.after_commit
|
70
68
|
self
|
71
69
|
end
|
72
70
|
end
|
@@ -77,10 +75,6 @@ class Bike::Set::Dynamic < Bike::Field
|
|
77
75
|
@storage.val
|
78
76
|
end
|
79
77
|
|
80
|
-
def _get(arg)
|
81
|
-
(@workflow._get(arg) || super) unless @workflow._hide? arg
|
82
|
-
end
|
83
|
-
|
84
78
|
def _get_by_tmpl(arg, tmpl = '')
|
85
79
|
if arg[:action] == :read || self != Bike.base
|
86
80
|
super
|
@@ -95,10 +89,6 @@ _html
|
|
95
89
|
end
|
96
90
|
end
|
97
91
|
|
98
|
-
def _get_by_self_reference(arg)
|
99
|
-
super unless @workflow._hide?(arg)
|
100
|
-
end
|
101
|
-
|
102
92
|
def permit_get?(arg)
|
103
93
|
permit?(arg[:action]) || collect_item(arg[:conds] || {}).all? {|item|
|
104
94
|
item[:id][Bike::REX::ID_NEW] ?
|
data/t/skin/_users/index.html
CHANGED
data/t/test_bike.rb
CHANGED
@@ -87,6 +87,21 @@ class TC_Bike < Test::Unit::TestCase
|
|
87
87
|
'Bike#rebuild_params should rebuild both the special symbols and regular items'
|
88
88
|
)
|
89
89
|
|
90
|
+
hash = bike.instance_eval {
|
91
|
+
rebuild_params(
|
92
|
+
'_token' => 'foo',
|
93
|
+
'dest_action' => 'bar'
|
94
|
+
)
|
95
|
+
}
|
96
|
+
assert_equal(
|
97
|
+
{
|
98
|
+
:token => 'foo',
|
99
|
+
:dest_action => 'bar',
|
100
|
+
},
|
101
|
+
hash,
|
102
|
+
'Bike#rebuild_params should use symbols for special keys'
|
103
|
+
)
|
104
|
+
|
90
105
|
hash = bike.instance_eval {
|
91
106
|
rebuild_params(
|
92
107
|
'moo.conds-p' => '9',
|
@@ -648,115 +663,6 @@ class TC_Bike < Test::Unit::TestCase
|
|
648
663
|
)
|
649
664
|
end
|
650
665
|
|
651
|
-
def test_login
|
652
|
-
Bike.client = nil
|
653
|
-
res = Bike.new.send(
|
654
|
-
:login,
|
655
|
-
Bike::Set::Static::Folder.root.item('foo', 'main'),
|
656
|
-
{'id' => 'test', 'pw' => 'test', :conds => {:id => '20100222_0123'}, 'dest_action' => 'update'}
|
657
|
-
)
|
658
|
-
assert_equal(
|
659
|
-
'test',
|
660
|
-
Bike.client,
|
661
|
-
'Bike#login should set Bike.client given a valid pair of user/password'
|
662
|
-
)
|
663
|
-
assert_match(
|
664
|
-
%r{/foo/20100222/123/update.html},
|
665
|
-
res[1]['Location'],
|
666
|
-
'Bike#login should return a proper location header'
|
667
|
-
)
|
668
|
-
end
|
669
|
-
|
670
|
-
def test_login_default_action
|
671
|
-
Bike.client = nil
|
672
|
-
res = Bike.new.send(
|
673
|
-
:login,
|
674
|
-
Bike::Set::Static::Folder.root.item('foo', 'main'),
|
675
|
-
{'id' => 'test', 'pw' => 'test', :conds => {:id => '20100222_0123'}}
|
676
|
-
)
|
677
|
-
assert_match(
|
678
|
-
%r{/foo/20100222/123/index.html},
|
679
|
-
res[1]['Location'],
|
680
|
-
"Bike#login should set 'index' as the default action of a location"
|
681
|
-
)
|
682
|
-
end
|
683
|
-
|
684
|
-
def test_login_with_wrong_account
|
685
|
-
Bike.client = nil
|
686
|
-
|
687
|
-
assert_raise(
|
688
|
-
Bike::Error::Forbidden,
|
689
|
-
'Bike#login should raise Error::Forbidden given a non-existent user'
|
690
|
-
) {
|
691
|
-
Bike.new.send(
|
692
|
-
:login,
|
693
|
-
Bike::Set::Static::Folder.root.item('foo', 'main'),
|
694
|
-
{'id' => 'non-existent', 'pw' => 'test'}
|
695
|
-
)
|
696
|
-
}
|
697
|
-
assert_equal(
|
698
|
-
'nobody',
|
699
|
-
Bike.client,
|
700
|
-
'Bike#login should not set Bike.client with a non-existent user'
|
701
|
-
)
|
702
|
-
|
703
|
-
assert_raise(
|
704
|
-
Bike::Error::Forbidden,
|
705
|
-
'Bike#login should raise Error::Forbidden given a empty password'
|
706
|
-
) {
|
707
|
-
Bike.new.send(
|
708
|
-
:login,
|
709
|
-
Bike::Set::Static::Folder.root.item('foo', 'main'),
|
710
|
-
{'id' => 'test', 'pw' => nil}
|
711
|
-
)
|
712
|
-
}
|
713
|
-
assert_equal(
|
714
|
-
'nobody',
|
715
|
-
Bike.client,
|
716
|
-
'Bike#login should not set Bike.client with an empty password'
|
717
|
-
)
|
718
|
-
|
719
|
-
assert_raise(
|
720
|
-
Bike::Error::Forbidden,
|
721
|
-
'Bike#login should raise Error::Forbidden given a wrong password'
|
722
|
-
) {
|
723
|
-
res = Bike.new.send(
|
724
|
-
:login,
|
725
|
-
Bike::Set::Static::Folder.root.item('foo', 'main'),
|
726
|
-
{
|
727
|
-
'id' => 'test',
|
728
|
-
'pw' => 'wrong',
|
729
|
-
:conds => {:id => '20100222_0123'},
|
730
|
-
'dest_action' => 'update'
|
731
|
-
}
|
732
|
-
)
|
733
|
-
}
|
734
|
-
assert_equal(
|
735
|
-
'nobody',
|
736
|
-
Bike.client,
|
737
|
-
'Bike#login should not set Bike.client with a wrong password'
|
738
|
-
)
|
739
|
-
end
|
740
|
-
|
741
|
-
def test_logout
|
742
|
-
Bike.client = 'frank'
|
743
|
-
res = Bike.new.send(
|
744
|
-
:logout,
|
745
|
-
Bike::Set::Static::Folder.root.item('foo', 'main'),
|
746
|
-
{'id' => 'test', 'pw' => 'test', :conds => {:id => '20100222_0123'}}
|
747
|
-
)
|
748
|
-
assert_equal(
|
749
|
-
'nobody',
|
750
|
-
Bike.client,
|
751
|
-
'Bike#logout should clear Bike.client'
|
752
|
-
)
|
753
|
-
assert_match(
|
754
|
-
%r{/foo/20100222/123/index.html},
|
755
|
-
res[1]['Location'],
|
756
|
-
'Bike#logout should return a proper location header'
|
757
|
-
)
|
758
|
-
end
|
759
|
-
|
760
666
|
def test_libdir
|
761
667
|
assert_match(
|
762
668
|
%r{^.*/lib$},
|
data/t/test_call.rb
CHANGED
@@ -772,7 +772,7 @@ _html
|
|
772
772
|
res = Rack::MockRequest.new(@bike).post(
|
773
773
|
"http://#{base_uri}/t_store/main/update.html",
|
774
774
|
{
|
775
|
-
:input => ".action-preview_update=submit&_1-name=fz&_1-comment=howdy.&.status-public=create"
|
775
|
+
:input => ".action-preview_update=submit&_1-name=fz&_1-comment=howdy.&.status-public=create&_token=#{Bike.token}"
|
776
776
|
}
|
777
777
|
)
|
778
778
|
tid = res.headers['Location'][Bike::REX::TID]
|
@@ -861,7 +861,7 @@ _html
|
|
861
861
|
res = Rack::MockRequest.new(@bike).post(
|
862
862
|
'http://example.com/t_store/main/update.html',
|
863
863
|
{
|
864
|
-
:input => ".action-preview_update=submit&_1-name=verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrylong&_1-comment=howdy.&.status-public=create"
|
864
|
+
:input => ".action-preview_update=submit&_1-name=verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrylong&_1-comment=howdy.&.status-public=create&_token=#{Bike.token}"
|
865
865
|
}
|
866
866
|
)
|
867
867
|
assert_equal(
|
@@ -1054,6 +1054,16 @@ _html
|
|
1054
1054
|
res.status,
|
1055
1055
|
'Bike#call with :login action should return status 422 upon failure'
|
1056
1056
|
)
|
1057
|
+
assert_match(
|
1058
|
+
/^<html/,
|
1059
|
+
res.body,
|
1060
|
+
'Bike#call with :login action should return login form upon failure'
|
1061
|
+
)
|
1062
|
+
assert_match(
|
1063
|
+
/name="dest_action" value="update"/,
|
1064
|
+
res.body,
|
1065
|
+
'Bike#call with :login action should keep the dest_action upon failure'
|
1066
|
+
)
|
1057
1067
|
end
|
1058
1068
|
|
1059
1069
|
def test_post_logout
|
@@ -1278,4 +1288,26 @@ _html
|
|
1278
1288
|
)
|
1279
1289
|
end
|
1280
1290
|
|
1291
|
+
def test_workflow_register
|
1292
|
+
Bike.client = nil
|
1293
|
+
Bike::Set::Static::Folder.root.item('_users', 'main').storage.delete('00000000_don')
|
1294
|
+
|
1295
|
+
res = Rack::MockRequest.new(@bike).post(
|
1296
|
+
'http://example.com/_users/main/update.html',
|
1297
|
+
{
|
1298
|
+
:input => "_1-_id=don&_1-password=secret&.status-public=create&_token=#{Bike.token}"
|
1299
|
+
}
|
1300
|
+
)
|
1301
|
+
assert_match(
|
1302
|
+
/done\.html$/,
|
1303
|
+
res.headers['Location'],
|
1304
|
+
'Workflow::Register should have special __p_next_action().'
|
1305
|
+
)
|
1306
|
+
assert_equal(
|
1307
|
+
'don',
|
1308
|
+
Bike::Set::Static::Folder.root.item('_users', 'main', 'don')[:owner],
|
1309
|
+
'Workflow::Register should set special item[:owner]'
|
1310
|
+
)
|
1311
|
+
end
|
1312
|
+
|
1281
1313
|
end
|
data/t/test_workflow.rb
CHANGED
@@ -38,8 +38,8 @@ class TC_Workflow < Test::Unit::TestCase
|
|
38
38
|
|
39
39
|
assert_equal(
|
40
40
|
sd,
|
41
|
-
Bike::Workflow.instance(sd).
|
42
|
-
'Bike::Workflow.instance should set @
|
41
|
+
Bike::Workflow.instance(sd).f,
|
42
|
+
'Bike::Workflow.instance should set @f'
|
43
43
|
)
|
44
44
|
end
|
45
45
|
|
@@ -364,4 +364,107 @@ class TC_Workflow < Test::Unit::TestCase
|
|
364
364
|
)
|
365
365
|
end
|
366
366
|
|
367
|
+
def test_login
|
368
|
+
Bike.client = nil
|
369
|
+
res = Bike::Set::Static::Folder.root.item('foo', 'main').workflow.send(
|
370
|
+
:_p_login,
|
371
|
+
{'id' => 'test', 'pw' => 'test', :conds => {:id => '20100222_0123'}, :dest_action => 'update'}
|
372
|
+
)
|
373
|
+
assert_equal(
|
374
|
+
'test',
|
375
|
+
Bike.client,
|
376
|
+
'Bike#login should set Bike.client given a valid pair of user/password'
|
377
|
+
)
|
378
|
+
assert_match(
|
379
|
+
%r{/foo/20100222/123/update.html},
|
380
|
+
res[1]['Location'],
|
381
|
+
'Bike#login should return a proper location header'
|
382
|
+
)
|
383
|
+
end
|
384
|
+
|
385
|
+
def test_login_default_action
|
386
|
+
Bike.client = nil
|
387
|
+
res = Bike::Set::Static::Folder.root.item('foo', 'main').workflow.send(
|
388
|
+
:_p_login,
|
389
|
+
{'id' => 'test', 'pw' => 'test', :conds => {:id => '20100222_0123'}}
|
390
|
+
)
|
391
|
+
assert_match(
|
392
|
+
%r{/foo/20100222/123/index.html},
|
393
|
+
res[1]['Location'],
|
394
|
+
"Bike#login should set 'index' as the default action of a location"
|
395
|
+
)
|
396
|
+
end
|
397
|
+
|
398
|
+
def test_login_with_wrong_account
|
399
|
+
Bike.client = nil
|
400
|
+
|
401
|
+
assert_raise(
|
402
|
+
Bike::Error::Forbidden,
|
403
|
+
'Bike#login should raise Error::Forbidden given a non-existent user'
|
404
|
+
) {
|
405
|
+
res = Bike::Set::Static::Folder.root.item('foo', 'main').workflow.send(
|
406
|
+
:_p_login,
|
407
|
+
{'id' => 'non-existent', 'pw' => 'test'}
|
408
|
+
)
|
409
|
+
}
|
410
|
+
assert_equal(
|
411
|
+
'nobody',
|
412
|
+
Bike.client,
|
413
|
+
'Bike#login should not set Bike.client with a non-existent user'
|
414
|
+
)
|
415
|
+
|
416
|
+
assert_raise(
|
417
|
+
Bike::Error::Forbidden,
|
418
|
+
'Bike#login should raise Error::Forbidden given a empty password'
|
419
|
+
) {
|
420
|
+
res = Bike::Set::Static::Folder.root.item('foo', 'main').workflow.send(
|
421
|
+
:_p_login,
|
422
|
+
{'id' => 'test', 'pw' => nil}
|
423
|
+
)
|
424
|
+
}
|
425
|
+
assert_equal(
|
426
|
+
'nobody',
|
427
|
+
Bike.client,
|
428
|
+
'Bike#login should not set Bike.client with an empty password'
|
429
|
+
)
|
430
|
+
|
431
|
+
assert_raise(
|
432
|
+
Bike::Error::Forbidden,
|
433
|
+
'Bike#login should raise Error::Forbidden given a wrong password'
|
434
|
+
) {
|
435
|
+
res = Bike::Set::Static::Folder.root.item('foo', 'main').workflow.send(
|
436
|
+
:_p_login,
|
437
|
+
{
|
438
|
+
'id' => 'test',
|
439
|
+
'pw' => 'wrong',
|
440
|
+
:conds => {:id => '20100222_0123'},
|
441
|
+
:dest_action => 'update'
|
442
|
+
}
|
443
|
+
)
|
444
|
+
}
|
445
|
+
assert_equal(
|
446
|
+
'nobody',
|
447
|
+
Bike.client,
|
448
|
+
'Bike#login should not set Bike.client with a wrong password'
|
449
|
+
)
|
450
|
+
end
|
451
|
+
|
452
|
+
def test_logout
|
453
|
+
Bike.client = 'frank'
|
454
|
+
res = Bike::Set::Static::Folder.root.item('foo', 'main').workflow.send(
|
455
|
+
:_p_logout,
|
456
|
+
{'id' => 'test', 'pw' => 'test', :conds => {:id => '20100222_0123'}, :token => Bike.token}
|
457
|
+
)
|
458
|
+
assert_equal(
|
459
|
+
'nobody',
|
460
|
+
Bike.client,
|
461
|
+
'Bike#logout should clear Bike.client'
|
462
|
+
)
|
463
|
+
assert_match(
|
464
|
+
%r{/foo/20100222/123/index.html},
|
465
|
+
res[1]['Location'],
|
466
|
+
'Bike#logout should return a proper location header'
|
467
|
+
)
|
468
|
+
end
|
469
|
+
|
367
470
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 2
|
9
|
+
version: 0.2.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Akira FUNAI
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-10-12 00:00:00 +09:00
|
18
18
|
default_executable: bike
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -146,6 +146,7 @@ files:
|
|
146
146
|
- lib/_i18n.rb
|
147
147
|
- lib/_parser.rb
|
148
148
|
- lib/_path.rb
|
149
|
+
- lib/_response.rb
|
149
150
|
- lib/_storage/_storage.rb
|
150
151
|
- lib/_storage/file.rb
|
151
152
|
- lib/_storage/sequel.rb
|