dao 4.2.1 → 4.4.2

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.
Files changed (78) hide show
  1. data/README +103 -63
  2. data/Rakefile +3 -3
  3. data/dao.gemspec +27 -16
  4. data/lib/dao.rb +17 -17
  5. data/lib/dao/active_record.rb +1 -0
  6. data/lib/dao/api.rb +2 -1
  7. data/lib/dao/api/{endpoints.rb → call.rb} +1 -0
  8. data/lib/dao/api/context.rb +2 -0
  9. data/lib/dao/api/dsl.rb +1 -0
  10. data/lib/dao/api/initializers.rb +1 -0
  11. data/lib/dao/api/modes.rb +1 -0
  12. data/lib/dao/api/routes.rb +1 -0
  13. data/lib/dao/blankslate.rb +1 -0
  14. data/lib/dao/conducer.rb +315 -274
  15. data/lib/dao/conducer/active_model.rb +98 -0
  16. data/lib/dao/conducer/attributes.rb +1 -0
  17. data/lib/dao/conducer/autocrud.rb +58 -0
  18. data/lib/dao/conducer/callback_support.rb +20 -0
  19. data/lib/dao/conducer/collection.rb +45 -0
  20. data/lib/dao/conducer/controller_support.rb +104 -0
  21. data/lib/dao/conducer/nav_support.rb +9 -0
  22. data/lib/dao/conducer/view_support.rb +16 -0
  23. data/lib/dao/data.rb +2 -1
  24. data/lib/dao/db.rb +2 -0
  25. data/lib/dao/endpoint.rb +1 -0
  26. data/lib/dao/engine.rb +1 -0
  27. data/lib/dao/errors.rb +109 -99
  28. data/lib/dao/exceptions.rb +1 -0
  29. data/lib/dao/extractor.rb +1 -0
  30. data/lib/dao/form.rb +175 -20
  31. data/lib/dao/instance_exec.rb +1 -0
  32. data/lib/dao/mode.rb +1 -0
  33. data/lib/dao/mongo_mapper.rb +1 -0
  34. data/lib/dao/name.rb +1 -0
  35. data/lib/dao/params.rb +2 -1
  36. data/lib/dao/path.rb +1 -0
  37. data/lib/dao/path_map.rb +24 -0
  38. data/lib/dao/rack.rb +1 -0
  39. data/lib/dao/rack/middleware.rb +1 -0
  40. data/lib/dao/rack/middleware/params_parser.rb +1 -0
  41. data/lib/dao/rails.rb +12 -32
  42. data/lib/dao/rails/lib/generators/dao/USAGE +2 -2
  43. data/lib/dao/rails/lib/generators/dao/dao_generator.rb +8 -27
  44. data/lib/dao/rails/lib/generators/dao/templates/api.rb +2 -1
  45. data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +22 -20
  46. data/lib/dao/rails/lib/generators/dao/templates/conducer.rb +49 -43
  47. data/lib/dao/rails/lib/generators/dao/templates/dao.css +26 -25
  48. data/lib/dao/rails/lib/generators/dao/templates/dao.js +3 -0
  49. data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +58 -45
  50. data/lib/dao/result.rb +50 -1
  51. data/lib/dao/route.rb +1 -0
  52. data/lib/dao/slug.rb +12 -36
  53. data/lib/dao/status.rb +91 -7
  54. data/lib/dao/stdext.rb +1 -0
  55. data/lib/dao/support.rb +90 -80
  56. data/lib/dao/upload.rb +396 -0
  57. data/lib/dao/validations.rb +23 -5
  58. data/lib/dao/validations/callback.rb +5 -0
  59. data/lib/dao/validations/common.rb +100 -3
  60. data/lib/dao/validations/instance.rb +17 -0
  61. data/lib/dao/validations/validator.rb +192 -91
  62. data/test/active_model_conducer_lint_test.rb +1 -0
  63. data/test/api_test.rb +15 -0
  64. data/test/conducer_test.rb +608 -90
  65. data/test/data/han-solo.jpg +0 -0
  66. data/test/form_test.rb +1 -0
  67. data/test/helper.rb +1 -0
  68. data/test/leak.rb +1 -0
  69. data/test/support_test.rb +4 -1
  70. data/test/testing.rb +1 -0
  71. data/test/validations_test.rb +176 -30
  72. metadata +120 -131
  73. data/b.rb +0 -38
  74. data/lib/dao/conducer/crud.rb +0 -70
  75. data/lib/dao/current.rb +0 -66
  76. data/lib/dao/image_cache.rb +0 -193
  77. data/lib/dao/rails/lib/generators/dao/templates/conducer_controller.rb +0 -79
  78. data/test/db.yml +0 -9
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Dao
2
3
  module Validations
