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.
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