dao 3.3.0 → 4.2.1

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 (60) hide show
  1. data/README +7 -0
  2. data/Rakefile +36 -17
  3. data/b.rb +38 -0
  4. data/dao.gemspec +41 -13
  5. data/lib/dao.rb +44 -13
  6. data/lib/dao/api.rb +1 -1
  7. data/lib/dao/api/context.rb +35 -45
  8. data/lib/dao/api/endpoints.rb +225 -91
  9. data/lib/dao/conducer.rb +437 -0
  10. data/lib/dao/conducer/attributes.rb +21 -0
  11. data/lib/dao/conducer/crud.rb +70 -0
  12. data/lib/dao/current.rb +66 -0
  13. data/lib/dao/db.rb +44 -5
  14. data/lib/dao/endpoint.rb +13 -1
  15. data/lib/dao/errors.rb +74 -59
  16. data/lib/dao/exceptions.rb +1 -2
  17. data/lib/dao/extractor.rb +68 -0
  18. data/lib/dao/form.rb +139 -46
  19. data/lib/dao/image_cache.rb +193 -0
  20. data/lib/dao/instance_exec.rb +1 -1
  21. data/lib/dao/name.rb +7 -0
  22. data/lib/dao/params.rb +16 -66
  23. data/lib/dao/rack.rb +3 -0
  24. data/lib/dao/rack/middleware.rb +5 -0
  25. data/lib/dao/rack/middleware/params_parser.rb +24 -0
  26. data/lib/dao/rails.rb +22 -5
  27. data/lib/dao/rails/lib/generators/dao/USAGE +2 -6
  28. data/lib/dao/rails/lib/generators/dao/dao_generator.rb +52 -7
  29. data/lib/dao/rails/lib/generators/dao/templates/api.rb +23 -7
  30. data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +24 -7
  31. data/lib/dao/rails/lib/generators/dao/templates/conducer.rb +64 -0
  32. data/lib/dao/rails/lib/generators/dao/templates/conducer_controller.rb +79 -0
  33. data/lib/dao/rails/lib/generators/dao/templates/dao.js +13 -6
  34. data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +75 -11
  35. data/lib/dao/result.rb +1 -26
  36. data/lib/dao/slug.rb +37 -8
  37. data/lib/dao/status.rb +4 -0
  38. data/lib/dao/support.rb +155 -0
  39. data/lib/dao/validations.rb +48 -157
  40. data/lib/dao/validations/callback.rb +30 -0
  41. data/lib/dao/validations/common.rb +322 -320
  42. data/lib/dao/validations/validator.rb +219 -0
  43. data/test/active_model_conducer_lint_test.rb +19 -0
  44. data/test/api_test.rb +261 -0
  45. data/test/conducer_test.rb +205 -0
  46. data/test/db.yml +9 -0
  47. data/test/form_test.rb +42 -0
  48. data/test/support_test.rb +52 -0
  49. data/test/testing.rb +145 -24
  50. data/test/validations_test.rb +156 -0
  51. metadata +138 -21
  52. data/TODO +0 -33
  53. data/a.rb +0 -80
  54. data/db/dao.yml +0 -5
  55. data/lib/dao/api/interfaces.rb +0 -306
  56. data/lib/dao/interface.rb +0 -28
  57. data/lib/dao/presenter.rb +0 -129
  58. data/lib/dao/rails/lib/generators/dao/api_generator.rb +0 -3
  59. data/lib/dao/validations/base.rb +0 -68
  60. data/test/dao_test.rb +0 -506
@@ -1,146 +1,174 @@
1
1
  module Dao
2
2
  class Api
3
+ # class methods
4
+ #
3
5
  class << Api
4
- def endpoints
5
- @endpoints ||= Map.new
6
+ def unload!
7
+ state.clear
6
8
  end
7
9
 
8
- def endpoint(path, &block)
9
- api = self
10
- path = Path.new(path)
10
+ def state
11
+ @state ||= {
12
+ :endpoints => Map.new,
13
+ :blocks => {},
14
+ :README => [],
15
+ :docs => []
16
+ }
17
+ end
11
18
 
12
- method =
13
- module_eval{
14
- define_method(path + '/endpoint', &block)
15
- instance_method(path + '/endpoint')
16
- }
19
+ def call(*args, &block)
20
+ options = Dao.options_for!(args)
21
+ path = Path.new(args.shift || raise(ArgumentError, "no path!"))
17
22
 
23
+ api = self
18
24
 
