dao 2.1.0 → 2.2.3
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 +1 -0
- data/TODO +39 -0
- data/dao.gemspec +4 -4
- data/lib/dao.rb +8 -4
- data/lib/dao/active_record.rb +46 -4
- data/lib/dao/api/interfaces.rb +19 -4
- data/lib/dao/data.rb +3 -53
- data/lib/dao/errors.rb +26 -19
- data/lib/dao/exceptions.rb +23 -1
- data/lib/dao/form.rb +55 -11
- data/lib/dao/params.rb +41 -51
- data/lib/dao/presenter.rb +129 -0
- data/lib/dao/rails.rb +49 -1
- data/lib/dao/rails/lib/generators/dao/dao_generator.rb +1 -2
- data/lib/dao/rails/lib/generators/dao/templates/api.rb +1 -1
- data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +25 -31
- data/lib/dao/rails/lib/generators/dao/templates/dao.js +34 -25
- data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +2 -1
- data/lib/dao/result.rb +27 -2
- data/lib/dao/status.rb +10 -2
- data/lib/dao/support.rb +1 -1
- data/lib/dao/validations.rb +143 -8
- data/test/dao_test.rb +56 -1
- metadata +15 -13
@@ -1,52 +1,39 @@
|
|
1
1
|
class APIController < ApplicationController
|
2
2
|
layout false
|
3
3
|
|
4
|
-
skip_before_filter true
|
5
4
|
skip_before_filter :verify_authenticity_token
|
6
5
|
|
7
6
|
before_filter :setup_path
|
8
7
|
before_filter :setup_api
|
9
8
|
|
10
|
-
WhiteList = %w( ping index
|
11
|
-
|
12
|
-
### skip_before_filter :set_current_user if Rails.env.production?
|
13
|
-
|
14
|
-
##
|
15
|
-
# /api/foo/2/bar/4 -> api.call('/foo/2/bar/4')
|
16
|
-
#
|
17
|
-
def call
|
18
|
-
path = params[:path]
|
19
|
-
mode = params['mode'] || (request.get? ? 'read' : 'write')
|
20
|
-
|
21
|
-
result = api.mode(mode).call(path, params)
|
9
|
+
WhiteList = Set.new( %w( ping index ) )
|
10
|
+
BlackList = Set.new( %w( ) )
|
22
11
|
|
12
|
+
def index
|
13
|
+
result = call(path, params)
|
23
14
|
respond_with(result)
|
24
15
|
end
|
25
16
|
|
26
|
-
|
27
|
-
#
|
28
|
-
def index
|
29
|
-
json = json_for(api.index)
|
17
|
+
protected
|
30
18
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
19
|
+
def call(path, params)
|
20
|
+
mode = params['mode'] || (request.get? ? 'read' : 'write')
|
21
|
+
result = api.mode(mode).call(path, params)
|
35
22
|
end
|
36
23
|
|
37
|
-
|
24
|
+
def respond_with(object, options = {})
|
25
|
+
json = json_for(object)
|
38
26
|
|
39
|
-
|
40
|
-
|
27
|
+
status = object.status if object.respond_to?(:status)
|
28
|
+
status = status.code if status.respond_to?(:code)
|
29
|
+
status = options[:status] || 200 unless status
|
41
30
|
|
42
31
|
respond_to do |wants|
|
43
|
-
wants.json{ render :json => json, :status =>
|
44
|
-
wants.html{ render :text => json, :status =>
|
32
|
+
wants.json{ render :json => json, :status => status }
|
33
|
+
wants.html{ render :text => json, :status => status, :content_type => 'text/plain' }
|
45
34
|
end
|
46
35
|
end
|
47
36
|
|
48
|
-
# if you don't have yajl-ruby and yajl/json_gem loaded your json will suck
|
49
|
-
#
|
50
37
|
def json_for(object)
|
51
38
|
if Rails.env.production?
|
52
39
|
::JSON.generate(object)
|
@@ -56,7 +43,7 @@ protected
|
|
56
43
|
end
|
57
44
|
|
58
45
|
def setup_path
|
59
|
-
@path = params[:path]
|
46
|
+
@path = params[:path] || params[:action] || 'index'
|
60
47
|
end
|
61
48
|
|
62
49
|
def path
|
@@ -97,14 +84,21 @@ protected
|
|
97
84
|
end
|
98
85
|
|
99
86
|
def self.white_listed?(path)
|
100
|
-
|
101
|
-
@white_listed[path.to_s]
|
87
|
+
WhiteList.include?(path.to_s)
|
102
88
|
end
|
103
89
|
|
104
90
|
def white_listed?(path)
|
105
91
|
self.class.white_listed?(path)
|
106
92
|
end
|
107
93
|
|
94
|
+
def self.black_listed?(path)
|
95
|
+
BlackList.include?(path.to_s)
|
96
|
+
end
|
97
|
+
|
98
|
+
def black_listed?(path)
|
99
|
+
self.class.black_listed?(path)
|
100
|
+
end
|
101
|
+
|
108
102
|
def http_basic_auth
|
109
103
|
@http_basic_auth ||= (
|
110
104
|
request.env['HTTP_AUTHORIZATION'] ||
|
@@ -37,8 +37,14 @@ if(!window.Dao){
|
|
37
37
|
|
38
38
|
if(arguments.length > 1){
|
39
39
|
options.path = arguments[0];
|
40
|
-
|
41
|
-
|
40
|
+
|
41
|
+
if(typeof(arguments[1])=='function'){
|
42
|
+
options.success = arguments[1];
|
43
|
+
options.params = arguments[2];
|
44
|
+
} else {
|
45
|
+
options.params = arguments[1];
|
46
|
+
options.success = arguments[2];
|
47
|
+
}
|
42
48
|
}
|
43
49
|
|
44
50
|
if(!options.path){
|
@@ -49,35 +55,35 @@ if(!window.Dao){
|
|
49
55
|
options.params = {};
|
50
56
|
}
|
51
57
|
|
52
|
-
if(!options.success){
|
53
|
-
options.success = function(result){
|
54
|
-
result = new Dao.Result(result);
|
55
|
-
api.result = result;
|
56
|
-
api.results.push(result);
|
57
|
-
};
|
58
|
-
}
|
59
|
-
|
60
58
|
var url = api.route + options.path;
|
61
|
-
|
62
59
|
var data = options.params;
|
63
60
|
|
64
|
-
|
65
|
-
|
66
|
-
|
61
|
+
if(options.success){
|
62
|
+
|
63
|
+
var returned = api;
|
64
|
+
var success = function(result){
|
65
|
+
var result = new Dao.Result(result);
|
67
66
|
options.success(result);
|
68
|
-
}
|
69
|
-
api.result = result;
|
70
|
-
api.results.push(result);
|
71
|
-
}
|
72
|
-
};
|
67
|
+
};
|
73
68
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
69
|
+
var ajax = {'url' : url, 'data' : data, 'success' : success, 'async' : true};
|
70
|
+
|
71
|
+
Dao[api.mode](ajax);
|
72
|
+
return(returned);
|
78
73
|
|
79
|
-
|
80
|
-
|
74
|
+
} else {
|
75
|
+
|
76
|
+
var returned = null;
|
77
|
+
var success = function(result){
|
78
|
+
returned = new Dao.Result(result);
|
79
|
+
};
|
80
|
+
|
81
|
+
var ajax = {'url' : url, 'data' : data, 'success' : success, 'async' : false};
|
82
|
+
|
83
|
+
Dao[api.mode](ajax);
|
84
|
+
return(returned);
|
85
|
+
|
86
|
+
};
|
81
87
|
};
|
82
88
|
|
83
89
|
// meta-program api.read(..), api.post(...), ...
|
@@ -120,6 +126,9 @@ if(!window.Dao){
|
|
120
126
|
ajax.url = options.url;
|
121
127
|
ajax.dataType = 'json';
|
122
128
|
ajax.cache = false;
|
129
|
+
if(options.async==false || options.sync==true){
|
130
|
+
ajax.async = false;
|
131
|
+
};
|
123
132
|
if(ajax.type == 'POST' || ajax.type == 'PUT'){
|
124
133
|
ajax.data = jq.toJSON(options.data || {});
|
125
134
|
} else {
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module DaoHelper
|
2
2
|
def render_dao(result, *args, &block)
|
3
3
|
if result.status =~ 200 or result.status == 420
|
4
|
+
@result = result unless defined?(@result)
|
4
5
|
render(*args, &block)
|
5
6
|
else
|
6
|
-
|
7
|
+
result.error!
|
7
8
|
end
|
8
9
|
end
|
9
10
|
end
|
data/lib/dao/result.rb
CHANGED
@@ -7,6 +7,7 @@ module Dao
|
|
7
7
|
attr_accessor :mode
|
8
8
|
attr_accessor :params
|
9
9
|
attr_accessor :validations
|
10
|
+
attr_accessor :presenter
|
10
11
|
attr_accessor :form
|
11
12
|
attr_accessor :forcing_validity
|
12
13
|
|
@@ -18,11 +19,19 @@ module Dao
|
|
18
19
|
options = Dao.options_for!(args)
|
19
20
|
args.push('/dao') if args.empty?
|
20
21
|
|
21
|
-
|
22
|
+
path_args = args.select{|arg| arg.is_a?(String) or args.is_a?(Symbol)}
|
23
|
+
data_args = args.select{|arg| arg.is_a?(Hash)}
|
24
|
+
data_args += [options[:data]] if options.has_key?(:data)
|
25
|
+
|
26
|
+
path = Path.for(*path_args)
|
22
27
|
status = Status.ok
|
23
28
|
errors = Errors.new
|
24
29
|
data = Data.new
|
25
30
|
|
31
|
+
data_args.each do |data_arg|
|
32
|
+
data.update(data_arg)
|
33
|
+
end
|
34
|
+
|
26
35
|
api = options[:api]
|
27
36
|
interface = options[:interface]
|
28
37
|
params = options[:params] || Params.new
|
@@ -33,6 +42,7 @@ module Dao
|
|
33
42
|
|
34
43
|
form = Form.for(self)
|
35
44
|
validations = Validations.for(self)
|
45
|
+
presenter = Presenter.for(self)
|
36
46
|
|
37
47
|
self[:path] = path
|
38
48
|
self[:status] = status
|
@@ -45,6 +55,7 @@ module Dao
|
|
45
55
|
@params = params
|
46
56
|
@form = form
|
47
57
|
@validations = validations
|
58
|
+
@presenter = presenter
|
48
59
|
@forcing_validity = false
|
49
60
|
end
|
50
61
|
|
@@ -90,11 +101,13 @@ module Dao
|
|
90
101
|
@forcing_validity = true
|
91
102
|
end
|
92
103
|
|
93
|
-
def valid?
|
104
|
+
def valid?(*args)
|
94
105
|
if @forcing_validity
|
95
106
|
true
|
96
107
|
else
|
108
|
+
options = Dao.options_for!(args)
|
97
109
|
validate unless validations.ran?
|
110
|
+
validate if options[:validate]
|
98
111
|
errors.empty? and status.ok?
|
99
112
|
end
|
100
113
|
end
|
@@ -122,5 +135,17 @@ module Dao
|
|
122
135
|
def validates(*args, &block)
|
123
136
|
validations.add(*args, &block)
|
124
137
|
end
|
138
|
+
|
139
|
+
def error!
|
140
|
+
raise Dao::Error::Result.for(self)
|
141
|
+
end
|
142
|
+
|
143
|
+
def tag(*args, &block)
|
144
|
+
presenter.tag(*args, &block)
|
145
|
+
end
|
146
|
+
|
147
|
+
def inspect
|
148
|
+
::JSON.pretty_generate(self, :max_nesting => 0)
|
149
|
+
end
|
125
150
|
end
|
126
151
|
end
|
data/lib/dao/status.rb
CHANGED
@@ -113,6 +113,10 @@ module Dao
|
|
113
113
|
Groups.each do |code, group|
|
114
114
|
module_eval <<-__, __FILE__, __LINE__ -1
|
115
115
|
|
116
|
+
def Status.#{ group }
|
117
|
+
@status_group_#{ group } ||= Status.for(#{ code })
|
118
|
+
end
|
119
|
+
|
116
120
|
def #{ group }?()
|
117
121
|
#{ code } == @group
|
118
122
|
end
|
@@ -183,8 +187,12 @@ module Dao
|
|
183
187
|
message = Code2Message[code]
|
184
188
|
new(code, message)
|
185
189
|
when Symbol, String
|
186
|
-
|
187
|
-
|
190
|
+
if arg.to_s =~ %r/^\d+$/
|
191
|
+
code = arg.to_i
|
192
|
+
else
|
193
|
+
sym = Status.underscore(arg).to_sym
|
194
|
+
code = Symbol2Code[sym]
|
195
|
+
end
|
188
196
|
if code
|
189
197
|
message = Code2Message[code]
|
190
198
|
else
|
data/lib/dao/support.rb
CHANGED
data/lib/dao/validations.rb
CHANGED
@@ -28,6 +28,8 @@ module Dao
|
|
28
28
|
end
|
29
29
|
|
30
30
|
|
31
|
+
# class methods
|
32
|
+
#
|
31
33
|
class << Validations
|
32
34
|
def for(*args, &block)
|
33
35
|
new(*args, &block)
|
@@ -43,6 +45,8 @@ module Dao
|
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
48
|
+
# instance methods
|
49
|
+
#
|
46
50
|
attr_accessor :result
|
47
51
|
attr_accessor :ran
|
48
52
|
|
@@ -51,7 +55,6 @@ module Dao
|
|
51
55
|
@ran = false
|
52
56
|
super
|
53
57
|
end
|
54
|
-
|
55
58
|
alias_method('ran?', 'ran')
|
56
59
|
|
57
60
|
def params
|
@@ -75,11 +78,10 @@ module Dao
|
|
75
78
|
depth_first_each{ size += 1 }
|
76
79
|
size
|
77
80
|
end
|
78
|
-
|
79
81
|
alias_method('count', 'size')
|
80
82
|
alias_method('length', 'size')
|
81
83
|
|
82
|
-
Cleared = '
|
84
|
+
Cleared = 'Cleared'.freeze unless defined?(Cleared)
|
83
85
|
|
84
86
|
def run
|
85
87
|
previous_errors = []
|
@@ -88,8 +90,7 @@ module Dao
|
|
88
90
|
errors.each_message do |keys, message|
|
89
91
|
previous_errors.push([keys, message])
|
90
92
|
end
|
91
|
-
|
92
|
-
errors.clear
|
93
|
+
errors.clear!
|
93
94
|
|
94
95
|
depth_first_each do |keys, chain|
|
95
96
|
chain.each do |callback|
|
@@ -158,12 +159,10 @@ module Dao
|
|
158
159
|
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
159
160
|
block = args.pop if args.last.respond_to?(:call)
|
160
161
|
block ||= NotNil
|
161
|
-
callback =
|
162
|
+
callback = Callback.new(options, &block)
|
162
163
|
set(args => Callback::Chain.new) unless has?(args)
|
163
164
|
get(args).add(callback)
|
164
165
|
callback
|
165
|
-
#args.push(callback)
|
166
|
-
#set(*args)
|
167
166
|
end
|
168
167
|
end
|
169
168
|
|
@@ -429,6 +428,142 @@ module Dao
|
|
429
428
|
|
430
429
|
validates(*args, &block)
|
431
430
|
end
|
431
|
+
|
432
|
+
def validates_any_of(*args)
|
433
|
+
options = Dao.options_for!(args)
|
434
|
+
list = args
|
435
|
+
|
436
|
+
list.each do |args|
|
437
|
+
candidates = list.dup
|
438
|
+
candidates.delete(args)
|
439
|
+
|
440
|
+
message = options[:message] || "(or #{ candidates.map{|candidate| Array(candidate).join('.')}.join(', ') } ) is blank or missing"
|
441
|
+
allow_nil = options[:allow_nil]
|
442
|
+
allow_blank = options[:allow_blank]
|
443
|
+
|
444
|
+
result = self.result
|
445
|
+
|
446
|
+
block =
|
447
|
+
lambda do |value|
|
448
|
+
map = Dao.map(:valid => true)
|
449
|
+
values = list.map{|key| result.get(key)}
|
450
|
+
valid = false
|
451
|
+
values.each do |val|
|
452
|
+
if val
|
453
|
+
valid = true
|
454
|
+
break
|
455
|
+
end
|
456
|
+
|
457
|
+
if val.nil?
|
458
|
+
if allow_nil
|
459
|
+
valid = true
|
460
|
+
break
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
val = val.to_s.strip
|
465
|
+
|
466
|
+
if val.empty?
|
467
|
+
if allow_blank
|
468
|
+
valid = true
|
469
|
+
break
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
unless valid
|
475
|
+
if value.nil?
|
476
|
+
unless allow_nil
|
477
|
+
map[:message] = message
|
478
|
+
map[:valid] = false
|
479
|
+
throw(:valid, map)
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
value = value.to_s.strip
|
484
|
+
|
485
|
+
if value.empty?
|
486
|
+
unless allow_blank
|
487
|
+
map[:message] = message
|
488
|
+
map[:valid] = false
|
489
|
+
throw(:valid, map)
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
map
|
495
|
+
end
|
496
|
+
validates(*args, &block)
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
def validates_all_of(*args)
|
501
|
+
options = Dao.options_for!(args)
|
502
|
+
list = args
|
503
|
+
|
504
|
+
list.each do |args|
|
505
|
+
candidates = list.dup
|
506
|
+
candidates.delete(args)
|
507
|
+
|
508
|
+
message = options[:message] || "(and #{ candidates.map{|candidate| Array(candidate).join('.')}.join(', ') } ) is blank or missing"
|
509
|
+
allow_nil = options[:allow_nil]
|
510
|
+
allow_blank = options[:allow_blank]
|
511
|
+
|
512
|
+
result = self.result
|
513
|
+
|
514
|
+
block =
|
515
|
+
lambda do |value|
|
516
|
+
map = Dao.map(:valid => true)
|
517
|
+
|
518
|
+
values = list.map{|key| result.get(key)}
|
519
|
+
valid = true
|
520
|
+
values.each do |val|
|
521
|
+
if val
|
522
|
+
break
|
523
|
+
end
|
524
|
+
|
525
|
+
if val.nil?
|
526
|
+
unless allow_nil
|
527
|
+
valid = false
|
528
|
+
break
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
val = val.to_s.strip
|
533
|
+
|
534
|
+
if val.empty?
|
535
|
+
unless allow_blank
|
536
|
+
valid = false
|
537
|
+
break
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
unless valid
|
543
|
+
if value.nil?
|
544
|
+
unless allow_nil
|
545
|
+
map[:message] = message
|
546
|
+
map[:valid] = false
|
547
|
+
throw(:valid, map)
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
value = value.to_s.strip
|
552
|
+
|
553
|
+
if value.empty?
|
554
|
+
unless allow_blank
|
555
|
+
map[:message] = message
|
556
|
+
map[:valid] = false
|
557
|
+
throw(:valid, map)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
map
|
563
|
+
end
|
564
|
+
validates(*args, &block)
|
565
|
+
end
|
566
|
+
end
|
432
567
|
end
|
433
568
|
|
434
569
|
def Validations.add(method_name, &block)
|