dao 2.2.3 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/Rakefile +68 -113
  2. data/TODO +3 -9
  3. data/a.rb +25 -0
  4. data/dao.gemspec +70 -14
  5. data/lib/dao.rb +8 -8
  6. data/lib/dao/api.rb +1 -0
  7. data/lib/dao/api/context.rb +48 -23
  8. data/lib/dao/api/dsl.rb +1 -1
  9. data/lib/dao/api/interfaces.rb +149 -117
  10. data/lib/dao/api/modes.rb +24 -23
  11. data/lib/dao/api/routes.rb +9 -0
  12. data/lib/dao/data.rb +9 -2
  13. data/lib/dao/errors.rb +15 -11
  14. data/lib/dao/form.rb +46 -37
  15. data/lib/dao/interface.rb +1 -1
  16. data/lib/dao/mode.rb +45 -20
  17. data/lib/dao/params.rb +35 -53
  18. data/lib/dao/path.rb +6 -9
  19. data/lib/dao/rails/lib/generators/dao/templates/api.rb +1 -1
  20. data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +16 -0
  21. data/lib/dao/result.rb +26 -133
  22. data/lib/dao/route.rb +87 -0
  23. data/lib/dao/status.rb +96 -94
  24. data/lib/dao/support.rb +5 -3
  25. data/lib/dao/validations.rb +46 -442
  26. data/lib/dao/validations/base.rb +68 -0
  27. data/lib/dao/validations/common.rb +463 -0
  28. data/test/dao_test.rb +214 -33
  29. metadata +20 -112
  30. data/lib/dao/rails/app/api.rb +0 -55
  31. data/lib/dao/rails/app/controllers/api_controller.rb +0 -99
  32. data/sample/rails_app/Gemfile +0 -33
  33. data/sample/rails_app/Gemfile.lock +0 -88
  34. data/sample/rails_app/README +0 -1
  35. data/sample/rails_app/Rakefile +0 -7
  36. data/sample/rails_app/app/api.rb +0 -55
  37. data/sample/rails_app/app/controllers/api_controller.rb +0 -99
  38. data/sample/rails_app/app/controllers/application_controller.rb +0 -3
  39. data/sample/rails_app/app/helpers/application_helper.rb +0 -2
  40. data/sample/rails_app/app/views/layouts/application.html.erb +0 -14
  41. data/sample/rails_app/config.ru +0 -4
  42. data/sample/rails_app/config/application.rb +0 -51
  43. data/sample/rails_app/config/boot.rb +0 -13
  44. data/sample/rails_app/config/database.yml +0 -22
  45. data/sample/rails_app/config/environment.rb +0 -5
  46. data/sample/rails_app/config/environments/development.rb +0 -26
  47. data/sample/rails_app/config/environments/production.rb +0 -49
  48. data/sample/rails_app/config/environments/test.rb +0 -35
  49. data/sample/rails_app/config/initializers/backtrace_silencers.rb +0 -7
  50. data/sample/rails_app/config/initializers/inflections.rb +0 -10
  51. data/sample/rails_app/config/initializers/mime_types.rb +0 -5
  52. data/sample/rails_app/config/initializers/secret_token.rb +0 -7
  53. data/sample/rails_app/config/initializers/session_store.rb +0 -8
  54. data/sample/rails_app/config/locales/en.yml +0 -5
  55. data/sample/rails_app/config/routes.rb +0 -62
  56. data/sample/rails_app/db/development.sqlite3 +0 -0
  57. data/sample/rails_app/db/seeds.rb +0 -7
  58. data/sample/rails_app/doc/README_FOR_APP +0 -2
  59. data/sample/rails_app/log/development.log +0 -27
  60. data/sample/rails_app/log/production.log +0 -0
  61. data/sample/rails_app/log/server.log +0 -0
  62. data/sample/rails_app/log/test.log +0 -0
  63. data/sample/rails_app/pubic/javascripts/dao.js +0 -148
  64. data/sample/rails_app/public/404.html +0 -26
  65. data/sample/rails_app/public/422.html +0 -26
  66. data/sample/rails_app/public/500.html +0 -26
  67. data/sample/rails_app/public/favicon.ico +0 -0
  68. data/sample/rails_app/public/images/rails.png +0 -0
  69. data/sample/rails_app/public/index.html +0 -239
  70. data/sample/rails_app/public/javascripts/application.js +0 -2
  71. data/sample/rails_app/public/javascripts/controls.js +0 -965
  72. data/sample/rails_app/public/javascripts/dragdrop.js +0 -974
  73. data/sample/rails_app/public/javascripts/effects.js +0 -1123
  74. data/sample/rails_app/public/javascripts/prototype.js +0 -6001
  75. data/sample/rails_app/public/javascripts/rails.js +0 -175
  76. data/sample/rails_app/public/robots.txt +0 -5
  77. data/sample/rails_app/script/rails +0 -6
  78. data/sample/rails_app/test/performance/browsing_test.rb +0 -9
  79. data/sample/rails_app/test/test_helper.rb +0 -13
