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.
- data/README +7 -0
- data/Rakefile +36 -17
- data/b.rb +38 -0
- data/dao.gemspec +41 -13
- data/lib/dao.rb +44 -13
- data/lib/dao/api.rb +1 -1
- data/lib/dao/api/context.rb +35 -45
- data/lib/dao/api/endpoints.rb +225 -91
- data/lib/dao/conducer.rb +437 -0
- data/lib/dao/conducer/attributes.rb +21 -0
- data/lib/dao/conducer/crud.rb +70 -0
- data/lib/dao/current.rb +66 -0
- data/lib/dao/db.rb +44 -5
- data/lib/dao/endpoint.rb +13 -1
- data/lib/dao/errors.rb +74 -59
- data/lib/dao/exceptions.rb +1 -2
- data/lib/dao/extractor.rb +68 -0
- data/lib/dao/form.rb +139 -46
- data/lib/dao/image_cache.rb +193 -0
- data/lib/dao/instance_exec.rb +1 -1
- data/lib/dao/name.rb +7 -0
- data/lib/dao/params.rb +16 -66
- data/lib/dao/rack.rb +3 -0
- data/lib/dao/rack/middleware.rb +5 -0
- data/lib/dao/rack/middleware/params_parser.rb +24 -0
- data/lib/dao/rails.rb +22 -5
- data/lib/dao/rails/lib/generators/dao/USAGE +2 -6
- data/lib/dao/rails/lib/generators/dao/dao_generator.rb +52 -7
- data/lib/dao/rails/lib/generators/dao/templates/api.rb +23 -7
- data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +24 -7
- data/lib/dao/rails/lib/generators/dao/templates/conducer.rb +64 -0
- data/lib/dao/rails/lib/generators/dao/templates/conducer_controller.rb +79 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao.js +13 -6
- data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +75 -11
- data/lib/dao/result.rb +1 -26
- data/lib/dao/slug.rb +37 -8
- data/lib/dao/status.rb +4 -0
- data/lib/dao/support.rb +155 -0
- data/lib/dao/validations.rb +48 -157
- data/lib/dao/validations/callback.rb +30 -0
- data/lib/dao/validations/common.rb +322 -320
- data/lib/dao/validations/validator.rb +219 -0
- data/test/active_model_conducer_lint_test.rb +19 -0
- data/test/api_test.rb +261 -0
- data/test/conducer_test.rb +205 -0
- data/test/db.yml +9 -0
- data/test/form_test.rb +42 -0
- data/test/support_test.rb +52 -0
- data/test/testing.rb +145 -24
- data/test/validations_test.rb +156 -0
- metadata +138 -21
- data/TODO +0 -33
- data/a.rb +0 -80
- data/db/dao.yml +0 -5
- data/lib/dao/api/interfaces.rb +0 -306
- data/lib/dao/interface.rb +0 -28
- data/lib/dao/presenter.rb +0 -129
- data/lib/dao/rails/lib/generators/dao/api_generator.rb +0 -3
- data/lib/dao/validations/base.rb +0 -68
- data/test/dao_test.rb +0 -506
data/lib/dao/api/endpoints.rb
CHANGED
@@ -1,146 +1,174 @@
|
|
1
1
|
module Dao
|
2
2
|
class Api
|
3
|
+
# class methods
|
4
|
+
#
|
3
5
|
class << Api
|
4
|
-
def
|
5
|
-
|
6
|
+
def unload!
|
7
|
+
state.clear
|
6
8
|
end
|
7
9
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
10
|
+
def state
|
11
|
+
@state ||= {
|
12
|
+
:endpoints => Map.new,
|
13
|
+
:blocks => {},
|
14
|
+
:README => [],
|
15
|
+
:docs => []
|
16
|
+
}
|
17
|
+
end
|
11
18
|
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
30
|
-
|
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
|
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
|
-
|
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
|
-
|
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' =>
|
94
|
+
index[path] = endpoint.doc || {'description' => ''}
|
53
95
|
end
|
54
96
|
index
|
55
97
|
end
|
56
98
|
end
|
57
99
|
|
58
|
-
|
59
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
73
|
-
|
112
|
+
unless endpoint
|
113
|
+
route = route_for(path)
|
114
|
+
endpoint = endpoints[route]
|
74
115
|
end
|
75
116
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
134
|
+
|
135
|
+
##
|
136
|
+
#
|
137
|
+
context = Context.for(api, path, route, endpoint, params, options)
|
95
138
|
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
129
|
-
|
130
|
-
|
155
|
+
unless endpoint
|
156
|
+
route = route_for(path)
|
157
|
+
endpoint = endpoints[route]
|
158
|
+
end
|
131
159
|
|
132
|
-
|
133
|
-
result.valid?
|
160
|
+
endpoint
|
134
161
|
end
|
135
162
|
|
136
|
-
def validate!
|
137
|
-
result.validate!
|
138
|
-
end
|
139
163
|
|
140
|
-
|
141
|
-
|
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
|
data/lib/dao/conducer.rb
ADDED
@@ -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
|