dao 4.4.4 → 4.6.4
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 +2 -0
- data/dao.gemspec +9 -6
- data/lib/dao.rb +20 -11
- data/lib/dao/api/call.rb +1 -1
- data/lib/dao/conducer.rb +152 -106
- data/lib/dao/conducer/active_model.rb +8 -1
- data/lib/dao/conducer/collection.rb +4 -0
- data/lib/dao/conducer/controller_support.rb +8 -4
- data/lib/dao/conducer/nav_support.rb +2 -1
- data/lib/dao/conducer/view_support.rb +8 -4
- data/lib/dao/errors.rb +114 -48
- data/lib/dao/form.rb +113 -91
- data/lib/dao/rails.rb +4 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao.css +11 -21
- data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +17 -43
- data/lib/dao/route.rb +4 -3
- data/lib/dao/upload.rb +27 -5
- data/lib/dao/validations.rb +0 -1
- data/lib/dao/validations/validator.rb +5 -1
- data/test/conducer_test.rb +56 -9
- data/test/errors_test.rb +81 -0
- data/test/form_test.rb +123 -0
- data/test/helper.rb +11 -0
- data/test/support_test.rb +0 -1
- data/test/validations_test.rb +29 -8
- metadata +96 -28
@@ -35,7 +35,14 @@ module Dao
|
|
35
35
|
|
36
36
|
def default_model_name
|
37
37
|
return model_name_for('Conducer') if self == Dao::Conducer
|
38
|
-
|
38
|
+
|
39
|
+
suffixes = /(Conducer|Resource|Importer|Presenter|Conductor|Cell)\Z/o
|
40
|
+
|
41
|
+
name = self.name.to_s
|
42
|
+
name.sub!(suffixes, '') unless name.sub(suffixes, '').blank?
|
43
|
+
name.sub!(/(:|_)+$/, '')
|
44
|
+
|
45
|
+
model_name_for(name)
|
39
46
|
end
|
40
47
|
|
41
48
|
def collection_name
|
@@ -18,10 +18,14 @@ module Dao
|
|
18
18
|
def set_controller(controller)
|
19
19
|
@controller = controller
|
20
20
|
ensure
|
21
|
-
default_url_options
|
22
|
-
|
23
|
-
|
24
|
-
@action = Action.new(@controller.send(:action_name).to_s, self)
|
21
|
+
if defined?(default_url_options)
|
22
|
+
[:protocol, :host, :port].each{|attr| default_url_options[attr] = @controller.request.send(attr)}
|
23
|
+
end
|
24
|
+
@action = Action.new((@controller.send(:action_name) || :index).to_s, self)
|
25
|
+
end
|
26
|
+
|
27
|
+
def request
|
28
|
+
@controller.send(:request) if @controller
|
25
29
|
end
|
26
30
|
|
27
31
|
##
|
@@ -6,11 +6,15 @@ module Dao
|
|
6
6
|
|
7
7
|
class << Conducer
|
8
8
|
include Tagz.globally
|
9
|
-
end
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def install_routes!
|
11
|
+
url_helpers = Rails.application.try(:routes).try(:url_helpers)
|
12
|
+
include(url_helpers) if url_helpers
|
13
|
+
include(ActionView::Helpers) if defined?(ActionView::Helpers)
|
14
|
+
extend(url_helpers) if url_helpers
|
15
|
+
extend(ActionView::Helpers) if defined?(ActionView::Helpers)
|
16
|
+
end
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
data/lib/dao/errors.rb
CHANGED
@@ -113,31 +113,21 @@ module Dao
|
|
113
113
|
alias_method('add_to_base', 'add')
|
114
114
|
alias_method('add_to_base!', 'add!')
|
115
115
|
|
116
|
-
def relay(
|
117
|
-
|
118
|
-
when other.respond_to?(:each)
|
119
|
-
other.each do |key, messages|
|
120
|
-
Array(messages).each do |message|
|
121
|
-
add(key, message, options = {})
|
122
|
-
end
|
123
|
-
end
|
124
|
-
when other.respond_to?(:each_pair)
|
125
|
-
other.each_pair do |key, messages|
|
126
|
-
Array(messages).each do |message|
|
127
|
-
add(key, message, options = {})
|
128
|
-
end
|
129
|
-
end
|
116
|
+
def relay(*args)
|
117
|
+
options = args.size > 1 ? Map.options_for!(args) : Map.new
|
130
118
|
|
131
|
-
|
132
|
-
Array(other).flatten.each_slice(2) do |key, messages|
|
133
|
-
Array(messages).each do |message|
|
134
|
-
add(key, message, options = {})
|
135
|
-
end
|
136
|
-
end
|
119
|
+
prefix = Array(options.delete(:prefix))
|
137
120
|
|
138
|
-
|
139
|
-
|
121
|
+
args.flatten.compact.each do |source|
|
122
|
+
errors = source.respond_to?(:errors) ? source.errors : source
|
123
|
+
|
124
|
+
errors.each do |*argv|
|
125
|
+
msgs = Array(argv.pop)
|
126
|
+
key = prefix + Array(argv.pop)
|
127
|
+
msgs.each{|msg| add(Array(options[:key] || key), msg, options)}
|
128
|
+
end
|
140
129
|
end
|
130
|
+
|
141
131
|
self
|
142
132
|
end
|
143
133
|
|
@@ -181,7 +171,9 @@ module Dao
|
|
181
171
|
index = keys.pop
|
182
172
|
key = keys
|
183
173
|
value = value.to_s
|
174
|
+
|
184
175
|
next if value.strip.empty?
|
176
|
+
|
185
177
|
if key == Global
|
186
178
|
global_messages.push([key, value])
|
187
179
|
else
|
@@ -200,6 +192,18 @@ module Dao
|
|
200
192
|
end
|
201
193
|
end
|
202
194
|
|
195
|
+
def flatten
|
196
|
+
hash = Hash.new
|
197
|
+
|
198
|
+
depth_first_each do |keys, value|
|
199
|
+
index = keys.pop
|
200
|
+
hash[keys] ||= []
|
201
|
+
hash[keys].push("#{ value }")
|
202
|
+
end
|
203
|
+
|
204
|
+
hash
|
205
|
+
end
|
206
|
+
|
203
207
|
def each_full_message
|
204
208
|
full_messages.each{|msg| yield msg}
|
205
209
|
end
|
@@ -212,6 +216,14 @@ module Dao
|
|
212
216
|
select{|message| not message.strip.empty?}
|
213
217
|
end
|
214
218
|
|
219
|
+
def global
|
220
|
+
reject{|k, v| k != Global}
|
221
|
+
end
|
222
|
+
|
223
|
+
def local
|
224
|
+
reject{|k, v| k == Global}
|
225
|
+
end
|
226
|
+
|
215
227
|
# html generation methods
|
216
228
|
#
|
217
229
|
def to_html(*args)
|
@@ -222,50 +234,104 @@ module Dao
|
|
222
234
|
if block
|
223
235
|
define_method(:to_html, &block)
|
224
236
|
else
|
225
|
-
|
237
|
+
errors_to_html(*args)
|
226
238
|
end
|
227
239
|
end
|
228
240
|
|
229
|
-
def Errors.
|
241
|
+
def Errors.errors_to_html(*args)
|
242
|
+
::Errors2Html.to_html(*args)
|
243
|
+
end
|
244
|
+
|
245
|
+
def to_s(format = :html, *args, &block)
|
246
|
+
case format.to_s
|
247
|
+
when /html/
|
248
|
+
to_html(*args, &block)
|
249
|
+
|
250
|
+
when /text/
|
251
|
+
to_text(*args, &block)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
class KeyPrefixer
|
256
|
+
attr_accessor :object
|
257
|
+
attr_accessor :prefix
|
258
|
+
attr_accessor :global
|
259
|
+
|
260
|
+
def initialize(object)
|
261
|
+
@object = object
|
262
|
+
|
263
|
+
@prefix =
|
264
|
+
if @object && @object.respond_to?(:model_name)
|
265
|
+
@object.model_name.underscore
|
266
|
+
else
|
267
|
+
nil
|
268
|
+
end
|
269
|
+
|
270
|
+
@global = Array(Global)
|
271
|
+
end
|
272
|
+
|
273
|
+
def prefix(key)
|
274
|
+
is_global_key = key == @global || Array(key) == @global
|
275
|
+
|
276
|
+
if @prefix
|
277
|
+
if is_global_key
|
278
|
+
@prefix
|
279
|
+
else
|
280
|
+
["#{ @prefix }.#{ key[0] }", *key[1..-1]]
|
281
|
+
end
|
282
|
+
else
|
283
|
+
if is_global_key
|
284
|
+
'global'
|
285
|
+
else
|
286
|
+
key
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def key_prefixer
|
293
|
+
@key_prefixer ||= KeyPrefixer.new(object)
|
294
|
+
end
|
295
|
+
|
296
|
+
def prefix_key(key)
|
297
|
+
key_prefixer.prefix(key)
|
298
|
+
end
|
299
|
+
|
300
|
+
def Errors.to_hash(*args)
|
230
301
|
error = args.shift
|
231
302
|
options = Map.options_for!(args)
|
232
303
|
errors = [error, *args].flatten.compact
|
233
304
|
|
234
|
-
|
235
|
-
|
236
|
-
emap = Map.new
|
305
|
+
map = Map.new
|
237
306
|
|
238
307
|
errors.each do |e|
|
239
308
|
e.full_messages.each do |key, message|
|
240
|
-
|
241
|
-
|
309
|
+
k = e.key_prefixer.prefix(key)
|
310
|
+
|
311
|
+
map.set(k, []) unless map.has?(k)
|
312
|
+
map.get(k).push("#{ message }")
|
242
313
|
end
|
243
314
|
end
|
244
315
|
|
245
|
-
|
246
|
-
|
247
|
-
div_(:class => "dao errors summary"){
|
248
|
-
__
|
249
|
-
|
250
|
-
h3_(:class => "caption"){ "We're so sorry, but can you please fix the following errors?" }
|
251
|
-
__
|
316
|
+
map.to_hash
|
317
|
+
end
|
252
318
|
|
253
|
-
|
254
|
-
|
255
|
-
|
319
|
+
def to_hash
|
320
|
+
Errors.to_hash(self)
|
321
|
+
end
|
256
322
|
|
257
|
-
|
323
|
+
def Errors.errors_to_text(*args)
|
324
|
+
hash = to_hash(*args)
|
258
325
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
}
|
326
|
+
if hash.empty?
|
327
|
+
nil
|
328
|
+
else
|
329
|
+
hash.to_yaml
|
330
|
+
end
|
265
331
|
end
|
266
332
|
|
267
|
-
def
|
268
|
-
|
333
|
+
def to_text
|
334
|
+
Errors.errors_to_text(self)
|
269
335
|
end
|
270
336
|
end
|
271
337
|
end
|
data/lib/dao/form.rb
CHANGED
@@ -11,9 +11,20 @@ module Dao
|
|
11
11
|
|
12
12
|
# builder stuff for compatibity with rails' form_for()
|
13
13
|
#
|
14
|
-
class Builder
|
14
|
+
class Builder < Form
|
15
15
|
def Builder.new(object_name, object, view, options, block)
|
16
|
-
|
16
|
+
if object.respond_to?(:form)
|
17
|
+
|
18
|
+
html = options[:html] || {}
|
19
|
+
html[:class] ||= 'dao'
|
20
|
+
unless html[:class] =~ /(\s|\A)dao(\Z|\s)/o
|
21
|
+
html[:class] << ' dao dao-form'
|
22
|
+
end
|
23
|
+
|
24
|
+
object.form
|
25
|
+
else
|
26
|
+
raise ArgumentError, object.class.name
|
27
|
+
end
|
17
28
|
end
|
18
29
|
end
|
19
30
|
|
@@ -69,14 +80,17 @@ module Dao
|
|
69
80
|
|
70
81
|
fattr(:name) do
|
71
82
|
name =
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
'
|
83
|
+
case
|
84
|
+
when @object.respond_to?(:form_name)
|
85
|
+
@object.form_name
|
86
|
+
when @object.respond_to?(:name)
|
87
|
+
@object.name
|
88
|
+
when @object.instance_variable_defined?('@form_name')
|
89
|
+
@object.instance_variable_get('@form_name')
|
90
|
+
when @object.instance_variable_defined?('@name')
|
91
|
+
@object.instance_variable_get('@name')
|
92
|
+
else
|
93
|
+
:form
|
80
94
|
end
|
81
95
|
|
82
96
|
case name
|
@@ -107,28 +121,24 @@ module Dao
|
|
107
121
|
end
|
108
122
|
end
|
109
123
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
if @object.respond_to?(:status)
|
114
|
-
throw :status, @object.status
|
115
|
-
end
|
116
|
-
if @object.instance_variable_defined?('@status')
|
117
|
-
throw :status, @object.instance_variable_get('@status')
|
118
|
-
end
|
119
|
-
Status.new
|
120
|
-
end
|
124
|
+
# support for rails' forms...
|
125
|
+
#
|
126
|
+
fattr(:multipart){ true }
|
121
127
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
+
%w( [] []= get set has has? ).each do |method|
|
129
|
+
class_eval <<-__
|
130
|
+
def #{ method }(*args, &block)
|
131
|
+
attributes.#{ method }(*args, &block)
|
132
|
+
end
|
133
|
+
__
|
128
134
|
end
|
129
135
|
|
130
136
|
# html generation methods
|
131
137
|
#
|
138
|
+
def element(which, *args, &block)
|
139
|
+
send(which, *args, &block)
|
140
|
+
end
|
141
|
+
|
132
142
|
def form(*args, &block)
|
133
143
|
options = args.extract_options!.to_options!
|
134
144
|
keys = args.flatten
|
@@ -153,11 +163,11 @@ module Dao
|
|
153
163
|
options = args.extract_options!.to_options!
|
154
164
|
keys = args.flatten
|
155
165
|
|
156
|
-
|
157
|
-
|
166
|
+
block ||=
|
167
|
+
proc do
|
168
|
+
options.delete(:content) ||
|
169
|
+
options.delete(:value) ||
|
158
170
|
keys.map{|key| key.to_s.titleize}.join(' ')
|
159
|
-
else
|
160
|
-
block ? block.call() : (options.delete(:content) || options.delete(:value))
|
161
171
|
end
|
162
172
|
|
163
173
|
id = options.delete(:id) || id_for(keys + [:label])
|
@@ -165,7 +175,7 @@ module Dao
|
|
165
175
|
error = error_for(keys, options.delete(:error))
|
166
176
|
target = options.delete(:for) || id_for(keys)
|
167
177
|
|
168
|
-
label_(options_for(options, :for => target, :class => klass, :id => id, :data_error => error))
|
178
|
+
label_(options_for(options, :for => target, :class => klass, :id => id, :data_error => error), &block)
|
169
179
|
end
|
170
180
|
|
171
181
|
def input(*args, &block)
|
@@ -210,14 +220,11 @@ module Dao
|
|
210
220
|
klass = class_for(keys, options.delete(:class))
|
211
221
|
error = error_for(keys, options.delete(:error))
|
212
222
|
|
213
|
-
value =
|
214
|
-
|
215
|
-
|
216
|
-
else
|
217
|
-
block ? block.call(attributes.get(keys)) : options.delete(:value)
|
218
|
-
end
|
223
|
+
value = options.has_key?(:value) ? options.delete(:value) : value_for(attributes, keys)
|
224
|
+
|
225
|
+
content = (block ? block.call : (options.delete(:content) || 'Submit'))
|
219
226
|
|
220
|
-
button_(options_for(options, :type => type, :name => name, :value => value, :class => klass, :id => id, :data_error => error)){}
|
227
|
+
button_(options_for(options, :type => type, :name => name, :value => value, :class => klass, :id => id, :data_error => error)){ content }
|
221
228
|
end
|
222
229
|
|
223
230
|
def radio_button(*args, &block)
|
@@ -257,7 +264,7 @@ module Dao
|
|
257
264
|
values = options.delete(:values) || options.delete(:checked)
|
258
265
|
|
259
266
|
unless options.has_key?(:checked)
|
260
|
-
checked =
|
267
|
+
checked = Coerce.boolean(attributes.get(keys))
|
261
268
|
options[:checked] = checked if checked
|
262
269
|
end
|
263
270
|
|
@@ -336,29 +343,22 @@ module Dao
|
|
336
343
|
keys = args.flatten
|
337
344
|
|
338
345
|
name = options.delete(:name) || name_for(keys)
|
339
|
-
|
340
|
-
blank = options.delete(:blank)
|
346
|
+
values = options.delete(:values) || options.delete(:options) || options.delete(:from)
|
341
347
|
|
342
|
-
|
343
|
-
|
344
|
-
options.delete(:selected)
|
345
|
-
else
|
346
|
-
value_for(attributes, keys)
|
347
|
-
end
|
348
|
+
has_blank = options.has_key?(:blank) && options[:blank] != false
|
349
|
+
blank = options.delete(:blank)
|
348
350
|
|
349
351
|
id = options.delete(:id) || id_for(keys)
|
350
352
|
klass = class_for(keys, options.delete(:class))
|
351
353
|
error = error_for(keys, options.delete(:error))
|
352
354
|
|
353
|
-
|
354
|
-
|
355
|
-
if from.nil?
|
355
|
+
if values.nil?
|
356
356
|
key = keys.map{|key| "#{ key }"}
|
357
357
|
key.last << "_options"
|
358
|
-
|
358
|
+
values = attributes.get(*key) if attributes.has?(*key)
|
359
359
|
end
|
360
360
|
|
361
|
-
list = Array(
|
361
|
+
list = Array(values).map{|value| value.dup rescue value} # ensure list is dup'd
|
362
362
|
|
363
363
|
case list.first
|
364
364
|
when Hash, Array
|
@@ -369,52 +369,68 @@ module Dao
|
|
369
369
|
list.map!{|element| [element, element]}
|
370
370
|
end
|
371
371
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
372
|
+
if has_blank
|
373
|
+
case blank
|
374
|
+
when false
|
375
|
+
blank = nil
|
376
|
+
when nil, true
|
377
|
+
blank = [nil, nil]
|
378
|
+
else
|
379
|
+
blank = [Array(blank).first, '']
|
380
|
+
end
|
379
381
|
end
|
380
382
|
|
381
383
|
selected_value =
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
key = [:id, 'id', :value, 'value'].detect{|k| selected.has_key?(k)}
|
387
|
-
key ? selected[key] : selected
|
388
|
-
else
|
389
|
-
selected
|
384
|
+
if options.has_key?(:selected)
|
385
|
+
options.delete(:selected)
|
386
|
+
else
|
387
|
+
value_for(attributes, keys)
|
390
388
|
end
|
391
389
|
|
392
390
|
select_(options_for(options, :name => name, :class => klass, :id => id, :data_error => error)){
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
content, value, selected, *ignored = returned
|
399
|
-
when Hash
|
400
|
-
content = returned[:content]
|
401
|
-
value = returned[:value]
|
402
|
-
selected = returned[:selected]
|
403
|
-
else
|
404
|
-
content = returned
|
405
|
-
value = returned
|
406
|
-
selected = nil
|
407
|
-
end
|
408
|
-
|
409
|
-
value ||= content
|
391
|
+
if blank
|
392
|
+
content = blank.first || ''
|
393
|
+
value = blank.last
|
394
|
+
value.nil? ? option_(){ content } : option_(:value => value){ content }
|
395
|
+
end
|
410
396
|
|
411
|
-
|
412
|
-
|
397
|
+
unless list.empty?
|
398
|
+
list.each do |pair|
|
399
|
+
returned = block ? Dao.call(block, :call, pair.first, pair.last, selected_value) : pair
|
400
|
+
|
401
|
+
opts = Map.new
|
402
|
+
selected = nil
|
403
|
+
|
404
|
+
case returned
|
405
|
+
when Array
|
406
|
+
content, value, selected, *ignored = returned
|
407
|
+
if value.is_a?(Hash)
|
408
|
+
map = Map.for(value)
|
409
|
+
value = map.delete(:value)
|
410
|
+
selected = map.delete(:selected)
|
411
|
+
opts.update(map)
|
412
|
+
end
|
413
|
+
|
414
|
+
when Hash
|
415
|
+
content = returned[:content]
|
416
|
+
value = returned[:value]
|
417
|
+
selected = returned[:selected]
|
418
|
+
|
419
|
+
else
|
420
|
+
content = returned
|
421
|
+
value = returned
|
422
|
+
selected = nil
|
423
|
+
end
|
424
|
+
|
425
|
+
if selected.nil?
|
426
|
+
selected = (value.to_s == selected_value.to_s)
|
427
|
+
end
|
428
|
+
|
429
|
+
opts[:value] = (value.nil? ? content : value)
|
430
|
+
opts[:selected] = Coerce.boolean(selected) if selected
|
431
|
+
|
432
|
+
option_(opts){ content }
|
413
433
|
end
|
414
|
-
|
415
|
-
opts = {:value => value}
|
416
|
-
opts[:selected] = !!selected if selected
|
417
|
-
option_(opts){ content }
|
418
434
|
end
|
419
435
|
}
|
420
436
|
end
|
@@ -521,6 +537,7 @@ module Dao
|
|
521
537
|
|
522
538
|
def options_for(*hashes)
|
523
539
|
map = Map.new
|
540
|
+
|
524
541
|
hashes.flatten.each do |h|
|
525
542
|
case((data = h.delete(:data) || h.delete('data')))
|
526
543
|
when Hash
|
@@ -533,6 +550,11 @@ module Dao
|
|
533
550
|
map[attr_for(k)] = v unless v.nil?
|
534
551
|
end
|
535
552
|
end
|
553
|
+
|
554
|
+
%w( readonly disabled autofocus checked multiple ).each do |attr|
|
555
|
+
map.delete(attr) unless Coerce.boolean(map[attr])
|
556
|
+
end
|
557
|
+
|
536
558
|
map
|
537
559
|
end
|
538
560
|
|