dao 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +96 -17
- data/Rakefile +92 -43
- data/dao.gemspec +12 -8
- data/lib/dao.rb +33 -20
- data/lib/dao/active_record.rb +51 -43
- data/lib/dao/api.rb +1 -1
- data/lib/dao/api/context.rb +6 -6
- data/lib/dao/api/dsl.rb +2 -2
- data/lib/dao/api/interfaces.rb +232 -0
- data/lib/dao/api/modes.rb +9 -6
- data/lib/dao/data.rb +7 -0
- data/lib/dao/errors.rb +27 -30
- data/lib/dao/form.rb +52 -28
- data/lib/dao/instance_exec.rb +37 -0
- data/lib/dao/interface.rb +16 -0
- data/lib/dao/params.rb +48 -24
- data/lib/dao/path.rb +1 -1
- data/lib/dao/rails.rb +5 -2
- data/lib/dao/rails/app/api.rb +1 -1
- data/lib/dao/rails/lib/generators/dao/dao_generator.rb +12 -0
- data/lib/dao/rails/lib/generators/dao/templates/api.rb +7 -1
- data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +25 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao.css +27 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao.js +149 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +10 -0
- data/lib/dao/result.rb +53 -14
- data/lib/dao/status.rb +162 -1
- data/lib/dao/stdext.rb +6 -0
- data/lib/dao/support.rb +10 -2
- data/lib/dao/validations.rb +338 -14
- data/sample/rails_app/pubic/javascripts/dao.js +148 -0
- data/test/dao_test.rb +18 -19
- metadata +29 -18
- data/TODO +0 -37
- data/db/dao.yml +0 -8
data/lib/dao/api.rb
CHANGED
data/lib/dao/api/context.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
1
|
module Dao
|
2
2
|
class Context
|
3
|
-
Attrs = %w( api
|
3
|
+
Attrs = %w( api interface params result method args )
|
4
4
|
Attrs.each{|attr| attr_accessor(attr)}
|
5
5
|
|
6
6
|
def initialize(*args, &block)
|
7
7
|
options = Dao.options_for!(args)
|
8
8
|
|
9
9
|
api = options[:api]
|
10
|
-
|
10
|
+
interface = options[:interface]
|
11
11
|
params = options[:params]
|
12
12
|
|
13
|
-
params = Params.for(:api => api, :
|
14
|
-
result = Result.new(:api => api, :
|
13
|
+
params = Params.for(:api => api, :interface => interface, :params => params)
|
14
|
+
result = Result.new(:api => api, :interface => interface, :params => params)
|
15
15
|
params.result = result
|
16
16
|
|
17
|
-
method =
|
17
|
+
method = interface.method.bind(api)
|
18
18
|
args = [params, result].slice(0, method.arity)
|
19
19
|
|
20
20
|
self.api = api
|
21
|
-
self.
|
21
|
+
self.interface = interface
|
22
22
|
self.params = params
|
23
23
|
self.result = result
|
24
24
|
self.method = method
|
data/lib/dao/api/dsl.rb
CHANGED
@@ -16,10 +16,10 @@ module Dao
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def no_docs_left_on_stack!
|
19
|
-
raise "no
|
19
|
+
raise "no interface for #{ docs.inspect }" unless docs.empty?
|
20
20
|
end
|
21
21
|
|
22
|
-
%w(
|
22
|
+
%w( interface doc docs description desc ).each do |method|
|
23
23
|
module_eval <<-__, __FILE__, __LINE__ - 1
|
24
24
|
|
25
25
|
def #{ method }(*args, &block)
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module Dao
|
2
|
+
class Api
|
3
|
+
class << Api
|
4
|
+
def interfaces
|
5
|
+
@interfaces ||= Map.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def interface(path, &block)
|
9
|
+
api = self
|
10
|
+
path = Path.new(path)
|
11
|
+
|
12
|
+
method =
|
13
|
+
module_eval{
|
14
|
+
define_method(path + '/interface', &block)
|
15
|
+
instance_method(path + '/interface')
|
16
|
+
}
|
17
|
+
|
18
|
+
|
19
|
+
interface = Interface.new(
|
20
|
+
'api' => api,
|
21
|
+
'path' => path,
|
22
|
+
'method' => method,
|
23
|
+
'doc' => docs.pop
|
24
|
+
)
|
25
|
+
|
26
|
+
interfaces[path] = interface
|
27
|
+
end
|
28
|
+
|
29
|
+
def description(string)
|
30
|
+
doc(:description => Dao.unindent(string))
|
31
|
+
end
|
32
|
+
alias_method('desc', 'description')
|
33
|
+
|
34
|
+
def doc(*args)
|
35
|
+
docs.push(Map[:description, nil]) if docs.empty?
|
36
|
+
doc = docs.last
|
37
|
+
options = Dao.options_for!(args)
|
38
|
+
if options.empty?
|
39
|
+
options[:description] = args.join(' ')
|
40
|
+
end
|
41
|
+
doc.update(options)
|
42
|
+
doc
|
43
|
+
end
|
44
|
+
|
45
|
+
def docs
|
46
|
+
@docs ||= []
|
47
|
+
end
|
48
|
+
|
49
|
+
def index
|
50
|
+
index = Map.new
|
51
|
+
interfaces.each do |path, interface|
|
52
|
+
index[path] = interface.doc || {'description' => path}
|
53
|
+
end
|
54
|
+
index
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def call(path = '/index', params = {})
|
59
|
+
api = self
|
60
|
+
path = Path.new(path)
|
61
|
+
interface = interfaces[path]
|
62
|
+
raise(NameError, "NO SUCH INTERFACE: #{ path }") unless interface
|
63
|
+
|
64
|
+
params = Dao.parse(path, params)
|
65
|
+
|
66
|
+
context = Context.new(
|
67
|
+
:api => api,
|
68
|
+
:interface => interface,
|
69
|
+
:params => params
|
70
|
+
)
|
71
|
+
|
72
|
+
callstack(context) do
|
73
|
+
catching(:result){ context.call() }
|
74
|
+
end
|
75
|
+
|
76
|
+
context.result
|
77
|
+
end
|
78
|
+
|
79
|
+
def index
|
80
|
+
self.class.index
|
81
|
+
end
|
82
|
+
|
83
|
+
def interfaces
|
84
|
+
self.class.interfaces
|
85
|
+
end
|
86
|
+
|
87
|
+
def context
|
88
|
+
callstack.last || raise('no context!')
|
89
|
+
end
|
90
|
+
|
91
|
+
def result
|
92
|
+
context.result
|
93
|
+
end
|
94
|
+
|
95
|
+
def params
|
96
|
+
result.params
|
97
|
+
end
|
98
|
+
|
99
|
+
def errors
|
100
|
+
result.errors
|
101
|
+
end
|
102
|
+
|
103
|
+
def apply(hash = {})
|
104
|
+
data.apply(hash)
|
105
|
+
end
|
106
|
+
|
107
|
+
def update(hash = {})
|
108
|
+
data.update(hash)
|
109
|
+
end
|
110
|
+
|
111
|
+
def default(*args)
|
112
|
+
hash = Map.options_for!(args)
|
113
|
+
if hash.empty?
|
114
|
+
value = args.pop
|
115
|
+
key = args
|
116
|
+
hash = {key => value}
|
117
|
+
end
|
118
|
+
data.apply(hash)
|
119
|
+
end
|
120
|
+
|
121
|
+
def status(*args, &block)
|
122
|
+
result.status(*args, &block)
|
123
|
+
end
|
124
|
+
|
125
|
+
def data(*args)
|
126
|
+
if args.empty?
|
127
|
+
result.data
|
128
|
+
else
|
129
|
+
result.data.replace(*args)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def data!(*args)
|
134
|
+
result.data.replace(*args)
|
135
|
+
valid!
|
136
|
+
end
|
137
|
+
|
138
|
+
def update(*args, &block)
|
139
|
+
data.update(*args, &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
def replace(*args, &block)
|
143
|
+
data.replace(*args, &block)
|
144
|
+
end
|
145
|
+
|
146
|
+
def validations
|
147
|
+
result.validations
|
148
|
+
end
|
149
|
+
|
150
|
+
def validates(*args, &block)
|
151
|
+
result.validates(*args, &block)
|
152
|
+
end
|
153
|
+
|
154
|
+
def validate
|
155
|
+
result.validate
|
156
|
+
end
|
157
|
+
|
158
|
+
def valid?
|
159
|
+
result.valid?
|
160
|
+
end
|
161
|
+
|
162
|
+
def validate!
|
163
|
+
result.validate!
|
164
|
+
end
|
165
|
+
|
166
|
+
def valid!
|
167
|
+
result.valid!
|
168
|
+
end
|
169
|
+
|
170
|
+
include Validations::Common
|
171
|
+
|
172
|
+
def return!(*value)
|
173
|
+
throw(:result, *value)
|
174
|
+
end
|
175
|
+
|
176
|
+
=begin
|
177
|
+
def set(*args, &block)
|
178
|
+
result.data.set(*args, &block)
|
179
|
+
end
|
180
|
+
|
181
|
+
def get(*args, &block)
|
182
|
+
params.data.get(*args, &block)
|
183
|
+
end
|
184
|
+
=end
|
185
|
+
|
186
|
+
def callstack(context = nil, &block)
|
187
|
+
@callstack ||= []
|
188
|
+
|
189
|
+
if block and context
|
190
|
+
begin
|
191
|
+
@callstack.push(context)
|
192
|
+
return block.call()
|
193
|
+
ensure
|
194
|
+
@callstack.pop
|
195
|
+
end
|
196
|
+
else
|
197
|
+
@callstack
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def catching(label = :result, &block)
|
202
|
+
@catching ||= []
|
203
|
+
|
204
|
+
if block
|
205
|
+
begin
|
206
|
+
@catching.push(label)
|
207
|
+
catch(label, &block)
|
208
|
+
ensure
|
209
|
+
@catching.pop
|
210
|
+
end
|
211
|
+
else
|
212
|
+
@catching.last
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def catching_results(&block)
|
217
|
+
catching(:result, &block)
|
218
|
+
end
|
219
|
+
|
220
|
+
def catching?
|
221
|
+
catching
|
222
|
+
end
|
223
|
+
|
224
|
+
def catching_results?
|
225
|
+
catching == :result
|
226
|
+
end
|
227
|
+
|
228
|
+
def respond_to?(*args)
|
229
|
+
super(*args) || super(Path.absolute_path_for(*args))
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
data/lib/dao/api/modes.rb
CHANGED
@@ -19,7 +19,14 @@ module Dao
|
|
19
19
|
|
20
20
|
def #{ mode }(*args, &block)
|
21
21
|
if args.empty?
|
22
|
-
|
22
|
+
if catching_results?
|
23
|
+
if self.mode == #{ mode.inspect }
|
24
|
+
mode(#{ mode.inspect }, &block)
|
25
|
+
return!
|
26
|
+
end
|
27
|
+
else
|
28
|
+
mode(#{ mode.inspect }, &block)
|
29
|
+
end
|
23
30
|
else
|
24
31
|
mode(#{ mode.inspect }) do
|
25
32
|
call(*args, &block)
|
@@ -68,11 +75,7 @@ module Dao
|
|
68
75
|
if block.nil?
|
69
76
|
condition
|
70
77
|
else
|
71
|
-
if condition
|
72
|
-
result = block.call
|
73
|
-
throw(:result, result) if catching_the_result?
|
74
|
-
result
|
75
|
-
end
|
78
|
+
send(mode, &block) if condition
|
76
79
|
end
|
77
80
|
end
|
78
81
|
end
|
data/lib/dao/data.rb
CHANGED
data/lib/dao/errors.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
module Dao
|
2
2
|
class Errors < ::Map
|
3
|
+
include Tagz.globally
|
4
|
+
class << Errors
|
5
|
+
include Tagz.globally
|
6
|
+
end
|
7
|
+
|
3
8
|
Global = '*' unless defined?(Global)
|
4
|
-
Separator = '
|
9
|
+
Separator = '⇒' unless defined?(Separator)
|
5
10
|
|
6
11
|
class Message < ::String
|
7
12
|
attr_accessor :sticky
|
@@ -129,7 +134,7 @@ module Dao
|
|
129
134
|
end
|
130
135
|
|
131
136
|
def invalid?(*keys)
|
132
|
-
!get(keys).nil?
|
137
|
+
has?(keys) and !get(keys).nil?
|
133
138
|
end
|
134
139
|
|
135
140
|
alias_method 'on?', 'invalid?'
|
@@ -146,6 +151,7 @@ module Dao
|
|
146
151
|
alias_method 'length', 'size'
|
147
152
|
|
148
153
|
def full_messages
|
154
|
+
global_messages = []
|
149
155
|
full_messages = []
|
150
156
|
|
151
157
|
depth_first_each do |keys, value|
|
@@ -153,21 +159,14 @@ module Dao
|
|
153
159
|
key = keys.join('.')
|
154
160
|
value = value.to_s
|
155
161
|
next if value.strip.empty?
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
full_messages.sort! do |a,b|
|
160
|
-
a, b = a.first, b.first
|
161
|
-
if a == Global
|
162
|
-
b == Global ? 0 : -1
|
163
|
-
elsif b == Global
|
164
|
-
a == Global ? 0 : 1
|
162
|
+
if key == Global
|
163
|
+
global_messages.push([key, value])
|
165
164
|
else
|
166
|
-
|
165
|
+
full_messages.push([key, value])
|
167
166
|
end
|
168
167
|
end
|
169
168
|
|
170
|
-
full_messages
|
169
|
+
global_messages + full_messages
|
171
170
|
end
|
172
171
|
|
173
172
|
def each_message
|
@@ -208,27 +207,25 @@ module Dao
|
|
208
207
|
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
209
208
|
errors = [error, *args].flatten.compact
|
210
209
|
|
211
|
-
|
210
|
+
at_least_one_error = false
|
212
211
|
css_class = options[:class] || 'dao errors'
|
213
212
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
html.push("</tr>")
|
213
|
+
to_html =
|
214
|
+
table_(:class => css_class){
|
215
|
+
caption_{ "We're so sorry, but can you please fix the following errors?" }
|
216
|
+
errors.each do |e|
|
217
|
+
e.full_messages.each do |key, message|
|
218
|
+
at_least_one_error = true
|
219
|
+
tr_{
|
220
|
+
td_(:class => :key){ key }
|
221
|
+
td_(:class => :separator){ Separator }
|
222
|
+
td_(:class => :message){ message }
|
223
|
+
}
|
224
|
+
end
|
227
225
|
end
|
228
|
-
|
229
|
-
html.push("</table>")
|
226
|
+
}
|
230
227
|
|
231
|
-
|
228
|
+
at_least_one_error ? to_html : ''
|
232
229
|
end
|
233
230
|
|
234
231
|
def to_s(*args, &block)
|
data/lib/dao/form.rb
CHANGED
@@ -24,18 +24,18 @@ module Dao
|
|
24
24
|
super
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
result.
|
29
|
-
end
|
30
|
-
|
31
|
-
def ==(other)
|
32
|
-
params.object_id == other.params.object_id
|
27
|
+
def data
|
28
|
+
result.data
|
33
29
|
end
|
34
30
|
|
35
31
|
def errors
|
36
32
|
result.errors
|
37
33
|
end
|
38
34
|
|
35
|
+
def ==(other)
|
36
|
+
result == other.result
|
37
|
+
end
|
38
|
+
|
39
39
|
def form(*args, &block)
|
40
40
|
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
41
41
|
keys = args.flatten
|
@@ -87,9 +87,9 @@ module Dao
|
|
87
87
|
|
88
88
|
value =
|
89
89
|
if block.nil? and !options.has_key?(:value)
|
90
|
-
value_for(
|
90
|
+
value_for(data, keys)
|
91
91
|
else
|
92
|
-
block ? block.call(
|
92
|
+
block ? block.call(data.get(keys)) : options.delete(:value)
|
93
93
|
end
|
94
94
|
|
95
95
|
input_(options_for(options, :type => type, :name => name, :value => value, :class => klass, :id => id, :data_error => error)){}
|
@@ -115,9 +115,9 @@ module Dao
|
|
115
115
|
|
116
116
|
value =
|
117
117
|
if block.nil? and !options.has_key?(:value)
|
118
|
-
value_for(
|
118
|
+
value_for(data, keys)
|
119
119
|
else
|
120
|
-
block ? block.call(
|
120
|
+
block ? block.call(data.get(keys)) : options.delete(:value)
|
121
121
|
end
|
122
122
|
|
123
123
|
button_(options_for(options, :type => type, :name => name, :value => value, :class => klass, :id => id, :data_error => error)){}
|
@@ -141,12 +141,12 @@ module Dao
|
|
141
141
|
|
142
142
|
value =
|
143
143
|
if block.nil? and !options.has_key?(:value)
|
144
|
-
value_for(
|
144
|
+
value_for(data, keys)
|
145
145
|
else
|
146
|
-
block ? block.call(
|
146
|
+
block ? block.call(data.get(keys)) : options.delete(:value)
|
147
147
|
end
|
148
148
|
|
149
|
-
textarea_(options_for(options, :name => name, :class => klass, :id => id, :data_error => error)){ value }
|
149
|
+
textarea_(options_for(options, :name => name, :class => klass, :id => id, :data_error => error)){ value.to_s }
|
150
150
|
end
|
151
151
|
|
152
152
|
def select(*args, &block)
|
@@ -154,27 +154,51 @@ module Dao
|
|
154
154
|
keys = args.flatten
|
155
155
|
|
156
156
|
name = options.delete(:name) || name_for(keys)
|
157
|
-
from = options.delete(:from) || options.delete(:
|
157
|
+
from = options.delete(:from) || options.delete(:options)
|
158
158
|
|
159
159
|
id = options.delete(:id) || id_for(keys)
|
160
160
|
klass = class_for(keys, options.delete(:class))
|
161
161
|
error = error_for(keys, options.delete(:error))
|
162
162
|
|
163
|
+
block ||= lambda{|pair| pair = Array(pair).flatten.compact; [pair.first, pair.last, selected=nil]}
|
164
|
+
|
165
|
+
if from.nil?
|
166
|
+
key = keys.map{|key| "#{ key }"}
|
167
|
+
key.last << "_options"
|
168
|
+
from = data.get(*key) if data.has?(*key)
|
169
|
+
end
|
170
|
+
|
171
|
+
list = Array(from)
|
172
|
+
|
173
|
+
|
174
|
+
case list.first
|
175
|
+
when Hash, Array
|
176
|
+
nil
|
177
|
+
else
|
178
|
+
list.flatten!
|
179
|
+
list.compact!
|
180
|
+
list.map!{|element| [element, element]}
|
181
|
+
end
|
182
|
+
|
183
|
+
selected_value = value_for(data, keys)
|
184
|
+
|
163
185
|
select_(options_for(options, :name => name, :class => klass, :id => id, :data_error => error)){
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
case result
|
186
|
+
list.each do |pair|
|
187
|
+
returned = block.call(pair)
|
188
|
+
case returned
|
168
189
|
when Array
|
169
|
-
value, content, selected, *ignored =
|
190
|
+
value, content, selected, *ignored = returned
|
170
191
|
when Hash
|
171
|
-
value =
|
172
|
-
content =
|
173
|
-
selected =
|
192
|
+
value = returned[:value]
|
193
|
+
content = returned[:content] || value
|
194
|
+
selected = returned[:selected]
|
174
195
|
else
|
175
|
-
value =
|
176
|
-
content =
|
177
|
-
selected =
|
196
|
+
value = returned
|
197
|
+
content = returned
|
198
|
+
selected = nil
|
199
|
+
end
|
200
|
+
if selected.nil?
|
201
|
+
selected = value.to_s==selected_value.to_s
|
178
202
|
end
|
179
203
|
opts = {:value => value}
|
180
204
|
opts[:selected] = !!selected if selected
|
@@ -204,9 +228,9 @@ module Dao
|
|
204
228
|
end
|
205
229
|
end
|
206
230
|
|
207
|
-
def value_for(
|
208
|
-
return nil unless
|
209
|
-
value = Tagz.escapeHTML(
|
231
|
+
def value_for(data, keys)
|
232
|
+
return nil unless data.has?(keys)
|
233
|
+
value = Tagz.escapeHTML(data.get(keys))
|
210
234
|
end
|
211
235
|
|
212
236
|
def name_for(keys)
|