@@ -2,6 +2,7 @@ module Dao
2
2
  class Api
3
3
  Dao.load 'api/initializers.rb'
4
4
  Dao.load 'api/modes.rb'
5
+ Dao.load 'api/routes.rb'
5
6
  Dao.load 'api/context.rb'
6
7
  Dao.load 'api/interfaces.rb'
7
8
  Dao.load 'api/dsl.rb'
@@ -1,38 +1,63 @@
1
1
  module Dao
2
2
  class Context
3
- Attrs = %w( api interface params result method args )
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
- def initialize(*args, &block)
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
- api = options[:api]
10
- interface = options[:interface]
11
- params = options[:params]
12
-
13
- params = Params.for(:api => api, :interface => interface, :params => 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
- self.api = api
21
- self.interface = interface
22
- self.params = params
23
- self.result = result
24
- self.method = method
25
- self.args = args
26
- end
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
- def call()
29
- method.call(*args)
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 update(options = {})
33
- options.each do |key, val|
34
- send("#{ key }=", val)
35
- end
59
+ def call
60
+ method.call(*args)
36
61
  end
37
62
  end
38
63
  end
@@ -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)
@@ -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' => path}
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
- options = Map.options(options || {})
71
-
72
- params = Dao.parse(path, params, options)
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.new(
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){ context.call() }
98
+ catching(:result) do
99
+ context.call()
100
+ end
82
101
  end
83
102
 
84
103
  context.result
85
104
  end
86
105
 
87
- def index
88
- result = Result.new('/index')
89
- result.data.update(self.class.index)
90
- result
106
+ # lookup a route
107
+ #
108
+ def route_for(*args)
109
+ self.class.routes.match(*args)
91
110
  end
92
111
 
93
- def interfaces
94
- self.class.interfaces
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 result
102
- context.result
133
+ def context?
134
+ !!callstack.last
103
135
  end
104
136
 
105
- def params
106
- result.params
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 errors
110
- result.errors
152
+ def return!(*value)
153
+ throw(:result, *value)
111
154
  end
112
155
 
113
- def apply(hash = {})
114
- data.apply(hash)
156
+ def catching_results(&block)
157
+ catching(:result, &block)
115
158
  end
116
159
 
117
- def update(hash = {})
118
- data.update(hash)
160
+ def catching?
161
+ catching
119
162
  end
120
163
 
121
- def default(*args)
122
- hash = Map.options_for!(args)
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
- def status(*args, &block)
132
- result.status(*args, &block)
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!(*args, &block)
136
- status(*args, &block)
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
- if args.empty?
142
- result.data
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
- result.data.replace(*args)
192
+ data(*args)
150
193
  valid!
151
194
  end
152
195
 
153
- def update(*args, &block)
154
- data.update(*args, &block)
155
- end
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 validates(*args, &block)
166
- result.validates(*args, &block)
201
+ def error!
202
+ result.error!
167
203
  end
168
204
 
169
- def validate
170
- result.validate
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
- def valid?
174
- result.valid?
215
+ # misc
216
+ #
217
+ def index
218
+ self.class.index
175
219
  end
176
220
 
177
- def validate!
178
- result.validate!
221
+ def interfaces
222
+ self.class.interfaces
179
223
  end
180
224
 
181
- def valid!
182
- result.valid!
225
+ def respond_to?(*args)
226
+ super(*args) || super(Path.absolute_path_for(*args))
183
227
  end
184
228
 
185
- include Validations::Common
186
-
187
- def return!(*value)
188
- throw(:result, *value)
189
- end
229
+ # immediate parameter parsing support
230
+ #
231
+ def parameter(*args, &block)
232
+ options = Map.options_for!(args)
190
233
 
191
- =begin
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
- def get(*args, &block)
197
- params.data.get(*args, &block)
198
- end
199
- =end
236
+ raise(ArgumentError, 'no keys') if keys.empty?
200
237
 
201
- def callstack(context = nil, &block)
202
- @callstack ||= []
238
+ blank = Object.new.freeze
239
+ value = blank
203
240
 
204
- if block and context
205
- begin
206
- @callstack.push(context)
207
- return block.call()
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
- def catching(label = :result, &block)
217
- @catching ||= []
218
-
219
- if block
220
- begin
221
- @catching.push(label)
222
- catch(label, &block)
223
- ensure
224
- @catching.pop
225
- end
226
- else
227
- @catching.last
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
- def catching_results(&block)
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 catching_results?
240
- catching == :result
241
- end
242
-
243
- def respond_to?(*args)
244
- super(*args) || super(Path.absolute_path_for(*args))
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