dao 4.2.1 → 4.4.2

Sign up to get free protection for your applications and to get access to all the features.
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