dao 3.3.0 → 4.2.1

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 (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