dao 3.3.0 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/README +7 -0
  2. data/Rakefile +36 -17
  3. data/b.rb +38 -0
  4. data/dao.gemspec +41 -13
  5. data/lib/dao.rb +44 -13
  6. data/lib/dao/api.rb +1 -1
  7. data/lib/dao/api/context.rb +35 -45
  8. data/lib/dao/api/endpoints.rb +225 -91
  9. data/lib/dao/conducer.rb +437 -0
  10. data/lib/dao/conducer/attributes.rb +21 -0
  11. data/lib/dao/conducer/crud.rb +70 -0
  12. data/lib/dao/current.rb +66 -0
  13. data/lib/dao/db.rb +44 -5
  14. data/lib/dao/endpoint.rb +13 -1
  15. data/lib/dao/errors.rb +74 -59
  16. data/lib/dao/exceptions.rb +1 -2
  17. data/lib/dao/extractor.rb +68 -0
  18. data/lib/dao/form.rb +139 -46
  19. data/lib/dao/image_cache.rb +193 -0
  20. data/lib/dao/instance_exec.rb +1 -1
  21. data/lib/dao/name.rb +7 -0
  22. data/lib/dao/params.rb +16 -66
  23. data/lib/dao/rack.rb +3 -0
  24. data/lib/dao/rack/middleware.rb +5 -0
  25. data/lib/dao/rack/middleware/params_parser.rb +24 -0
  26. data/lib/dao/rails.rb +22 -5
  27. data/lib/dao/rails/lib/generators/dao/USAGE +2 -6
  28. data/lib/dao/rails/lib/generators/dao/dao_generator.rb +52 -7
  29. data/lib/dao/rails/lib/generators/dao/templates/api.rb +23 -7
  30. data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +24 -7
  31. data/lib/dao/rails/lib/generators/dao/templates/conducer.rb +64 -0
  32. data/lib/dao/rails/lib/generators/dao/templates/conducer_controller.rb +79 -0
  33. data/lib/dao/rails/lib/generators/dao/templates/dao.js +13 -6
  34. data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +75 -11
  35. data/lib/dao/result.rb +1 -26
  36. data/lib/dao/slug.rb +37 -8
  37. data/lib/dao/status.rb +4 -0
  38. data/lib/dao/support.rb +155 -0
  39. data/lib/dao/validations.rb +48 -157
  40. data/lib/dao/validations/callback.rb +30 -0
  41. data/lib/dao/validations/common.rb +322 -320
  42. data/lib/dao/validations/validator.rb +219 -0
  43. data/test/active_model_conducer_lint_test.rb +19 -0
  44. data/test/api_test.rb +261 -0
  45. data/test/conducer_test.rb +205 -0
  46. data/test/db.yml +9 -0
  47. data/test/form_test.rb +42 -0
  48. data/test/support_test.rb +52 -0
  49. data/test/testing.rb +145 -24
  50. data/test/validations_test.rb +156 -0
  51. metadata +138 -21
  52. data/TODO +0 -33
  53. data/a.rb +0 -80
  54. data/db/dao.yml +0 -5
  55. data/lib/dao/api/interfaces.rb +0 -306
  56. data/lib/dao/interface.rb +0 -28
  57. data/lib/dao/presenter.rb +0 -129
  58. data/lib/dao/rails/lib/generators/dao/api_generator.rb +0 -3
  59. data/lib/dao/validations/base.rb +0 -68
  60. data/test/dao_test.rb +0 -506
@@ -1,6 +1,5 @@
1
1
  module Dao
2
- class Dao::Error < ::StandardError
3
- end
2
+ class Dao::Error < ::StandardError; end
4
3
 
5
4
  class Dao::Error::Result < Error
6
5
  attr_accessor :result
