dao 3.3.0 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
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