19
- endpoint = Endpoint.new(
20
- 'api' => api,
21
- 'path' => path,
22
- 'method' => method,
23
- 'doc' => docs.pop
24
- )
25
+ route = routes.add(path) if Route.like?(path)
26
+
27
+ doc = args.shift || options[:doc]
28
+ self.doc(doc) if doc
29
+
30
+ endpoint =
31
+ if options.key?(:alias)
32
+ aliased_path = Path.new(options[:alias])
33
+ endpoints[aliased_path] || raise(ArgumentError, "no such path #{ aliased_path }!")
34
+ else
35
+ Endpoint.new({
36
+ 'api' => api,
37
+ 'path' => path,
38
+ 'route' => route,
39
+ 'block' => block,
40
+ 'doc' => docs.pop
41
+ })
42
+ end
25
43
 
26
44
  endpoints[path] = endpoint
27
45
  end
46
+ alias_method('endpoint', 'call')
28
47
 
29
- def description(string)
30
- doc(:description => Dao.unindent(string))
48
+ def endpoints
49
+ state[:endpoints]
31
50
  end
32
- alias_method('desc', 'description')
33
51
 
34
52
  def doc(*args)
35
- docs.push(Map[:description, nil]) if docs.empty?
53
+ docs.push(Map(:doc => nil)) if docs.empty?
36
54
  doc = docs.last
55
+
37
56
  options = Dao.options_for!(args)
38
- if options.empty?
39
- options[:description] = args.join(' ')
40
- end
57
+ options[:doc] = lines_for(*args) if options.empty?
58
+
41
59
  doc.update(options)
42
60
  doc
43
61
  end
44
62
 
63
+ def description(*args)
64
+ doc(:doc => lines_for(*args))
65
+ end
66
+
67
+ alias_method('desc', 'description')
68
+
45
69
  def docs
46
- @docs ||= []
70
+ state[:docs]
71
+ end
72
+
73
+ def readme(*args)
74
+ if args.empty?
75
+ state[:README]
76
+ else
77
+ state[:README] = lines_for(args)
78
+ end
79
+ end
80
+ alias_method('README', 'readme')
81
+
82
+ def lines_for(*args)
83
+ Dao.unindent(args.flatten.compact.join("\n")).split(/\n/)
84
+ end
85
+
86
+ def readme=(readme)
87
+ self.readme = readme.to_s
47
88
  end
48
89
 
49
90
  def index
50
91
  index = Map.new
92
+ index[:README] = readme
51
93
  endpoints.each do |path, endpoint|
52
- index[path] = endpoint.doc || {'description' => path}
94
+ index[path] = endpoint.doc || {'description' => ''}
53
95
  end
54
96
  index
55
97
  end
56
98
  end
57
99
 
58
- def call(path = '/index', params = {})
59
- api = self
60
- path = Path.new(path)
61
- endpoint = endpoints[path]
62
- raise(NameError, path) unless endpoint
100
+ # instance methods
101
+ #
63
102
 
64
- params = parse_params(params, path)
65
103
 
66
- context = Context.new(
67
- :api => api,
68
- :endpoint => endpoint,
69
- :params => params
70
- )
104
+ # call support
105
+ #
106
+ def call(path = '/index', params = {}, options = {})
107
+ api = self
108
+ path = Path.new(path)
109
+ endpoint = endpoints[path] ### endpoints.by_path(path)
110
+ route = nil
71
111
 
72
- callstack(context) do
73
- catching(:result){ context.call() }
112
+ unless endpoint
113
+ route = route_for(path)
114
+ endpoint = endpoints[route]
74
115
  end
75
116
 
76
- context.result
77
- end
78
-
79
- def index
80
- self.class.index
81
- end
117
+ unless endpoint
118
+ return index if path == '/index'
119
+ raise(NameError, "NO SUCH INTERFACE: #{ path }")
120
+ end
82
121
 
83
- def parse_params(params, path)
84
- return params if params.is_a?(Params)
85
- re = %r/^#{ Regexp.escape(path) }/
86
- params.each do |key, val|
87
- return Params.parse(path, params) if key =~ re
122
+ if route
123
+ params.update(route.params_for(path))
124
+ path = route.path_for(params)
125
+ else
126
+ if Route.like?(path)
127
+ route = Route.new(path)
128
+ path = route.path_for(params)
129
+ else
130
+ route = path
131
+ end
88
132
  end
89
- return params
90
- end
91
133
 