@@ -0,0 +1,68 @@
1
+ module Dao
2
+ class Extractor < BlankSlate
3
+ attr :target
4
+ attr :extracted
5
+ attr :strategies
6
+
7
+ def initialize(target, *args, &block)
8
+ @target = target
9
+ @strategies = Map.new
10
+ @extracted = Map.new
11
+ extracts(*args)
12
+ end
13
+
14
+ def inspect
15
+ @extracted.inspect
16
+ end
17
+
18
+ def extracts(*args, &block)
19
+ hashes = []
20
+
21
+ args.each do |arg|
22
+ if arg.is_a?(Hash)
23
+ hashes.push(arg)
24
+ else
25
+ if block
26
+ hashes.push(arg => block)
27
+ end
28
+ end
29
+ end
30
+
31
+ hashes.each do |hash|
32
+ hash.each do |key, val|
33
+ next unless val.respond_to?(:call)
34
+ @strategies[key] = val
35
+ end
36
+ end
37
+
38
+ self
39
+ end
40
+
41
+ def method_missing(method, *args, &block)
42
+ super unless @strategies.has_key?(method)
43
+ extract(method, &@strategies[method])
44
+ end
45
+
46
+ def extract(attribute, &block)
47
+ return @extracted[attribute] if @extracted.has_key?(attribute)
48
+
49
+ if @target.respond_to?(attribute)
50
+ value = @target.send(attribute)
51
+ @extracted[attribute] = value
52
+ return @extracted[attribute]
53
+ end
54
+
55
+ ivar = "@#{ attribute }"
56
+ if @target.instance_variable_defined?(ivar)
57
+ value = @target.instance_variable_get(ivar)
58
+ @extracted[attribute] = value
59
+ return @extracted[attribute]
60
+ end
61
+
62
+ if block
63
+ @extracted[attribute] = block.call
64
+ return @extracted[attribute]
65
+ end
66
+ end
67
+ end
68
+ end
data/lib/dao/form.rb CHANGED
@@ -18,26 +18,110 @@ module Dao
18
18
 
19
19
  # instance methods
20
20
  #
21
- attr_accessor :map
21
+ attr_accessor :object
22
22
 
23
- def initialize(*args, &block)
24
- @map = args.first.is_a?(Map) ? args.shift : Map.new
23
+ def initialize(*args)
24
+ @object = args.shift
25
25
  end
26
26
 
27
- def errors
28
- @map.errors
27
+ fattr(:attributes) do
28
+ attributes =
29
+ catch(:attributes) do
30
+ if @object.respond_to?(:attributes)
31
+ throw :attributes, @object.attributes
32
+ end
33
+ if @object.instance_variable_defined?('@attributes')
34
+ throw :attributes, @object.instance_variable_get('@attributes')
35
+ end
36
+ if @object.is_a?(Map)
37
+ throw :attributes, @object
38
+ end
39
+ if @object.respond_to?(:to_map)
40
+ throw :attributes, Map.new(@object.to_map)
41
+ end
42
+ if @object.is_a?(Hash)
43
+ throw :attributes, Map.new(@object)
44
+ end
45
+ if @object.respond_to?(:to_hash)
46
+ throw :attributes, Map.new(@object.to_hash)
47
+ end
48
+ Map.new
49
+ end
50
+
51
+ case attributes
52
+ when Map
53
+ attributes
54
+ when Hash
55
+ Map.new(attributes)
56
+ else
57
+ raise(ArgumentError.new("#{ attributes.inspect } (#{ attributes.class })"))
58
+ end
29
59
  end
30
60
 
31
- def path
32
- @map.path
61
+ fattr(:name) do
62
+ name =
63
+ catch(:name) do
64
+ if @object.respond_to?(:name)
65
+ throw :name, @object.name
66
+ end
67
+ if @object.instance_variable_defined?('@name')
68
+ throw :name, @object.instance_variable_get('@name')
69
+ end
70
+ 'form'
71
+ end
72
+
73
+ case name
74
+ when Symbol, String
75
+ name.to_s
76
+ else
77
+ raise(ArgumentError.new("#{ name.inspect } (#{ name.class })"))
78
+ end
79
+ end
80
+
81
+ fattr(:errors) do
82
+ errors =
83
+ catch(:errors) do
84
+ if @object.respond_to?(:errors)
85
+ throw :errors, @object.errors
86
+ end
87
+ if @object.instance_variable_defined?('@errors')
88
+ throw :errors, @object.instance_variable_get('@errors')
89
+ end
90
+ Errors.new
91
+ end
92
+
93
+ case errors
94
+ when Errors
95
+ errors
96
+ else
97
+ raise(ArgumentError.new("#{ errors.inspect } (#{ errors.class })"))
98
+ end
33
99
  end
34
100
 
101
+ fattr(:status) do
102
+ status =
103
+ catch(:status) do
104
+ if @object.respond_to?(:status)
105
+ throw :status, @object.status
106
+ end
107
+ if @object.instance_variable_defined?('@status')
108
+ throw :status, @object.instance_variable_get('@status')
109
+ end
110
+ Status.new
111
+ end
35
112
 
113
+ case status
114
+ when Status
115
+ status
116
+ else
117
+ raise(ArgumentError.new("#{ status.inspect } (#{ status.class })"))
118
+ end
119
+ end
36
120
 
37
121
  # html generation methods