3
4
  class Callback < ::Proc
@@ -24,6 +25,10 @@ module Dao
24
25
  def each(&block)
25
26
  @chain.each(&block)
26
27
  end
28
+
29
+ def each_with_index(&block)
30
+ @chain.each_with_index(&block)
31
+ end
27
32
  end
28
33
  end
29
34
  end
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Dao
2
3
  module Validations
3
4
  module Common
@@ -10,8 +11,13 @@ module Dao
10
11
  options[:minimum] = options[:in].begin
11
12
  options[:maximum] = options[:in].end
12
13
  end
13
- minimum = options[:minimum] || 1
14
- maximum = options[:maximum]
14
+ minimum = options[:minimum] || options[:min] || 1
15
+ maximum = options[:maximum] || options[:max]
16
+
17
+ exact = options[:value] || options[:exact] || options[:exactly] || options[:is]
18
+ if exact
19
+ minimum = maximum = exact
20
+ end
15
21
 
16
22
  too_short = options[:too_short] || message || 'is too short'
17
23
  too_long = options[:too_long] || message || 'is too long'
@@ -31,7 +37,7 @@ module Dao
31
37
  throw(:validation, m)
32
38
  end
33
39
 
34
- value = value.to_s.strip
40
+ value = value.to_s.strip unless(value.respond_to?(:empty?) and value.respond_to?(:size))
35
41
 
36
42
  if value.empty? and allow_blank
37
43
  m[:valid] = true
@@ -56,6 +62,8 @@ module Dao
56
62
  validates(*args, &block)
57
63
  end
58
64
 
65
+ alias_method('validates_size_of', 'validates_length_of')
66
+
59
67
  def validates_word_count_of(*args)
60
68
  options = Map.options_for!(args)
61
69
 
@@ -113,6 +121,95 @@ module Dao
113
121
  validates(*args, &block)
114
122
  end
115
123
 
124
+ def validates_inclusion_of(*args)
125
+ options = Map.options_for!(args)
126
+
127
+ list = Array(options[:in])
128
+ message = options[:message]
129
+ message ||= "not in #{ list.map{|item| item.inspect}.join(' | ') }"
130
+
131
+ allow_nil = options[:allow_nil]
132
+ allow_blank = options[:allow_blank]
133
+
134
+ block =
135
+ lambda do |value|
136
+ m = Map(:valid => true)
137
+
138
+ if value.nil? and allow_nil
139
+ m[:valid] = true
140
+ throw(:validation, m)
141
+ end
142
+
143
+ value = value.to_s.strip
144
+
145
+ if value.empty? and allow_blank
146
+ m[:valid] = true
147
+ throw(:validation, m)
148
+ end
149
+
150
+ unless list.any?{|item| item === value}
151
+ m[:message] = message
152
+ m[:valid] = false
153
+ throw(:validation, m)
154
+ end
155
+
156
+ m
157
+ end
158
+
159
+ validates(*args, &block)
160
+ end
161
+
162
+ def validates_case_of(*args)
163
+ options = Map.options_for!(args)
164
+
165
+ target = [:case, :target, :is, :as].map{|k| options.getopt(k)}.compact.first
166
+ message = options[:message] || "is not a case of #{ target.inspect }"
167
+
168
+ allow_nil = options[:allow_nil]
169
+ allow_blank = options[:allow_blank]
170
+
171
+ block =
172
+ lambda do |value|
173
+ m = Map(:valid => true)
174
+
175
+ if value.nil? and allow_nil
176
+ m[:valid] = true
177
+ throw(:validation, m)
178
+ end
179
+
180
+ if [value].join.strip.empty? and allow_blank
181
+ m[:valid] = true
182
+ throw(:validation, m)
183
+ end
184
+
185
+ unless target === value
186
+ m[:message] = message
187
+ m[:valid] = false
188
+ throw(:validation, m)
189
+ end
190
+
191
+ m
192
+ end
193
+
194
+ validates(*args, &block)
195
+ end
196
+
197
+ def validates_type_of(*args)
198
+ options = Map.options_for(args)
199
+ target = [:case, :target, :is, :as].map{|k| options.getopt(k)}.compact.first
200
+ options[:message] ||= "is not of type #{ target.inspect }"
201
+ args.push(options)
202
+ validates_case_of(*args)
203
+ end
204
+
205
+ def validates_value_of(*args)
206
+ options = Map.options_for(args)
207
+ target = [:case, :target, :is, :as].map{|k| options.getopt(k)}.compact.first
208
+ options[:message] ||= "does not have the value #{ target.inspect }"
209
+ args.push(options)
210
+ validates_case_of(*args)
211
+ end
212
+
116
213
  def validates_as_email(*args)
