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
data/lib/dao/form.rb
CHANGED
@@ -60,19 +60,19 @@ module Dao
|
|
60
60
|
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
61
61
|
keys = args.flatten
|
62
62
|
|
63
|
-
name = options.delete(:name) || keys.last
|
64
63
|
id = options.delete(:id) || id_for(keys)
|
65
64
|
klass = class_for(keys, options.delete(:class))
|
66
65
|
error = error_for(keys, options.delete(:error))
|
66
|
+
target = options.delete(:for) || id_for(keys)
|
67
67
|
|
68
68
|
content =
|
69
|
-
if block.nil? and !options.has_key?(:content)
|
70
|
-
|
69
|
+
if block.nil? and !options.has_key?(:content)
|
70
|
+
humanize(keys.last)
|
71
71
|
else
|
72
72
|
block ? block.call() : options.delete(:content)
|
73
73
|
end
|
74
74
|
|
75
|
-
label_(options_for(options, :
|
75
|
+
label_(options_for(options, :for => target, :class => klass, :id => id, :data_error => error)){ content }
|
76
76
|
end
|
77
77
|
|
78
78
|
def input(*args, &block)
|
@@ -97,10 +97,14 @@ module Dao
|
|
97
97
|
|
98
98
|
def submit(*args, &block)
|
99
99
|
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
100
|
+
|
101
|
+
content = block ? block.call : (args.first || 'Submit')
|
102
|
+
|
103
|
+
options[:name] ||= :submit
|
104
|
+
options[:type] ||= :submit
|
105
|
+
options[:value] ||= content
|
106
|
+
|
107
|
+
input_(options_for(options)){}
|
104
108
|
end
|
105
109
|
|
106
110
|
def button(*args)
|
@@ -156,6 +160,13 @@ module Dao
|
|
156
160
|
name = options.delete(:name) || name_for(keys)
|
157
161
|
from = options.delete(:from) || options.delete(:options)
|
158
162
|
|
163
|
+
selected =
|
164
|
+
if options.has_key?(:selected)
|
165
|
+
options.delete(:selected)
|
166
|
+
else
|
167
|
+
value_for(data, keys)
|
168
|
+
end
|
169
|
+
|
159
170
|
id = options.delete(:id) || id_for(keys)
|
160
171
|
klass = class_for(keys, options.delete(:class))
|
161
172
|
error = error_for(keys, options.delete(:error))
|
@@ -170,7 +181,6 @@ module Dao
|
|
170
181
|
|
171
182
|
list = Array(from)
|
172
183
|
|
173
|
-
|
174
184
|
case list.first
|
175
185
|
when Hash, Array
|
176
186
|
nil
|
@@ -180,11 +190,21 @@ module Dao
|
|
180
190
|
list.map!{|element| [element, element]}
|
181
191
|
end
|
182
192
|
|
183
|
-
selected_value =
|
193
|
+
selected_value =
|
194
|
+
case selected
|
195
|
+
when Array
|
196
|
+
selected.first
|
197
|
+
when Hash
|
198
|
+
key = [:id, 'id', :value, 'value'].detect{|k| selected.has_key?(k)}
|
199
|
+
key ? selected[key] : selected
|
200
|
+
else
|
201
|
+
selected
|
202
|
+
end
|
184
203
|
|
185
204
|
select_(options_for(options, :name => name, :class => klass, :id => id, :data_error => error)){
|
186
205
|
list.each do |pair|
|
187
206
|
returned = block.call(pair)
|
207
|
+
|
188
208
|
case returned
|
189
209
|
when Array
|
190
210
|
value, content, selected, *ignored = returned
|
@@ -197,9 +217,11 @@ module Dao
|
|
197
217
|
content = returned
|
198
218
|
selected = nil
|
199
219
|
end
|
220
|
+
|
200
221
|
if selected.nil?
|
201
222
|
selected = value.to_s==selected_value.to_s
|
202
223
|
end
|
224
|
+
|
203
225
|
opts = {:value => value}
|
204
226
|
opts[:selected] = !!selected if selected
|
205
227
|
option_(opts){ content }
|
@@ -233,8 +255,24 @@ module Dao
|
|
233
255
|
value = Tagz.escapeHTML(data.get(keys))
|
234
256
|
end
|
235
257
|
|
258
|
+
def Form.name_for(path, *keys)
|
259
|
+
path = Path.new(path) unless path.is_a?(Path)
|
260
|
+
"#{ path }(#{ Array(keys).flatten.compact.join(',') })"
|
261
|
+
end
|
262
|
+
|
263
|
+
def Form.name_re_for(path)
|
264
|
+
path = Path.new(path) unless path.is_a?(Path)
|
265
|
+
Regexp.new(/^#{ Regexp.escape(path) }/)
|
266
|
+
end
|
267
|
+
|
268
|
+
def Form.encoded?(path, params)
|
269
|
+
name_re = Form.name_re_for(path)
|
270
|
+
params.keys.any?{|key| name_re =~ key.to_s}
|
271
|
+
end
|
272
|
+
|
273
|
+
|
236
274
|
def name_for(keys)
|
237
|
-
|
275
|
+
Form.name_for(result.path, keys)
|
238
276
|
end
|
239
277
|
|
240
278
|
def options_for(*hashes)
|
@@ -256,5 +294,11 @@ module Dao
|
|
256
294
|
def attr_for(string)
|
257
295
|
slug_for(string).gsub(/_/, '-')
|
258
296
|
end
|
297
|
+
|
298
|
+
def humanize(string)
|
299
|
+
string = string.to_s
|
300
|
+
string = string.humanize if string.respond_to?(:humanize)
|
301
|
+
string
|
302
|
+
end
|
259
303
|
end
|
260
304
|
end
|
data/lib/dao/params.rb
CHANGED
@@ -3,9 +3,11 @@ module Dao
|
|
3
3
|
include Dao::InstanceExec
|
4
4
|
|
5
5
|
class << Params
|
6
|
-
def parse(prefix, params = {})
|
6
|
+
def parse(prefix, params = {}, options = {})
|
7
7
|
prefix = prefix.to_s
|
8
|
-
params = Map.new(params)
|
8
|
+
params = Map.new(params || {})
|
9
|
+
base = Map.new(params || {})
|
10
|
+
options = Map.options(options || {})
|
9
11
|
parsed = Params.new
|
10
12
|
parsed.update(params[prefix]) if params.has_key?(prefix)
|
11
13
|
|
@@ -17,11 +19,44 @@ module Dao
|
|
17
19
|
matched, keys = re.match(key).to_a
|
18
20
|
next unless matched
|
19
21
|
next unless keys
|
20
|
-
keys = keys
|
22
|
+
keys = keys_for(keys)
|
21
23
|
parsed.set(keys => value)
|
24
|
+
base.delete(key)
|
22
25
|
end
|
23
26
|
|
24
|
-
|
27
|
+
whitelist = Set.new( [options.getopt([:include, :select, :only])].flatten.compact.map{|k| k.to_s} )
|
28
|
+
blacklist = Set.new( [options.getopt([:exclude, :reject, :except])].flatten.compact.map{|k| k.to_s} )
|
29
|
+
|
30
|
+
unless blacklist.empty?
|
31
|
+
base.keys.dup.each do |key|
|
32
|
+
base.delete(key) if blacklist.include?(key.to_s)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
unless whitelist.empty?
|
37
|
+
base.keys.dup.each do |key|
|
38
|
+
base.delete(key) unless whitelist.include?(key.to_s)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if options.getopt(:fold, default=true)
|
43
|
+
parsed_and_folded = base.merge(parsed)
|
44
|
+
else
|
45
|
+
parsed
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def keys_for(keys)
|
50
|
+
keys.strip.split(%r/\s*,\s*/).map{|key| key =~ %r/^\d+$/ ? Integer(key) : key}
|
51
|
+
end
|
52
|
+
|
53
|
+
def process(path, params, options = {})
|
54
|
+
return params if params.is_a?(Params)
|
55
|
+
|
56
|
+
parsed = Params.parse(path, params, options)
|
57
|
+
return parsed unless parsed.empty?
|
58
|
+
|
59
|
+
return Params.new(params)
|
25
60
|
end
|
26
61
|
end
|
27
62
|
|
@@ -82,52 +117,7 @@ module Dao
|
|
82
117
|
end
|
83
118
|
end
|
84
119
|
|
85
|
-
def Dao.parse(
|
86
|
-
|
87
|
-
|
88
|
-
smells_like_unparsed_rails_params = (
|
89
|
-
params.has_key?(:action) and
|
90
|
-
params.has_key?(:controller) and
|
91
|
-
params.class.name =~ /HashWithIndifferentAccess/
|
92
|
-
)
|
93
|
-
return(Params.parse(path.to_s, params)) if smells_like_unparsed_rails_params
|
94
|
-
|
95
|
-
smells_like_unparsed_params = false
|
96
|
-
unparsed_re = %r/^#{ Regexp.escape(path.to_s) }/
|
97
|
-
params.each do |key, val|
|
98
|
-
if key =~ unparsed_re
|
99
|
-
smells_like_unparsed_params = true
|
100
|
-
break
|
101
|
-
end
|
102
|
-
end
|
103
|
-
return(Params.parse(path.to_s, params)) if smells_like_unparsed_params
|
104
|
-
|
105
|
-
return Params.new(params)
|
106
|
-
end
|
107
|
-
|
108
|
-
=begin
|
109
|
-
def Dao.parse(path, params)
|
110
|
-
returned = Params.new(params)
|
111
|
-
|
112
|
-
smells_like_rails = (params.has_key?(:action) and params.has_key?(:controller) and params.class.name =~ /HashWithIndifferentAccess/)
|
113
|
-
if smells_like_rails
|
114
|
-
returned.delete(:action)
|
115
|
-
returned.delete(:controller)
|
116
|
-
returned.update(Params.parse(path, params))
|
117
|
-
return returned
|
118
|
-
end
|
119
|
-
|
120
|
-
re = %r/^#{ Regexp.escape(path) }/
|
121
|
-
smells_like_unparsed = false
|
122
|
-
params.each do |key, val|
|
123
|
-
break(smells_like_unparsed = true) if key =~ re
|
124
|
-
end
|
125
|
-
if smells_like_unparsed
|
126
|
-
returned.update(Params.parse(path, params))
|
127
|
-
return returned
|
128
|
-
end
|
129
|
-
|
130
|
-
return returned
|
120
|
+
def Dao.parse(*args, &block)
|
121
|
+
Params.process(*args, &block)
|
131
122
|
end
|
132
|
-
=end
|
133
123
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Dao
|
2
|
+
class Presenter
|
3
|
+
include Tagz.globally
|
4
|
+
|
5
|
+
class << Presenter
|
6
|
+
def for(*args, &block)
|
7
|
+
new(*args, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :result
|
12
|
+
attr_accessor :formats
|
13
|
+
|
14
|
+
def initialize(*args, &block)
|
15
|
+
@result = args.shift if args.first.is_a?(Result)
|
16
|
+
@formats = Map.new
|
17
|
+
end
|
18
|
+
|
19
|
+
%w( set get has has? [] ).each do |method|
|
20
|
+
module_eval <<-__, __FILE__, __LINE__
|
21
|
+
def #{ method }(*args, &block)
|
22
|
+
data.#{ method }(*args, &block)
|
23
|
+
end
|
24
|
+
__
|
25
|
+
end
|
26
|
+
|
27
|
+
def extend(*args, &block)
|
28
|
+
return super if block.nil?
|
29
|
+
singleton_class =
|
30
|
+
class << self
|
31
|
+
self
|
32
|
+
end
|
33
|
+
singleton_class.module_eval(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def tag(*args, &block)
|
37
|
+
options = Dao.options_for!(args)
|
38
|
+
args.push(:div) if args.empty?
|
39
|
+
tagname = args.shift
|
40
|
+
keys = args
|
41
|
+
|
42
|
+
tag_method = "#{ tagname }_"
|
43
|
+
|
44
|
+
id = options.delete(:id) || id_for(keys)
|
45
|
+
klass = class_for(keys, options.delete(:class))
|
46
|
+
error = error_for(keys, options.delete(:error))
|
47
|
+
|
48
|
+
tag_options =
|
49
|
+
options_for(options, :id => id, :class => klass, :data_error => error)
|
50
|
+
|
51
|
+
value = value_for(keys)
|
52
|
+
tag_value = instance_exec(value, &format_for(keys))
|
53
|
+
|
54
|
+
send(tag_method, tag_options){ tag_value }
|
55
|
+
end
|
56
|
+
|
57
|
+
include InstanceExec
|
58
|
+
|
59
|
+
DefaultFormat = lambda{|value| value}
|
60
|
+
|
61
|
+
def format_for(keys)
|
62
|
+
formats.get(keys) || DefaultFormat
|
63
|
+
end
|
64
|
+
|
65
|
+
def format(list_of_keys, &block)
|
66
|
+
Array(list_of_keys).each do |keys|
|
67
|
+
formats.set(keys, block)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def data
|
72
|
+
result.data
|
73
|
+
end
|
74
|
+
|
75
|
+
def errors
|
76
|
+
result.errors
|
77
|
+
end
|
78
|
+
|
79
|
+
def ==(other)
|
80
|
+
result == other.result
|
81
|
+
end
|
82
|
+
|
83
|
+
def id_for(keys)
|
84
|
+
id = [result.path, keys.join('-')].compact.join('_')
|
85
|
+
slug_for(id)
|
86
|
+
end
|
87
|
+
|
88
|
+
def class_for(keys, klass = nil)
|
89
|
+
klass =
|
90
|
+
if result.errors.on?(keys)
|
91
|
+
[klass, 'dao', 'errors'].compact.join(' ')
|
92
|
+
else
|
93
|
+
[klass, 'dao'].compact.join(' ')
|
94
|
+
end
|
95
|
+
klass
|
96
|
+
end
|
97
|
+
|
98
|
+
def error_for(keys, klass = nil)
|
99
|
+
if result.errors.on?(keys)
|
100
|
+
result.errors.get(keys)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def value_for(keys)
|
105
|
+
return nil unless data.has?(keys)
|
106
|
+
value = Tagz.escapeHTML(data.get(keys))
|
107
|
+
end
|
108
|
+
|
109
|
+
def options_for(*hashes)
|
110
|
+
map = Map.new
|
111
|
+
hashes.flatten.each do |h|
|
112
|
+
h.each{|k,v| map[attr_for(k)] = v unless v.nil?}
|
113
|
+
end
|
114
|
+
map
|
115
|
+
end
|
116
|
+
|
117
|
+
def slug_for(string)
|
118
|
+
string = string.to_s
|
119
|
+
words = string.to_s.scan(%r/\w+/)
|
120
|
+
words.map!{|word| word.gsub(%r/[^0-9a-zA-Z_:-]/, '')}
|
121
|
+
words.delete_if{|word| word.nil? or word.strip.empty?}
|
122
|
+
words.join('-').downcase.sub(/_+$/, '')
|
123
|
+
end
|
124
|
+
|
125
|
+
def attr_for(string)
|
126
|
+
slug_for(string).gsub(/_/, '-')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/dao/rails.rb
CHANGED
@@ -7,12 +7,60 @@ if defined?(Rails)
|
|
7
7
|
### ref: https://gist.github.com/af7e572c2dc973add221
|
8
8
|
|
9
9
|
paths.path = ROOT_DIR
|
10
|
+
|
10
11
|
### config.autoload_paths << APP_DIR
|
11
12
|
### $LOAD_PATH.push(File.join(Rails.root.to_s, 'app'))
|
12
|
-
|
13
13
|
#config.after_initialize do
|
14
14
|
#unloadable(Dao)
|
15
15
|
#end
|
16
|
+
|
17
|
+
|
18
|
+
# yes yes, this should probably be somewhere else...
|
19
|
+
#
|
20
|
+
config.after_initialize do
|
21
|
+
|
22
|
+
ActionController::Base.module_eval do
|
23
|
+
|
24
|
+
# you will likely want to override this!
|
25
|
+
#
|
26
|
+
def current_api
|
27
|
+
@api ||= (
|
28
|
+
api = Api.new
|
29
|
+
%w( real_user effective_user current_user ).each do |attr|
|
30
|
+
getter = "#{ attr }"
|
31
|
+
setter = "#{ attr }="
|
32
|
+
if respond_to?(getter) and api.respond_to?(setter)
|
33
|
+
api.send(setter, send(getter))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
api
|
37
|
+
)
|
38
|
+
end
|
39
|
+
helper_method(:current_api)
|
40
|
+
alias_method(:api, :current_api)
|
41
|
+
helper_method(:api)
|
42
|
+
|
43
|
+
# setup sane rescuing from dao errors with crap statuses
|
44
|
+
#
|
45
|
+
rescue_from(Dao::Error::Result) do |error|
|
46
|
+
result = error.result
|
47
|
+
basename = "#{ result.status.code }.html"
|
48
|
+
error_page = File.join(Rails.root, 'public', basename)
|
49
|
+
|
50
|
+
if test(?e, error_page)
|
51
|
+
file = File.expand_path(error_page)
|
52
|
+
status = result.status.code
|
53
|
+
render(:file => file, :status => status, :layout => false)
|
54
|
+
else
|
55
|
+
text = result.status.to_s
|
56
|
+
status = result.status.code
|
57
|
+
render(:text => text, :status => status, :layout => false)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
16
64
|
end
|
17
65
|
end
|
18
66
|
end
|
@@ -12,8 +12,7 @@ class DaoGenerator < Rails::Generators::NamedBase
|
|
12
12
|
|
13
13
|
copy_file("dao.css", "public/stylesheets/dao.css")
|
14
14
|
|
15
|
-
route("match 'api/*path' => 'api#
|
16
|
-
route("match 'api' => 'api#index', :as => 'api_index'")
|
15
|
+
route("match 'api(/*path)' => 'api#index', :as => 'api'")
|
17
16
|
|
18
17
|
gem("yajl-ruby")
|
19
18
|
|