38
122
  #
39
123
  def form(*args, &block)
40
- options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
124
+ options = args.extract_options!.to_options!
41
125
  keys = args.flatten
42
126
 
43
127
  action = options.delete(:action) || './'
@@ -57,7 +141,7 @@ module Dao
57
141
  end
58
142
 
59
143
  def label(*args, &block)
60
- options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
144
+ options = args.extract_options!.to_options!
61
145
  keys = args.flatten
62
146
 
63
147
  id = options.delete(:id) || id_for(keys)
@@ -67,7 +151,7 @@ module Dao
67
151
 
68
152
  content =
69
153
  if block.nil? and !options.has_key?(:content)
70
- humanize(keys.last)
154
+ titleize(keys.last)
71
155
  else
72
156
  block ? block.call() : options.delete(:content)
73
157
  end
@@ -76,7 +160,7 @@ module Dao
76
160
  end
77
161
 
78
162
  def input(*args, &block)
79
- options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
163
+ options = args.extract_options!.to_options!
80
164
  keys = args.flatten
81
165
 
82
166
  type = options.delete(:type) || :text
@@ -87,16 +171,16 @@ module Dao
87
171
 
88
172
  value =
89
173
  if block.nil? and !options.has_key?(:value)
90
- value_for(@map, keys)
174
+ value_for(attributes, keys)
91
175
  else
92
- block ? block.call(@map.get(keys)) : options.delete(:value)
176
+ block ? block.call(attributes.get(keys)) : options.delete(:value)
93
177
  end
94
178
 
95
179
  input_(options_for(options, :type => type, :name => name, :value => value, :class => klass, :id => id, :data_error => error)){}
96
180
  end
97
181
 
98
182
  def submit(*args, &block)
99
- options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
183
+ options = args.extract_options!.to_options!
100
184
 
101
185
  content = block ? block.call : (args.first || 'Submit')
102
186
 
@@ -108,7 +192,7 @@ module Dao
108
192
  end
109
193
 
110
194
  def button(*args)
111
- options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
195
+ options = args.extract_options!.to_options!
112
196
  keys = args.flatten
113
197
 
114
198
  type = options.delete(:type) || :button
@@ -119,23 +203,23 @@ module Dao
119
203
 
120
204
  value =
121
205
  if block.nil? and !options.has_key?(:value)
122
- value_for(@map, keys)
206
+ value_for(attributes, keys)
123
207
  else
124
- block ? block.call(@map.get(keys)) : options.delete(:value)
208
+ block ? block.call(attributes.get(keys)) : options.delete(:value)
125
209
  end
126
210
 
127
211
  button_(options_for(options, :type => type, :name => name, :value => value, :class => klass, :id => id, :data_error => error)){}
128
212
  end
129
213
 
130
214
  def reset(*args)
131
- options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
215
+ options = args.extract_options!.to_options!
132
216
  options[:type] = :reset
133
217
  args.push(options)
134
218
  button(*args)
135
219
  end
136
220
 
137
221
  def textarea(*args, &block)
138
- options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
222
+ options = args.extract_options!.to_options!
139
223
  keys = args.flatten
140
224
 
141
225
  name = options.delete(:name) || name_for(keys)
@@ -145,27 +229,27 @@ module Dao
145
229
 
146
230
  value =
147
231
  if block.nil? and !options.has_key?(:value)
148
- value_for(@map, keys)
232
+ value_for(attributes, keys)
149
233
  else
150
- block ? block.call(@map.get(keys)) : options.delete(:value)
234
+ block ? block.call(attributes.get(keys)) : options.delete(:value)
151
235
  end
152
236
 
153
237
  textarea_(options_for(options, :name => name, :class => klass, :id => id, :data_error => error)){ value.to_s }
154
238
  end
155
239
 
156
240
  def select(*args, &block)
157
- options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
241
+ options = args.extract_options!.to_options!
158
242
  keys = args.flatten
159
243
 
160
244
  name = options.delete(:name) || name_for(keys)
161
- from = options.delete(:from) || options.delete(:options)
245
+ from = options.delete(:from) || options.delete(:options) || attributes.get(*keys)
162
246
  blank = options.delete(:blank)
163
247
 
164
248
  selected =
165
249
  if options.has_key?(:selected)
166
250
  options.delete(:selected)
167
251
  else
168
- value_for(@map, keys)
252
+ value_for(attributes, keys)
169
253
  end
170
254
 
171
255
  id = options.delete(:id) || id_for(keys)
@@ -177,7 +261,7 @@ module Dao
177
261
  if from.nil?