117
214
  options = Map.options_for!(args)
118
215
 
@@ -0,0 +1,17 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Dao
3
+ module Validations
4
+ class Instance < ::Map
5
+ end
6
+
7
+ Instance.send(:include, Validations)
8
+
9
+ def Validations.new(*args, &block)
10
+ Instance.new(*args, &block)
11
+ end
12
+
13
+ def Validations.for(*args, &block)
14
+ Instance.new(*args, &block)
15
+ end
16
+ end
17
+ end
@@ -1,113 +1,230 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Dao
2
3
  module Validations
3
4
  class Validator
4
- NotBlank = lambda{|value| !value.to_s.strip.empty?} unless defined?(NotBlank)
5
+ NotBlank = proc{|value| !value.to_s.strip.empty?} unless defined?(NotBlank)
5
6
  Cleared = 'Cleared'.freeze unless defined?(Cleared)
6
7
 
8
+ include Common
9
+
10
+ def validator
11
+ self
12
+ end
13
+
14
+ def validator=(validator)
15
+ raise NotImplementedError
16
+ end
17
+
7
18
  class << Validator
8
- def for(*args, &block)
9
- new(*args, &block)
19
+ def mixin(*args, &block)
20
+ new(*args, &block).tap{|validator| validator.mixin = true}
10
21
  end
11
22
  end
12
23
 
13
24
  attr_accessor :object
25
+ attr_accessor :options
14
26
  attr_accessor :validations
15
27
  attr_accessor :errors
16
28
  attr_accessor :status
17
29
 
18
- def initialize(object)
19
- @object = object
20
- @validations = Map.new
21
- @errors = Errors.new
22
- @status = Status.new
30
+ fattr(:attributes){ extract_attributes! }
31
+ alias_method(:data, :attributes)
32
+
33
+ fattr(:mixin){ false }
34
+
35
+ def initialize(*args, &block)
36
+ @object = args.shift
37
+ @options = Map.options_for(args)
38
+
39
+ if args.size == 1 and @object and @object.is_a?(Hash)
40
+ object_keys = @object.keys.map{|key| key.to_s}
41
+ option_keys = %w( object validations errors status )
42
+
43
+ object_is_options = !object_keys.empty? && (object_keys - option_keys).empty?
44
+
45
+ if object_is_options
46
+ @options = Map.for(@object)
47
+ @object = nil
48
+ end
49
+ end
50
+
51
+ @object ||= (@options[:object] || Map.new)
52
+ @validations ||= (@options[:validations] || Map.new)
53
+ @errors ||= (@options[:errors] || Errors.new)
54
+ @status ||= (@options[:status] || Status.new)
55
+
56
+ unless @object.respond_to?(:validator)
57
+ @object.send(:extend, Dao::Validations)
58
+ @object.validator = self
59
+ end
60
+
61
+ #@object.extend(InstanceExec) unless @object.respond_to?(:instance_exec)
23
62
  end
24
63
 
25
- fattr(:attributes) do
64
+ def extract_attributes!(object = @object)
26
65
  attributes =
27
66
  catch(:attributes) do
28
- if @object.respond_to?(:attributes)
29
- throw :attributes, @object.attributes
67
+ if object.respond_to?(:attributes)
68
+ throw :attributes, object.attributes
30
69
  end
31
- if @object.instance_variable_defined?('@attributes')
32
- throw :attributes, @object.instance_variable_get('@attributes')
70
+ if object.instance_variable_defined?('@attributes')
71
+ throw :attributes, object.instance_variable_get('@attributes')
33
72
  end
34
- if @object.is_a?(Map)
35
- throw :attributes, @object
73
+ if object.is_a?(Map)
74
+ throw :attributes, object
36
75
  end
37
- if @object.respond_to?(:to_map)
38
- throw :attributes, Map.new(@object.to_map)
76
+ if object.respond_to?(:to_map)
77
+ throw :attributes, Map.new(object.to_map)
39
78
  end
