dao 2.0.0 → 2.1.0
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 +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)
|