178
262
  key = keys.map{|key| "#{ key }"}
179
263
  key.last << "_options"
180
- from = @map.get(*key) if @map.has?(*key)
264
+ from = attributes.get(*key) if attributes.has?(*key)
181
265
  end
182
266
 
183
267
  list = Array(from)
@@ -242,13 +326,21 @@ module Dao
242
326
  # html generation support methods
243
327
  #
244
328
  def id_for(keys)
245
- id = [path, keys.join('-')].compact.join('_')
329
+ id = [name, keys.join('-')].compact.join('_')
246
330
  slug_for(id)
247
331
  end
248
332
 
333
+ def errors_on(keys)
334
+ errors.get(keys)
335
+ end
336
+
337
+ def errors_on?(*keys)
338
+ !errors_on(keys).blank?
339
+ end
340
+
249
341
  def class_for(keys, klass = nil)
250
342
  klass =
251
- if errors.on?(keys)
343
+ if errors_on?(keys)
252
344
  [klass, 'dao', 'errors'].compact.join(' ')
253
345
  else
254
346
  [klass, 'dao'].compact.join(' ')
@@ -257,31 +349,32 @@ module Dao
257
349
  end
258
350
 
259
351
  def error_for(keys, klass = nil)
260
- errors.get(keys) if errors.on?(keys)
352
+ if errors_on?(keys)
353
+ title = Array(keys).join(' ').titleize
354
+ messages = Array(errors.get(keys)).join(', ')
355
+ "#{ title }: #{ messages }"
356
+ end
261
357
  end
262
358
 
263
359
  def value_for(map, keys)
264
360
  return nil unless map.has?(keys)