40
- if @object.is_a?(Hash)
41
- throw :attributes, Map.new(@object)
79
+ if object.is_a?(Hash)
80
+ throw :attributes, Map.new(object)
42
81
  end
43
- if @object.respond_to?(:to_hash)
44
- throw :attributes, Map.new(@object.to_hash)
82
+ if object.respond_to?(:to_hash)
83
+ throw :attributes, Map.new(object.to_hash)
45
84
  end
46
- raise ArgumentError.new("found no attributes on #{ @object.inspect }(#{ @object.class.name })")
85
+ raise ArgumentError.new("found no attributes on #{ object.inspect }(#{ object.class.name })")
47
86
  end
48
87
 
49
- case attributes
50
- when Map
51
- attributes
52
- when Hash
53
- Map.new(attributes)
54
- else
55
- raise(ArgumentError.new("#{ attributes.inspect } (#{ attributes.class })"))
56
- end
88
+ @attributes =
89
+ case attributes
90
+ when Map
91
+ attributes
92
+ when Hash
93
+ Map.new(attributes)
94
+ else
95
+ raise(ArgumentError.new("#{ attributes.inspect } (#{ attributes.class })"))
96
+ end
97
+
98
+ @attributes
57
99
  end
58
100
 
59
- def add(*args, &block)
60
- options = Map.options_for!(args)
101
+ def validates(*args, &block)
61
102
  block = args.pop if args.last.respond_to?(:call)
62
103
  block ||= NotBlank
63
104
  callback = Callback.new(options, &block)
64
- validations.set(args => Callback::Chain.new) unless validations.has?(args)
65
- validations.get(args).add(callback)
105
+ options = Map.options_for!(args)
106
+ key = key_for(args)
107
+ validations = stack.validations.last || self.validations
108
+ validations[key] ||= Callback::Chain.new
109
+ validations[key].add(callback)
66
110
  callback
67
111
  end
68
- alias_method('validates', 'add')
112
+ alias_method('add', 'validates')
69
113
 
70
- def run_validations!(*args)
71
- run_validations(*args)
72
- ensure
73
- validated!
114
+ def validates_each(*args, &block)
115
+ options = Map.options_for!(args)
116
+ key = key_for(args)
117
+
118
+ args.push(options)
119
+
120
+ validates(*args) do |list|
121
+ Array(list).each_with_index do |item, index|
122
+ args = Dao.args_for_arity([item], block.arity)
123
+ validates(index, &block)
124
+ end
125
+ true
126
+ end
127
+ end
128
+
129
+ def stack
130
+ @stack ||= Map[:validations, [], :prefixes, []]
131
+ end
132
+
133
+ def prefixing(*prefix, &block)
134
+ prefix = Array(prefix).flatten.compact
135
+ push_prefix(prefix)
136
+ begin
137
+ block.call(*[prefix].slice(0, block.arity))
138
+ ensure
139
+ pop_prefix
140
+ end
141
+ end
142
+ alias_method('validating', 'prefixing')
143
+
144
+ def push_prefix(prefix)
145
+ prefix = Array(prefix).flatten.compact
146
+ stack.prefixes.push(prefix)
147
+ end
148
+
149
+ def pop_prefix
150
+ stack.prefixes.pop
151
+ end
152
+
153
+ def prefix
154
+ stack.prefixes.flatten.compact
74
155
  end
75
156
 
157
+ def key_for(*key)
158
+ prefix + Array(key).flatten.compact
159
+ end
160
+
161
+ def get(key)
162
+ attributes.get(key_for(key))
163
+ end
164
+
165
+ def set(key, val)
166
+ attributes.set(key_for(key), val)
167
+ end
168
+
169
+ def has(key)
170
+ attributes.has(key_for(key))
171
+ end
172
+
173
+ alias_method 'has?', 'has'
174
+
76
175
  def validations_search_path
77
176
  @validations_search_path ||= (
78
- list = [
79
- object,
80
- object.class.ancestors.map{|ancestor| ancestor.respond_to?(:validator) ? ancestor : nil}
81
- ]
82
- list.flatten!
83
- list.compact!
84
- list.reverse!
85
- list
177
+ if mixin?
178
+ list = [
179
+ object.respond_to?(:validator) ? object : nil,
180
+ object.class.ancestors.map{|ancestor| ancestor.respond_to?(:validator) ? ancestor : nil}
181
+ ]
182
+ list.flatten!
183
+ list.compact!
184
+ list.reverse!
185
+ list.uniq!
186
+ list
187
+ else
188
+ [self]
189
+ end
86
190
  )
