dao 2.2.3 → 3.1.0
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.
- data/Rakefile +68 -113
- data/TODO +3 -9
- data/a.rb +25 -0
- data/dao.gemspec +70 -14
- data/lib/dao.rb +8 -8
- data/lib/dao/api.rb +1 -0
- data/lib/dao/api/context.rb +48 -23
- data/lib/dao/api/dsl.rb +1 -1
- data/lib/dao/api/interfaces.rb +149 -117
- data/lib/dao/api/modes.rb +24 -23
- data/lib/dao/api/routes.rb +9 -0
- data/lib/dao/data.rb +9 -2
- data/lib/dao/errors.rb +15 -11
- data/lib/dao/form.rb +46 -37
- data/lib/dao/interface.rb +1 -1
- data/lib/dao/mode.rb +45 -20
- data/lib/dao/params.rb +35 -53
- data/lib/dao/path.rb +6 -9
- data/lib/dao/rails/lib/generators/dao/templates/api.rb +1 -1
- data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +16 -0
- data/lib/dao/result.rb +26 -133
- data/lib/dao/route.rb +87 -0
- data/lib/dao/status.rb +96 -94
- data/lib/dao/support.rb +5 -3
- data/lib/dao/validations.rb +46 -442
- data/lib/dao/validations/base.rb +68 -0
- data/lib/dao/validations/common.rb +463 -0
- data/test/dao_test.rb +214 -33
- metadata +20 -112
- data/lib/dao/rails/app/api.rb +0 -55
- data/lib/dao/rails/app/controllers/api_controller.rb +0 -99
- data/sample/rails_app/Gemfile +0 -33
- data/sample/rails_app/Gemfile.lock +0 -88
- data/sample/rails_app/README +0 -1
- data/sample/rails_app/Rakefile +0 -7
- data/sample/rails_app/app/api.rb +0 -55
- data/sample/rails_app/app/controllers/api_controller.rb +0 -99
- data/sample/rails_app/app/controllers/application_controller.rb +0 -3
- data/sample/rails_app/app/helpers/application_helper.rb +0 -2
- data/sample/rails_app/app/views/layouts/application.html.erb +0 -14
- data/sample/rails_app/config.ru +0 -4
- data/sample/rails_app/config/application.rb +0 -51
- data/sample/rails_app/config/boot.rb +0 -13
- data/sample/rails_app/config/database.yml +0 -22
- data/sample/rails_app/config/environment.rb +0 -5
- data/sample/rails_app/config/environments/development.rb +0 -26
- data/sample/rails_app/config/environments/production.rb +0 -49
- data/sample/rails_app/config/environments/test.rb +0 -35
- data/sample/rails_app/config/initializers/backtrace_silencers.rb +0 -7
- data/sample/rails_app/config/initializers/inflections.rb +0 -10
- data/sample/rails_app/config/initializers/mime_types.rb +0 -5
- data/sample/rails_app/config/initializers/secret_token.rb +0 -7
- data/sample/rails_app/config/initializers/session_store.rb +0 -8
- data/sample/rails_app/config/locales/en.yml +0 -5
- data/sample/rails_app/config/routes.rb +0 -62
- data/sample/rails_app/db/development.sqlite3 +0 -0
- data/sample/rails_app/db/seeds.rb +0 -7
- data/sample/rails_app/doc/README_FOR_APP +0 -2
- data/sample/rails_app/log/development.log +0 -27
- data/sample/rails_app/log/production.log +0 -0
- data/sample/rails_app/log/server.log +0 -0
- data/sample/rails_app/log/test.log +0 -0
- data/sample/rails_app/pubic/javascripts/dao.js +0 -148
- data/sample/rails_app/public/404.html +0 -26
- data/sample/rails_app/public/422.html +0 -26
- data/sample/rails_app/public/500.html +0 -26
- data/sample/rails_app/public/favicon.ico +0 -0
- data/sample/rails_app/public/images/rails.png +0 -0
- data/sample/rails_app/public/index.html +0 -239
- data/sample/rails_app/public/javascripts/application.js +0 -2
- data/sample/rails_app/public/javascripts/controls.js +0 -965
- data/sample/rails_app/public/javascripts/dragdrop.js +0 -974
- data/sample/rails_app/public/javascripts/effects.js +0 -1123
- data/sample/rails_app/public/javascripts/prototype.js +0 -6001
- data/sample/rails_app/public/javascripts/rails.js +0 -175
- data/sample/rails_app/public/robots.txt +0 -5
- data/sample/rails_app/script/rails +0 -6
- data/sample/rails_app/test/performance/browsing_test.rb +0 -9
- data/sample/rails_app/test/test_helper.rb +0 -13
data/lib/dao/api.rb
CHANGED
data/lib/dao/api/context.rb
CHANGED
@@ -1,38 +1,63 @@
|
|
1
1
|
module Dao
|
2
2
|
class Context
|
3
|
-
Attrs = %w( api interface params result
|
4
|
-
Attrs.each{|attr| attr_accessor(attr)}
|
3
|
+
Attrs = %w( api route path interface method args status errors params result data form validations )
|
5
4
|
|
6
|
-
|
5
|
+
Attrs.each{|a| attr_accessor(a)}
|
6
|
+
|
7
|
+
def Context.attrs
|
8
|
+
Attrs
|
9
|
+
end
|
10
|
+
|
11
|
+
def Context.for(api, route, path, interface, params, *args)
|
12
|
+
# setup
|
13
|
+
#
|
7
14
|
options = Dao.options_for!(args)
|
8
15
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
params
|
14
|
-
result = Result.new(:api => api, :interface => interface, :params => params)
|
15
|
-
params.result = result
|
16
|
+
parsed_params = Dao.parse(path, params, options)
|
17
|
+
|
18
|
+
result = Result.new(:mode => api.mode)
|
19
|
+
params = result.params
|
20
|
+
params.update(parsed_params)
|
16
21
|
|
17
22
|
method = interface.method.bind(api)
|
18
23
|
args = [params, result].slice(0, method.arity)
|
19
24
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
# build the context
|
26
|
+
#
|
27
|
+
context = new
|
28
|
+
context.api = api
|
29
|
+
context.interface = interface
|
30
|
+
context.route = route
|
31
|
+
context.path = path
|
32
|
+
context.method = method
|
33
|
+
context.args = args
|
34
|
+
context.status = Status.default
|
35
|
+
context.errors = Errors.new
|
27
36
|
|
28
|
-
|
29
|
-
|
37
|
+
context.result = result
|
38
|
+
context.data = result.data
|
39
|
+
|
40
|
+
context.params = params
|
41
|
+
context.form = params.form
|
42
|
+
context.validations = params.validations
|
43
|
+
|
44
|
+
# wire up shared state
|
45
|
+
#
|
46
|
+
result.route = context.route
|
47
|
+
result.path = context.path
|
48
|
+
result.status = context.status
|
49
|
+
result.errors = context.errors
|
50
|
+
|
51
|
+
params.route = context.route
|
52
|
+
params.path = context.path
|
53
|
+
params.status = context.status
|
54
|
+
params.errors = context.errors
|
55
|
+
|
56
|
+
context
|
30
57
|
end
|
31
58
|
|
32
|
-
def
|
33
|
-
|
34
|
-
send("#{ key }=", val)
|
35
|
-
end
|
59
|
+
def call
|
60
|
+
method.call(*args)
|
36
61
|
end
|
37
62
|
end
|
38
63
|
end
|
data/lib/dao/api/dsl.rb
CHANGED
@@ -19,7 +19,7 @@ module Dao
|
|
19
19
|
raise "no interface for #{ docs.inspect }" unless docs.empty?
|
20
20
|
end
|
21
21
|
|
22
|
-
%w( interface doc docs description desc ).each do |method|
|
22
|
+
%w( interface call doc docs description desc ).each do |method|
|
23
23
|
module_eval <<-__, __FILE__, __LINE__ - 1
|
24
24
|
|
25
25
|
def #{ method }(*args, &block)
|
data/lib/dao/api/interfaces.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Dao
|
2
2
|
class Api
|
3
|
+
# class methods
|
4
|
+
#
|
3
5
|
class << Api
|
4
6
|
def interfaces
|
5
7
|
@interfaces ||= Map.new
|
@@ -9,23 +11,24 @@ module Dao
|
|
9
11
|
api = self
|
10
12
|
path = Path.new(path)
|
11
13
|
|
14
|
+
route = routes.add(path) if Route.like?(path)
|
15
|
+
|
12
16
|
method =
|
13
17
|
module_eval{
|
14
18
|
define_method(path + '/interface', &block)
|
15
19
|
instance_method(path + '/interface')
|
16
20
|
}
|
17
21
|
|
18
|
-
|
19
22
|
interface = Interface.new(
|
20
23
|
'api' => api,
|
21
24
|
'path' => path,
|
25
|
+
'route' => route,
|
22
26
|
'method' => method,
|
23
27
|
'doc' => docs.pop
|
24
28
|
)
|
25
29
|
|
26
30
|
interfaces[path] = interface
|
27
31
|
end
|
28
|
-
|
29
32
|
alias_method('call', 'interface')
|
30
33
|
|
31
34
|
def description(string)
|
@@ -51,197 +54,226 @@ module Dao
|
|
51
54
|
def index
|
52
55
|
index = Map.new
|
53
56
|
interfaces.each do |path, interface|
|
54
|
-
index[path] = interface.doc || {'description' =>
|
57
|
+
index[path] = interface.doc || {'description' => ''}
|
55
58
|
end
|
56
59
|
index
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
63
|
+
# instance methods
|
64
|
+
#
|
65
|
+
|
66
|
+
|
67
|
+
# call support
|
68
|
+
#
|
60
69
|
def call(path = '/index', params = {}, options = {})
|
61
70
|
api = self
|
62
71
|
path = Path.new(path)
|
63
|
-
interface = interfaces[path]
|
72
|
+
interface = interfaces[path] ### interfaces.by_path(path)
|
73
|
+
route = nil
|
74
|
+
|
75
|
+
unless interface
|
76
|
+
route = route_for(path)
|
77
|
+
interface = interfaces[route]
|
78
|
+
end
|
64
79
|
|
65
80
|
unless interface
|
66
81
|
return index if path == '/index'
|
67
82
|
raise(NameError, "NO SUCH INTERFACE: #{ path }")
|
68
83
|
end
|
69
84
|
|
70
|
-
|
71
|
-
|
72
|
-
|
85
|
+
if route
|
86
|
+
params.update(route.params_for(path))
|
87
|
+
path = route.path_for(params)
|
88
|
+
else
|
89
|
+
if Route.like?(path)
|
90
|
+
route = Route.new(path)
|
91
|
+
path = route.path_for(params)
|
92
|
+
end
|
93
|
+
end
|
73
94
|
|
74
|
-
context = Context.
|
75
|
-
:api => api,
|
76
|
-
:interface => interface,
|
77
|
-
:params => params
|
78
|
-
)
|
95
|
+
context = Context.for(api, route, path, interface, params, options)
|
79
96
|
|
80
97
|
callstack(context) do
|
81
|
-
catching(:result)
|
98
|
+
catching(:result) do
|
99
|
+
context.call()
|
100
|
+
end
|
82
101
|
end
|
83
102
|
|
84
103
|
context.result
|
85
104
|
end
|
86
105
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
106
|
+
# lookup a route
|
107
|
+
#
|
108
|
+
def route_for(*args)
|
109
|
+
self.class.routes.match(*args)
|
91
110
|
end
|
92
111
|
|
93
|
-
|
94
|
-
|
112
|
+
# context support
|
113
|
+
#
|
114
|
+
def callstack(context = nil, &block)
|
115
|
+
@callstack ||= []
|
116
|
+
|
117
|
+
if block and context
|
118
|
+
begin
|
119
|
+
@callstack.push(context)
|
120
|
+
return block.call()
|
121
|
+
ensure
|
122
|
+
@callstack.pop
|
123
|
+
end
|
124
|
+
else
|
125
|
+
@callstack
|
126
|
+
end
|
95
127
|
end
|
96
128
|
|
97
129
|
def context
|
98
130
|
callstack.last || raise('no context!')
|
99
131
|
end
|
100
132
|
|
101
|
-
def
|
102
|
-
|
133
|
+
def context?
|
134
|
+
!!callstack.last
|
103
135
|
end
|
104
136
|
|
105
|
-
def
|
106
|
-
|
137
|
+
def catching(label = :result, &block)
|
138
|
+
@catching ||= []
|
139
|
+
|
140
|
+
if block
|
141
|
+
begin
|
142
|
+
@catching.push(label)
|
143
|
+
catch(label, &block)
|
144
|
+
ensure
|
145
|
+
@catching.pop
|
146
|
+
end
|
147
|
+
else
|
148
|
+
@catching.last
|
149
|
+
end
|
107
150
|
end
|
108
151
|
|
109
|
-
def
|
110
|
-
result
|
152
|
+
def return!(*value)
|
153
|
+
throw(:result, *value)
|
111
154
|
end
|
112
155
|
|
113
|
-
def
|
114
|
-
|
156
|
+
def catching_results(&block)
|
157
|
+
catching(:result, &block)
|
115
158
|
end
|
116
159
|
|
117
|
-
def
|
118
|
-
|
160
|
+
def catching?
|
161
|
+
catching
|
119
162
|
end
|
120
163
|
|
121
|
-
def
|
122
|
-
|
123
|
-
if hash.empty?
|
124
|
-
value = args.pop
|
125
|
-
key = args
|
126
|
-
hash = {key => value}
|
127
|
-
end
|
128
|
-
data.apply(hash)
|
164
|
+
def catching_results?
|
165
|
+
catching == :result
|
129
166
|
end
|
130
167
|
|
131
|
-
|
132
|
-
|
168
|
+
# delgate some methods to the context
|
169
|
+
#
|
170
|
+
Context.attrs.each do |method|
|
171
|
+
module_eval <<-__, __FILE__, __LINE__
|
172
|
+
def #{ method }(*args)
|
173
|
+
context.send(#{ method.inspect }, *args)
|
174
|
+
end
|
175
|
+
__
|
133
176
|
end
|
134
177
|
|
135
|
-
def status
|
136
|
-
status(*args
|
178
|
+
def status(*args)
|
179
|
+
context.status.update(*args) unless args.empty?
|
180
|
+
context.status
|
181
|
+
end
|
182
|
+
def status!(*args)
|
183
|
+
status.update(*args)
|
137
184
|
return!
|
138
185
|
end
|
139
186
|
|
140
187
|
def data(*args)
|
141
|
-
|
142
|
-
|
143
|
-
else
|
144
|
-
result.data.replace(*args)
|
145
|
-
end
|
188
|
+
context.data.replace(*args) unless args.empty?
|
189
|
+
context.data
|
146
190
|
end
|
147
|
-
|
148
191
|
def data!(*args)
|
149
|
-
|
192
|
+
data(*args)
|
150
193
|
valid!
|
151
194
|
end
|
152
195
|
|
153
|
-
def
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
def replace(*args, &block)
|
158
|
-
data.replace(*args, &block)
|
159
|
-
end
|
160
|
-
|
161
|
-
def validations
|
162
|
-
result.validations
|
196
|
+
def params!(*args)
|
197
|
+
params.replace(*args)
|
198
|
+
valid!
|
163
199
|
end
|
164
200
|
|
165
|
-
def
|
166
|
-
result.
|
201
|
+
def error!
|
202
|
+
result.error!
|
167
203
|
end
|
168
204
|
|
169
|
-
|
170
|
-
|
205
|
+
# delegate some methods to the params
|
206
|
+
#
|
207
|
+
Validations::Mixin.list.each do |method|
|
208
|
+
module_eval <<-__, __FILE__, __LINE__
|
209
|
+
def #{ method }(*args)
|
210
|
+
params.send(#{ method.inspect }, *args)
|
211
|
+
end
|
212
|
+
__
|
171
213
|
end
|
172
214
|
|
173
|
-
|
174
|
-
|
215
|
+
# misc
|
216
|
+
#
|
217
|
+
def index
|
218
|
+
self.class.index
|
175
219
|
end
|
176
220
|
|
177
|
-
def
|
178
|
-
|
221
|
+
def interfaces
|
222
|
+
self.class.interfaces
|
179
223
|
end
|
180
224
|
|
181
|
-
def
|
182
|
-
|
225
|
+
def respond_to?(*args)
|
226
|
+
super(*args) || super(Path.absolute_path_for(*args))
|
183
227
|
end
|
184
228
|
|
185
|
-
|
186
|
-
|
187
|
-
def
|
188
|
-
|
189
|
-
end
|
229
|
+
# immediate parameter parsing support
|
230
|
+
#
|
231
|
+
def parameter(*args, &block)
|
232
|
+
options = Map.options_for!(args)
|
190
233
|
|
191
|
-
=
|
192
|
-
def set(*args, &block)
|
193
|
-
result.data.set(*args, &block)
|
194
|
-
end
|
234
|
+
keys = args + Array(options[:keys]) + Array(options[:or])
|
195
235
|
|
196
|
-
|
197
|
-
params.data.get(*args, &block)
|
198
|
-
end
|
199
|
-
=end
|
236
|
+
raise(ArgumentError, 'no keys') if keys.empty?
|
200
237
|
|
201
|
-
|
202
|
-
|
238
|
+
blank = Object.new.freeze
|
239
|
+
value = blank
|
203
240
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
ensure
|
209
|
-
@callstack.pop
|
241
|
+
keys.each do |key|
|
242
|
+
if params.has?(key)
|
243
|
+
value = params.get(key)
|
244
|
+
break unless value.to_s.strip.empty?
|
210
245
|
end
|
211
|
-
else
|
212
|
-
@callstack
|
213
246
|
end
|
214
|
-
end
|
215
247
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
248
|
+
if value == blank
|
249
|
+
message =
|
250
|
+
case options[:error]
|
251
|
+
when nil, false
|
252
|
+
nil
|
253
|
+
when true
|
254
|
+
which = keys.map{|key| Array(key).join('.')}.join(' or ')
|
255
|
+
"#{ which } (parameter is blank)"
|
256
|
+
else
|
257
|
+
message = options[:error].to_s
|
258
|
+
end
|
259
|
+
errors.add(message) if message
|
260
|
+
|
261
|
+
status(options[:status]) if options[:status]
|
262
|
+
return! if options[:return!]
|
228
263
|
end
|
229
|
-
end
|
230
264
|
|
231
|
-
|
232
|
-
catching(:result, &block)
|
233
|
-
end
|
234
|
-
|
235
|
-
def catching?
|
236
|
-
catching
|
265
|
+
value == blank ? nil : value
|
237
266
|
end
|
267
|
+
alias_method('param', 'parameter')
|
238
268
|
|
239
|
-
def
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
269
|
+
def parameter!(*args, &block)
|
270
|
+
options = args.last.is_a?(Hash) ? Map.for(args.pop) : Map.new
|
271
|
+
args.push(options)
|
272
|
+
options[:error] = true unless options.has_key?(:error)
|
273
|
+
options[:return!] = true unless options.has_key?(:return!)
|
274
|
+
options[:status] = 412 unless options.has_key?(:status)
|
275
|
+
parameter(*args, &block)
|
245
276
|
end
|
277
|
+
alias_method('param!', 'parameter!')
|
246
278
|
end
|
247
279
|
end
|