265
- value = Tagz.escapeHTML(map.get(keys))
266
- end
267
-
268
- def Form.name_for(path, *keys)
269
- path = Path.new(path) unless path.is_a?(Path)
270
- "#{ path }(#{ Array(keys).flatten.compact.join(',') })"
271
- end
272
-
273
- def Form.name_re_for(path)
274
- path = Path.new(path) unless path.is_a?(Path)
275
- Regexp.new(/^#{ Regexp.escape(path) }/)
361
+ value = map.get(keys)
362
+ value =
363
+ case value
364
+ when Hash, Array
365
+ value.to_json
366
+ else
367
+ value
368
+ end
369
+ Tagz.escapeHTML(value)
276
370
  end
277
371
 
278
- def Form.encoded?(path, params)
279
- name_re = Form.name_re_for(path)
280
- params.keys.any?{|key| name_re =~ key.to_s}
372
+ def Form.name_for(name, *keys)
373
+ "dao[#{ name }][#{ Array(keys).flatten.compact.join('.') }]"
281
374
  end
282
375
 
283
- def name_for(keys)
284
- Form.name_for(path, keys)
376
+ def name_for(*keys)
377
+ Form.name_for(name, *keys)
285
378
  end
286
379
 
287
380
  def options_for(*hashes)
@@ -304,9 +397,9 @@ module Dao
304
397
  slug_for(string).gsub(/_/, '-')
305
398
  end
306
399
 
307
- def humanize(string)
400
+ def titleize(string)
308
401
  string = string.to_s
309
- string = string.humanize if string.respond_to?(:humanize)
402
+ string = string.titleize if string.respond_to?(:titleize)
310
403
  string
311
404
  end
312
405
  end
@@ -0,0 +1,193 @@
1
+ module Dao
2
+ require 'uuidtools'
3
+ require 'fileutils'
4
+ require 'cgi'
5
+
6
+ class ImageCache
7
+ Version = '1.0.0'
8
+
9
+ class << ImageCache
10
+ def version
11
+ ImageCache::Version
12
+ end
13
+
14
+ def base
15
+ @base ||= 'images/cache'
16
+ end
17
+
18
+ def base=(base)
19
+ @base = base.to_s.sub(%r|^/+|, '')
20
+ end
21
+
22
+ def url
23
+ '/' + base
24
+ end
25
+
26
+ def root
27
+ @root ||= File.join(Rails.root, 'public', base)
28
+ end
29
+
30
+ def uuid(*args)
31
+ UUIDTools::UUID.timestamp_create.to_s
32
+ end
33
+
34
+ def tmpdir(&block)
35
+ tmpdir = File.join(root, uuid)
36
+
37
+ if block
38
+ FileUtils.mkdir_p(tmpdir)
39
+ block.call(tmpdir)
40
+ else
41
+ tmpdir
42
+ end
43
+ end
44
+
45
+ def cleanname(path)
46
+ basename = File.basename(path.to_s)
47
+ CGI.unescape(basename).gsub(%r/[^0-9a-zA-Z_@)(~.-]/, '_').gsub(%r/_+/,'_')
48
+ end
49
+
50
+ def cache_key_for(key)
51
+ "#{ key }__cache"
52
+ end
53
+
54
+ def for(params, key = :image)
55
+ image = params[key]
56
+ if image.respond_to?(:read)
57
+ tmpdir do |tmp|
58
+ basename = cleanname(image.original_path)
59
+
60
+ path = File.join(tmp, basename)
61
+ open(path, 'w'){|fd| fd.write(image.read)}
62
+ image_cache = new(key, path)
63
+ params[key] = image_cache.io
64
+ return image_cache
65
+ end
66
+ end
67
+
68
+ cache_key = cache_key_for(key)
69
+ image_cache = params[cache_key]
70
+ if image_cache
71
+ dirname, basename = File.split(image_cache)
72
+ path = root + '/' + File.join(File.basename(dirname), basename)
73
+ image_cache = new(key, path)
74
+ params[key] = image_cache.io
75
+ return image_cache
76
+ end
77
+
78
+ return new(key, path=nil)
79
+ end
80
+
81
+ def finalizer(object_id)
82
+ if fd = IOs[object_id]
83
+ IO.for_fd(fd).close
84
+ IOs.delete(object_id)
85
+ end
86
+ end
87
+
88
+ UUIDPattern = %r/^[a-zA-Z0-9-]+$/io
89
+ Age = 60 * 60 * 24
90
+
91
+ def clear!(options = {})
92
+ glob = File.join(root, '*')
93
+ age = Integer(options[:age] || options['age'] || Age)
94
+ since = options[:since] || options['since'] || Time.now
95
+
96
+ Dir.glob(glob) do |entry|
97
+ begin
98
+ next unless test(?d, entry)
99
+ next unless File.basename(entry) =~ UUIDPattern
100
+
101
+ files = Dir.glob(File.join(entry, '**/**'))
102
+
103
+ all_files_are_old =
104
+ files.all? do |file|
105
+ begin
106
+ stat = File.stat(file)
107
+ age = since - stat.atime
108
+ age >= Age
109
+ rescue
110
+ false
111
+ end
112
+ end
113
+
114
+ FileUtils.rm_rf(entry) if all_files_are_old
115
+ rescue
116
+ next
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ attr_accessor :key
123
+ attr_accessor :cache_key
124
+ attr_accessor :path
125
+ attr_accessor :dirname
126
+ attr_accessor :basename
127
+ attr_accessor :value
128
+ attr_accessor :io
129
+
130
+ IOs = {}
131
+
132
+ def initialize(key, path)
133
+ @key = key.to_s
134
+ @cache_key = ImageCache.cache_key_for(key)
135
+
136
+ if path
137
+ @path = path
138
+ @dirname, @basename = File.split(@path)
139
+ @value = File.join(File.basename(@dirname), @basename).strip
140
+ @io = open(@path)
141
+ IOs[object_id] = @io.fileno
142
+ ObjectSpace.define_finalizer(self, ImageCache.method(:finalizer).to_proc)
143
+ else
144
+ @path = nil
145
+ @value = nil
146
+ end
147
+ end
148
+
149
+ def hidden
150
+ raw("<input type='hidden' name='#{ @cache_key }' value='#{ @value }' />") if @value
151
+ end
152
+
153
+ def to_s
154
+ hidden.to_s
155
+ end
156
+
157
+ def url
158
+ File.join(ImageCache.url, @value) if @value
159
+ end
160
+
161
+ def raw(*args)
162
+ string = args.join
163
+ if string.respond_to?(:html_safe)
164
+ string.html_safe
165
+ else
166
+ string
167
+ end
168
+ end
169
+
170
+ def clear!
171
+ FileUtils.rm_rf(@dirname) if test(?d, @dirname)
172
+ rescue
173
+ nil
174
+ ensure
175
+ if @io
176
+ @io.close
177
+ IOs.delete(object_id)
178
+ end
179
+ Thread.new{ ImageCache.clear! }
180
+ end
181
+ end
182
+
183
+ Image_cache = ImageCache unless defined?(Image_cache)
184
+
185
+ if defined?(Rails.env)
186
+ unless Rails.env.production?
187
+ if defined?(unloadable)
188
+ unloadable(ImageCache)
189
+ unloadable(Image_cache)
190
+ end
191
+ end
192
+ end
193
+ end