87
191
  end
88
192
 
89
193
  def validations_list
90
- validations_search_path.map{|object| object.validator.validations}
194
+ validations_search_path.map{|object| object.validator.validations}.uniq
91
195
  end
92
196
 
93
- def run_validations(*args)
94
- object = args.first || @object
197
+ def run_validations(list = validations_list)
198
+ loop do
199
+ stack.validations.push(Map.new)
95
200
 
96
- attributes.extend(InstanceExec) unless attributes.respond_to?(:instance_exec)
201
+ _run_validations(errors, list)
97
202
 
98
- previous_errors = []
99
- new_errors = []
203
+ added = stack.validations.pop
204
+ break if added.empty?
205
+ list = [added]
206
+ end
100
207
 
101
- errors.each_message do |keys, message|
102
- previous_errors.push([keys, message])
208
+ if status.ok? and !errors.empty?
209
+ status.source = errors
210
+ status.update(412)
103
211
  end
104
- errors.clear!
105
- status.ok!
106
212
 
107
- list = validations_list
213
+ if status == 412 and status.source == errors and errors.empty?
214
+ status.update(200)
215
+ end
216
+
217
+ errors
218
+ ensure
219
+ validated!(true)
220
+ end
221
+
222
+ alias_method 'run_validations!', 'run_validations'
223
+ alias_method 'validate', 'run_validations'
108
224
 
109
- list.each do |validations|
110
- validations.depth_first_each do |keys, chain|
225
+ def _run_validations(errors, list)
226
+ Array(list).each do |validations|
227
+ validations.each do |keys, chain|
111
228
  chain.each do |callback|
112
229
  next unless callback and callback.respond_to?(:to_proc)
113
230
 
@@ -116,53 +233,41 @@ module Dao
116
233
 
117
234
  returned =
118
235
  catch(:validation) do
119
- args = [value, attributes].slice(0, callback.arity)
120
- attributes.instance_exec(*args, &callback)
236
+ args = Dao.args_for_arity([value, attributes], callback.arity)
237
+
238
+ prefixing(keys) do
239
+ object.instance_exec(*args, &callback)
240
+ end
121
241
  end
122
242
 
243
+ errors_added = errors.size > number_of_errors
244
+
123
245
  case returned
124
246
  when Hash
125
- map = Map(returned)
247
+ map = Map.for(returned)
126
248
  valid = map[:valid]
127
249
  message = map[:message]
128
-
129
250
  when TrueClass, FalseClass
130
251
  valid = returned
131
252
  message = nil
132
-
133
253
  else
134
- any_errors_added = errors.size > number_of_errors
135
- valid = !any_errors_added
254
+ valid = !errors_added
136
255
  message = nil
137
256
  end
138
257
 
258
+ valid = false if errors_added
259
+
139
260
  message ||= callback.options[:message]
140
261
  message ||= (value.to_s.strip.empty? ? 'is blank' : 'is invalid')
141
262
 
142
- unless valid
143
- new_errors.push([keys, message])
263
+ if not valid
264
+ errors.add_from_source(keys, callback, message)
144
265
  else
145
- new_errors.push([keys, Cleared])
266
+ errors.delete_from_source(keys, callback)
146
267
  end
147
268
  end
148
269
  end
149
270
  end
150
-
151
- previous_errors.each do |keys, message|
152
- errors.add(keys, message) unless new_errors.assoc(keys)
153
- end
154
-
155
- new_errors.each do |keys, value|
156
- next if value == Cleared
157
- message = value
158
- errors.add(keys, message)
159
- end
160
-
161
- if status.ok? and !errors.empty?
162
- status.update(412)
163
- end
164
-
165
- errors
166
271
  end
167
272
 
168
273
  def validated?
@@ -174,10 +279,6 @@ module Dao
174
279
  @validated = !!boolean
175
280
  end
176
281
 
177
- def validate
178
- run_validations
179
- end
180
-
181
282
  def validate!
182
283
  raise Error.new("#{ object.class.name } is invalid!") unless valid?
183
284
  object
@@ -200,7 +301,7 @@ module Dao
200
301
  true
201
302
  else
202
303
  options = Map.options_for!(args)
203
- validate #if(options[:validate] or !validated?)
304
+ run_validations
204
305
  errors.empty? and status.ok?
205
306
  end
206
307
  end