92
- def endpoints
93
- self.class.endpoints
94
- end
134
+
135
+ ##
136
+ #
137
+ context = Context.for(api, path, route, endpoint, params, options)
95
138
 
96
- def context
97
- callstack.last
98
- end
139
+ callstack(context) do
140
+ catching(:result) do
141
+ context.call()
142
+ end
143
+ end
99
144
 
100
- def result
101
145
  context.result
102
146
  end
103
147
 
104
- def status(*args, &block)
105
- result.status(*args, &block)
106
- end
107
-
108
- def data
109
- result.data
110
- end
111
-
112
- def errors
113
- result.errors
114
- end
115
-
116
- def params
117
- result.params
118
- end
119
-
120
- def validations
121
- result.validations
122
- end
123
-
124
- def validates(*args, &block)
125
- result.validates(*args, &block)
126
- end
148
+ # will an endpoint route to a endpoint?
149
+ #
150
+ def route?(path)
151
+ path = Path.new(path)
152
+ endpoint = endpoints[path]
153
+ route = nil
127
154
 
128
- def validate
129
- result.validate
130
- end
155
+ unless endpoint
156
+ route = route_for(path)
157
+ endpoint = endpoints[route]
158
+ end
131
159
 
132
- def valid?
133
- result.valid?
160
+ endpoint
134
161
  end
135
162
 
136
- def validate!
137
- result.validate!
138
- end
139
163
 
140
- def valid!
141
- result.valid!
164
+ # lookup a route
165
+ #
166
+ def route_for(*args)
167
+ self.class.routes.match(*args)
142
168
  end
143
169
 
170
+ # context stack support
171
+ #
144
172
  def callstack(context = nil, &block)
145
173
  @callstack ||= []
146
174
 
@@ -156,6 +184,14 @@ module Dao
156
184
  end
157
185
  end
158
186
 
187
+ def context
188
+ callstack.last || raise('no context!')
189
+ end
190
+
191
+ def context?
192
+ !!callstack.last
193
+ end
194
+
159
195
  def catching(label = :result, &block)
160
196
  @catching ||= []
161
197
 
@@ -163,6 +199,8 @@ module Dao
163
199
  begin
164
200
  @catching.push(label)
165
201
  catch(label, &block)
202
+ rescue Dao::Validations::Error
203
+ nil
166
204
  ensure
167
205
  @catching.pop
168
206
  end
@@ -171,6 +209,10 @@ module Dao
171
209
  end
172
210
  end
173
211
 
212
+ def return!(*value)
213
+ throw(:result, *value)
214
+ end
215
+
174
216
  def catching_results(&block)
175
217
  catching(:result, &block)
176
218
  end
@@ -183,8 +225,100 @@ module Dao
183
225
  catching == :result
184
226
  end
185
227
 
228
+ # validations
229
+ #
230
+ include Validations
231
+
232
+ # delgate some methods to the context
233
+ #
234
+ Context.attrs.each do |method|
235
+ module_eval <<-__, __FILE__, __LINE__
236
+ def #{ method }(*args)
237
+ context.send(#{ method.inspect }, *args)
238
+ end
239
+ __
240
+ end
241
+
242
+ def status(*args)
243
+ context.status.update(*args) unless args.empty?
244
+ context.status
245
+ end
246
+ def status!(*args)
247
+ status.update(*args)
248
+ return!
249
+ end
250
+
251
+ def data(*args)
252
+ context.data.replace(*args) unless args.empty?
253
+ context.data
254
+ end
255
+ def data!(*args)
256
+ data(*args)
257
+ return!
258
+ end
259
+
260
+ # misc
261
+ #
262
+ def index
263
+ self.class.index
264
+ end
265
+
266
+ def endpoints
267
+ self.class.endpoints
268
+ end
269
+
186
270
  def respond_to?(*args)
187
271
  super(*args) || super(Path.absolute_path_for(*args))
188
272
  end
273
+
274
+ # immediate parameter parsing support
275
+ #
276
+ def parameter(*args, &block)
277
+ options = Map.options_for!(args)
278
+
279
+ keys = args + Array(options[:keys]) + Array(options[:or])
280
+
281
+ raise(ArgumentError, 'no keys') if keys.empty?
282
+
283
+ blank = Object.new.freeze
284
+ value = blank
285
+
286
+ keys.each do |key|
287
+ if params.has?(key)
288
+ value = params.get(key)
289
+ break unless value.to_s.strip.empty?
290
+ end
291
+ end
292
+
293
+ if value == blank
294
+ message =
295
+ case options[:error]
296
+ when nil, false
297
+ nil
298
+ when true
299
+ which = keys.map{|key| Array(key).join('.')}.join(' or ')
300
+ "#{ which } (parameter is blank)"
301
+ else
302
+ message = options[:error].to_s
303
+ end
304
+ errors.add(message) if message
305
+
306
+ status(options[:status]) if options[:status]
307
+ return! if options[:return!]
308
+ end
309
+
310
+ value == blank ? nil : value
311
+ end
312
+ alias_method('param', 'parameter')
313
+
314
+ def parameter!(*args, &block)
315
+ options = args.last.is_a?(Hash) ? Map.for(args.pop) : Map.new
316
+ args.push(options)
317
+ options[:error] = true unless options.has_key?(:error)
318
+ options[:return!] = true unless options.has_key?(:return!)
319
+ options[:status] = 412 unless options.has_key?(:status)
320
+ parameter(*args, &block)
321
+ end
322
+ alias_method('param!', 'parameter!')
189
323
  end
190
324
  end
@@ -0,0 +1,437 @@
1
+ module Dao
2
+
3
+ class Conducer
4
+ ##
5
+ #
6
+ include ActiveModel::Naming
7
+ #include ActiveModel::Conversion
8
+
9
+ #extend ActiveModel::Translation
10
+ #include ActiveModel::AttributeMethods
11
+ #include ActiveModel::Serialization
12
+ #include ActiveModel::Dirty
13
+ #include ActiveModel::MassAssignmentSecurity
14
+ #include ActiveModel::Observing
15
+ #include ActiveModel::Serializers::JSON
16
+ #include ActiveModel::Serializers::Xml
17
+
18
+ #include ActiveModel::Validations
19
+
20
+ extend ActiveModel::Callbacks
21
+
22
+ define_model_callbacks(:save, :create, :update, :destroy)
23
+ define_model_callbacks(:reset, :initialize, :find, :touch)
24
+ include ActiveModel::Validations::Callbacks
25
+
26
+ ##
27
+ #
28
+ include Dao::Validations
29
+ include Dao::Current
30
+
31
+ ## class_methods
32
+ #
33
+ class << Conducer
34
+ def name(*args)
35
+ return send('name=', args.first) unless args.empty?
36
+ @name ||= super
37
+ end
38
+
39
+ def name=(name)
40
+ @name = name.to_s
41
+ end
42
+
43
+ def model_name(*args)
44
+ return send('model_name=', args.first) unless args.empty?
45
+ @model_name ||= default_model_name
46
+ end
47
+
48
+ def model_name=(model_name)
49
+ @model_name = model_name_for(model_name)
50
+ end
51
+
52
+ def model_name_for(model_name)
53
+ ActiveModel::Name.new(Map[:name, model_name])
54
+ end
55
+
56
+ def default_model_name
57
+ model_name_for(name.to_s.sub(/Conducer$/, ''))
58
+ end
59
+
60
+ def table_name
61
+ @table_name ||= model_name.plural.to_s
62
+ end
63
+ alias_method('collection_name', 'table_name')
64
+
65
+ def table_name=(table_name)
66
+ @table_name = table_name.to_s
67
+ end
68
+ alias_method('collection_name=', 'table_name=')
69
+
70
+ def controller
71
+ defined?(@controller) ? @controller : Dao.current_controller
72
+ end
73
+
74
+ def controller=(controller)
75
+ @controller = controller
76
+ end
77
+
78
+ def mock_controller(*args, &block)
79
+ Dao.mock_controller(*args, &block)
80
+ end
81
+
82
+ def raise!(*args, &block)
83
+ kind = (args.first.is_a?(Symbol) ? args.shift : 'error').to_s.sub(/_error$/, '')
84
+
85
+ case kind
86
+ when /validation/
87
+ raise Validations::Error.new(*args, &block)
88
+ when /error/
89
+ raise Error.new(*args, &block)
90
+ else
91
+ raise Error.new(*args, &block)
92
+ end
93
+ end
94
+ end
95
+
96
+ ## contructor
97
+ #
98
+ %w(
99
+ name
100
+ attributes
101
+ errors
102
+ form
103
+ ).each{|a| fattr(a)}
104
+
105
+ def self.new(*args, &block)
106
+ allocate.tap do |conducer|
107
+ conducer.running_callbacks(:reset, :initialize) do
108
+ conducer.send(:reset, *args, &block)
109
+ conducer.send(:initialize, *args, &block)
110
+ end
111
+ end
112
+ end
113
+
114
+ def running_callbacks(*args, &block)
115
+ which = args.shift
116
+ if args.empty?
117
+ run_callbacks(which, &block)
118
+ else
119
+ run_callbacks(which){ running_callbacks(*args, &block) }
120
+ end
121
+ end
122
+
123
+ def reset(*args, &block)
124
+ controllers, args = args.partition{|arg| arg.is_a?(ActionController::Base)}
125
+ hashes, args = args.partition{|arg| arg.is_a?(Hash)}
126
+
127
+ @name = self.class.model_name.singular
128
+ @attributes = Attributes.for(self)
129
+ @form = Form.for(self)
130
+
131
+ validator.reset
132
+
133
+ set_controller(controllers.shift || Dao.current_controller || Dao.mock_controller)
134
+
135
+ hashes.each do |hash|
136
+ hash.each do |key, val|
137
+ @attributes.set(key_for(key) => val)
138
+ end
139
+ end
140
+
141
+ self
142
+ end
143
+
144
+ def errors
145
+ validator.errors
146
+ end
147
+
148
+ def status
149
+ validator.status
150
+ end
151
+
152
+ def initialize(*args, &block)
153
+ end
154
+
155
+ ## instance_methods
156
+ #
157
+ def inspect
158
+ ::JSON.pretty_generate(@attributes, :max_nesting => 0)
159
+ end
160
+
161
+ def id
162
+ @attributes[:id] || @attributes[:_id]
163
+ end
164
+
165
+ def id=(id)
166
+ @attributes[:id] = id
167
+ end
168
+
169
+ def key_for(*keys)
170
+ keys.flatten.map{|key| key =~ %r/^\d+$/ ? Integer(key) : key}
171
+ #key = keys.flatten.join('.').strip
172
+ #key.split(%r/\s*[,.]\s*/).map{|key| key =~ %r/^\d+$/ ? Integer(key) : key}
173
+ end
174
+
175
+ def [](key)
176
+ @attributes.get(key_for(key))
177
+ end
178
+
179
+ def []=(key, val)
180
+ @attributes.set(key_for(key), val)
181
+ end
182
+
183
+ %w( set get has? update ).each do |m|
184
+ module_eval <<-__, __FILE__, __LINE__
185
+ def #{ m }(*a, &b)
186
+ @attributes.#{ m }(*a, &b)
187
+ end
188
+ __
189
+ end
190
+
191
+ def method_missing(method, *args, &block)
192
+ case method.to_s
193
+ when /^(.*)[=]$/
194
+ key = key_for($1)
195
+ val = args.first
196
+ @attributes.set(key => val)
197
+
198
+ when /^(.*)[!]$/
199
+ key = key_for($1)
200
+ val = true
201
+ @attributes.set(key => val)
202
+
203
+ when /^(.*)[?]$/
204
+ key = key_for($1)
205
+ @attributes.has?(key)
206
+
207
+ else
208
+ key = key_for(method)
209
+ return @attributes.get(key) if @attributes.has?(key)
210
+ super
211
+ end
212
+ end
213
+
214
+ def inspect
215
+ "#{ self.class.name }(#{ @attributes.inspect.chomp })"
216
+ end
217
+
218
+ # active_model support
219
+ #
220
+
221
+
222
+ ## include ActiveModel::Conversion
223
+ #
224
+ def to_model
225
+ self
226
+ end
227
+
228
+ def to_key
229
+ id ? [id] : nil
230
+ end
231
+
232
+ def to_param
233
+ persisted? ? to_key.join('-') : nil
234
+ end
235
+
236
+ def persisted
237
+ !!(defined?(@persisted) ? @persisted : id)
238
+ end
239
+ def persisted?
240
+ persisted
241
+ end
242
+ def persisted=(value)
243
+ @persisted = !!value
244
+ end
245
+ def persisted!
246
+ self.persisted = true
247
+ end
248
+
249
+ def new_record
250
+ !!(defined?(@new_record) ? @new_record : id.blank?)
251
+ end
252
+ def new_record?
253
+ new_record
254
+ end
255
+ def new_record=(value)
256
+ @new_record = !!value
257
+ end
258
+ def new_record!
259
+ self.new_record = true
260
+ end
261
+
262
+ def destroyed
263
+ !!(defined?(@destroyed) ? @destroyed : id.blank?)
264
+ end
265
+ def destroyed?
266
+ destroyed
267
+ end
268
+ def destroyed=(value)
269
+ @destroyed = !!value
270
+ end
271
+ def destroyed!
272
+ self.destroyed = true
273
+ end
274
+
275
+
276
+ ## extend ActiveModel::Translation
277
+ #
278
+ def self.human_attribute_name(attribute, options = {})
279
+ attribute
280
+ end
281
+
282
+ def self.lookup_ancestors
283
+ [self]
284
+ end
285
+
286
+ def read_attribute_for_validation(key)
287
+ self[key]
288
+ end
289
+
290
+ ## view support
291
+ #
292
+ url_helpers = Rails.application.try(:routes).try(:url_helpers)
293
+ include(url_helpers) if url_helpers
294
+ include(ActionView::Helpers) if defined?(ActionView::Helpers)
295
+
296
+ def controller
297
+ @controller ||= (Dao.current_controller || Dao.mock_controller)
298
+ @controller
299
+ end
300
+
301
+ def controller=(controller)
302
+ @controller = controller
303
+ ensure
304
+ default_url_options[:protocol] = @controller.request.protocol
305
+ default_url_options[:host] = @controller.request.host
306
+ default_url_options[:port] = @controller.request.port
307
+ end
308
+
309
+ def set_controller(controller)
310
+ self.controller = controller
311
+ end
312
+
313
+ controller_delegates = %w(
314
+ render
315
+ render_to_string
316
+ )
317
+
318
+ controller_delegates.each do |method|
319
+ module_eval <<-__, __FILE__, __LINE__
320
+ def #{ method }(*args, &block)
321
+ controller.#{ method }(*args, &block)
322
+ end
323
+ __
324
+ end
325
+
326
+ ## generic crud support assuming valid .all, .find, #save and #destroy
327
+ #
328
+ =begin
329
+ def self.create(*args, &block)
330
+ allocate.tap do |conducer|
331
+ conducer.running_callbacks :reset, :initialize, :create do
332
+ conducer.send(:reset, *args, &block)
333
+ conducer.send(:initialize, *args, &block)
334
+ return false unless conducer.save
335
+ end
336
+ end
337
+ end
338
+
339
+ def self.create!(*args, &block)
340
+ allocate.tap do |conducer|
341
+ conducer.running_callbacks :reset, :initialize, :create do
342
+ conducer.send(:reset, *args, &block)
343
+ conducer.send(:initialize, *args, &block)
344
+ raise!(:validation_error) unless conducer.save
345
+ end
346
+ end
347
+ end
348
+
349
+ def self.blank(params = {})
350
+ new
351
+ end
352
+
353
+ def self.build(params = {})
354
+ new
355
+ end
356
+
357
+ def self.show(id)
358
+ find(id)
359
+ end
360
+
361
+ def self.index(params = {})
362
+ all(params)
363
+ end
364
+
365
+ def self.edit(id)
366
+ find(id)
367
+ end
368
+
369
+ def self.update(id)
370
+ find(id)
371
+ end
372
+
373
+ def self.destroy(id)
374
+ find(id)
375
+ end
376
+ =end
377
+
378
+ ##
379
+ #
380
+
381
+ ##
382
+ #
383
+ def reload
384
+ attributes =
385
+ if id
386
+ conducer = self.class.find(id)
387
+ conducer ? conducer.attributes : {}
388
+ else
389
+ {}
390
+ end
391
+ reset(attributes)
392
+ self
393
+ end
394
+
395
+ def save!
396
+ saved = !!save
397
+ raise!(:validation_error) unless saved
398
+ true
399
+ end
400
+
401
+ def update_attributes(attributes = {})
402
+ @attributes.set(attributes)
403
+ @attributes
404
+ end
405
+
406
+ def update_attributes!(*args, &block)
407
+ update_attributes(*args, &block)
408
+ ensure
409
+ save!
410
+ end
411
+
412
+ ## misc
413
+ #
414
+ def model_name
415
+ self.class.model_name
416
+ end
417
+
418
+ def form
419
+ @form
420
+ end
421
+
422
+ def raise!(*args, &block)
423
+ self.class.raise!(*args, &block)
424
+ end
425
+
426
+ def as_json
427
+ @attributes
428
+ end
429
+
430
+ def conducer
431
+ self
432
+ end
433
+ end
434
+
435
+ Dao.load('conducer/attributes.rb')
436
+ Dao.load('conducer/crud.rb')
437
+ end