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
data/lib/dao/upload.rb ADDED
@@ -0,0 +1,396 @@
1
+ module Dao
2
+ class Upload < ::Map
3
+ class << Upload
4
+ def url
5
+ @url ||= (
6
+ if defined?(Rails.root) and Rails.root
7
+ '/system/uploads'
8
+ else
9
+ "file:/#{ root }"
10
+ end
11
+ )
12
+ end
13
+
14
+ def url=(url)
15
+ @url = '/' + Array(url).join('/').squeeze('/').sub(%r|^/+|, '').sub(%r|/+$|, '')
16
+ end
17
+
18
+ def root
19
+ @root ||= (
20
+ if defined?(Rails.root) and Rails.root
21
+ root = File.join(Rails.root, 'public', Upload.url)
22
+ FileUtils.mkdir_p(root) unless test(?d, root)
23
+ root
24
+ else
25
+ Dir.tmpdir
26
+ end
27
+ )
28
+ end
29
+
30
+ def root=(root)
31
+ @root = File.expand_path(root)
32
+ end
33
+
34
+ def uuid
35
+ Dao.uuid
36
+ end
37
+
38
+ def tmpdir(&block)
39
+ tmpdir = File.join(root, uuid)
40
+ FileUtils.mkdir_p(tmpdir)
41
+ if block
42
+ block.call(tmpdir)
43
+ else
44
+ tmpdir
45
+ end
46
+ end
47
+
48
+ def cleanname(path)
49
+ basename = File.basename(path.to_s)
50
+ CGI.unescape(basename).gsub(%r/[^0-9a-zA-Z_@)(~.-]/, '_').gsub(%r/_+/,'_')
51
+ end
52
+
53
+ def hidden_key_for(*key)
54
+ Dao.key_for('uploads', *key)
55
+ end
56
+
57
+ def finalizer(object_id)
58
+ if fd = IOs[object_id]
59
+ ::IO.for_fd(fd).close rescue nil
60
+ IOs.delete(object_id)
61
+ end
62
+ end
63
+
64
+ UUIDPattern = %r/^[a-zA-Z0-9-]+$/io
65
+ Age = 60 * 60 * 24
66
+
67
+ def clear!(options = {})
68
+ return if Upload.turd?
69
+
70
+ glob = File.join(root, '*')
71
+ age = Integer(options[:age] || options['age'] || Age)
72
+ since = options[:since] || options['since'] || Time.now
73
+
74
+ Dir.glob(glob) do |entry|
75
+ begin
76
+ next unless test(?d, entry)
77
+ next unless File.basename(entry) =~ UUIDPattern
78
+
79
+ files = Dir.glob(File.join(entry, '**/**'))
80
+
81
+ all_files_are_old =
82
+ files.all? do |file|
83
+ begin
84
+ stat = File.stat(file)
85
+ file_age = since - stat.atime
86
+ file_age >= age
87
+ rescue
88
+ false
89
+ end
90
+ end
91
+
92
+ FileUtils.rm_rf(entry) if all_files_are_old
93
+ rescue
94
+ next
95
+ end
96
+ end
97
+ end
98
+
99
+ Thread.new do
100
+ Thread.current.abort_on_exception = true
101
+
102
+ loop do
103
+ sleep(60 * 60)
104
+ begin
105
+ Upload.clear!
106
+ rescue Object
107
+ nil
108
+ end
109
+ end
110
+ end
111
+
112
+ at_exit{ Upload.clear! }
113
+
114
+ def turd?
115
+ @turd ||= !!ENV['DAO_UPLOAD_TURD']
116
+ end
117
+
118
+ def name_for(key, &block)
119
+ if block
120
+ @name_for = block
121
+ else
122
+ defined?(@name_for) ? @name_for.call(key) : [prefix, *Array(key)].compact.join('.')
123
+ end
124
+ end
125
+
126
+ def prefix(*value)
127
+ @prefix = value.shift if value
128
+ @prefix
129
+ end
130
+
131
+ def prefix=(value)
132
+ @prefix = value
133
+ end
134
+
135
+ def rewind(io, &block)
136
+ begin
137
+ pos = io.pos
138
+ io.flush
139
+ io.rewind
140
+ rescue
141
+ nil
142
+ end
143
+
144
+ begin
145
+ block.call
146
+ ensure
147
+ begin
148
+ io.pos = pos
149
+ rescue
150
+ nil
151
+ end
152
+ end
153
+ end
154
+
155
+ alias_method('mount', 'new')
156
+ end
157
+
158
+ attr_accessor :conducer
159
+ attr_accessor :key
160
+ attr_accessor :options
161
+ attr_accessor :hidden_key
162
+ attr_accessor :name
163
+ attr_accessor :value
164
+ attr_accessor :path
165
+ attr_accessor :dirname
166
+ attr_accessor :basename
167
+ attr_accessor :io
168
+ attr_accessor :tmpdir
169
+ attr_accessor :placeholder
170
+
171
+ IOs = {}
172
+
173
+ def initialize(conducer, *args, &block)
174
+ @conducer = conducer
175
+
176
+ @options = Map.options_for!(args)
177
+ @key = Dao.key_for(args)
178
+ @hidden_key = Upload.hidden_key_for(@key)
179
+ @name = Upload.name_for(@hidden_key)
180
+
181
+ @placeholder = Placeholder.new(@options[:placeholder])
182
+
183
+ @path = nil
184
+ @dirname, @basename = nil
185
+ @value = nil
186
+ @io = nil
187
+ @tmpdir = nil
188
+
189
+ url = @placeholder.url
190
+
191
+ update(:file => @io, :cache => @value, :url => url)
192
+ end
193
+
194
+ def _set(value)
195
+ value =
196
+ case
197
+ when value.is_a?(Hash)
198
+ Map.for(value)
199
+ when value.respond_to?(:read)
200
+ Map.for(:file => value)
201
+ else
202
+ Map.for(:cache => value.to_s)
203
+ end
204
+
205
+ cache = value[:cache]
206
+ file = value[:file]
207
+
208
+ unless cache.blank?
209
+ process_previously_uploaded(cache)
210
+ end
211
+
212
+ unless file.blank?
213
+ process_currently_uploaded(file)
214
+ end
215
+
216
+ url = @value ? File.join(Upload.url, @value) : @placeholder.url
217
+
218
+ update(:file => @io, :cache => @value, :url => url)
219
+ end
220
+
221
+ def _key
222
+ key
223
+ end
224
+
225
+ def _value
226
+ path
227
+ end
228
+
229
+ def _clear
230
+ clear!
231
+ end
232
+
233
+ def hidden_value
234
+ @value
235
+ end
236
+
237
+ def blank?
238
+ @value.blank?
239
+ end
240
+
241
+ def url
242
+ self[:url]
243
+ end
244
+
245
+ def to_s
246
+ self[:url]
247
+ end
248
+
249
+ def process_previously_uploaded(cache)
250
+ cache = cache.to_s.strip
251
+
252
+ unless cache.empty?
253
+ dirname, basename = File.split(File.expand_path(cache))
254
+ relative_dirname = File.basename(dirname)
255
+ relative_basename = File.join(relative_dirname, basename)
256
+ path = Upload.root + '/' + relative_basename
257
+
258
+ if test(?s, path)
259
+ gcopen(path)
260
+ @tmpdir = @dirname
261
+ end
262
+ end
263
+ end
264
+
265
+ def process_currently_uploaded(io)
266
+ unless @tmpdir
267
+ @tmpdir = Upload.tmpdir
268
+ end
269
+
270
+ original_basename =
271
+ [:original_path, :original_filename, :path, :filename, :pathname].
272
+ map{|msg| io.send(msg).to_s if io.respond_to?(msg)}.
273
+ compact.
274
+ first
275
+
276
+ basename = Upload.cleanname(original_basename)
277
+
278
+ path = File.join(@tmpdir, basename)
279
+
280
+ copied = false
281
+
282
+ Upload.rewind(io) do
283
+ src = io.path
284
+ dst = path
285
+
286
+ strategies = [
287
+ proc{ `ln -f #{ src.inspect } #{ dst.inspect } || cp -f #{ src.inspect } #{ dst.inspect }`},
288
+ proc{ FileUtils.ln(src, dst) },
289
+ proc{ FileUtils.cp(src, dst) },
290
+ proc{
291
+ open(dst, 'wb'){|fd| fd.write(io.read)}
292
+ }
293
+ ]
294
+
295
+ FileUtils.rm_f(dst)
296
+ strategies.each do |strategy|
297
+ strategy.call rescue nil
298
+ break if((copied = test(?e, dst)))
299
+ end
300
+ end
301
+
302
+ raise("failed to copy #{ io.path.inspect } -> #{ path.inspect }") unless copied
303
+
304
+ gcopen(path) if test(?s, path)
305
+ end
306
+
307
+ def gcopen(path)
308
+ @path = path
309
+ @dirname, @basename = File.split(@path)
310
+ @value = File.join(File.basename(@dirname), @basename).strip
311
+ @io = open(@path, 'rb')
312
+ IOs[object_id] = @io.fileno
313
+ ObjectSpace.define_finalizer(self, Upload.method(:finalizer).to_proc)
314
+ @io
315
+ end
316
+
317
+ def inspect
318
+ {
319
+ Upload.name =>
320
+ {
321
+ :key => key,
322
+ :hidden_key => hidden_key,
323
+ :hidden_value => hidden_value,
324
+ :name => name,
325
+ :path => path,
326
+ :io => io
327
+ }
328
+ }.inspect
329
+ end
330
+
331
+ def clear!(&block)
332
+ result = block ? block.call(@path) : nil
333
+
334
+ unless Upload.turd?
335
+ begin
336
+ FileUtils.rm_rf(@tmpdir) if test(?d, @tmpdir)
337
+ rescue
338
+ nil
339
+ ensure
340
+ @io.close rescue nil
341
+ IOs.delete(object_id)
342
+ end
343
+ end
344
+
345
+ result
346
+ end
347
+ alias_method('clear', 'clear!')
348
+
349
+ class Placeholder < ::String
350
+ def Placeholder.route
351
+ "/assets"
352
+ end
353
+
354
+ def Placeholder.root
355
+ File.join(Rails.root, "app", "assets", "placeholders")
356
+ end
357
+
358
+ attr_accessor :url
359
+ attr_accessor :path
360
+
361
+ def initialize(placeholder = '', options = {})
362
+ replace(placeholder.to_s)
363
+ options.to_options!
364
+ @url = options[:url] || default_url
365
+ @path = options[:path] || default_path
366
+ end
367
+
368
+ def default_url
369
+ return nil if blank?
370
+ absolute? ? self : File.join(Placeholder.route, self)
371
+ end
372
+
373
+ def default_path
374
+ return nil if blank?
375
+ absolute? ? nil : File.join(Placeholder.root, self)
376
+ end
377
+
378
+ def basename
379
+ File.basename(self)
380
+ end
381
+
382
+ def absolute?
383
+ self =~ %r|\A([^:/]++:/)?/|
384
+ end
385
+ end
386
+
387
+ def placeholder
388
+ @placeholder ||= Placeholder.new
389
+ end
390
+
391
+ def placeholder=(placeholder)
392
+ @placeholder = placeholder.is_a?(Placeholder) ? placeholder : Placeholder.new(placeholder)
393
+ end
394
+ end
395
+
396
+ end
@@ -1,14 +1,26 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Dao
2
3
  module Validations
3
- class Error < Dao::Error; end
4
+ class Error < Dao::Error
5
+ attr_accessor :errors
6
+
7
+ def initialize(*args, &block)
8
+ @errors = args.shift if args.first.respond_to?(:full_messages)
9
+ super(*args, &block)
10
+ end
11
+
12
+ def object
13
+ @errors.object if(@errors and @errors.respond_to?(:object))
14
+ end
15
+ end
4
16
 
5
- Dao.load('validations/validator.rb')
6
17
  Dao.load('validations/callback.rb')
7
18
  Dao.load('validations/common.rb')
19
+ Dao.load('validations/validator.rb')
8
20
 
9
21
  ClassMethods = proc do
10
22
  def validator
11
- @validator ||= Validator.new(self)
23
+ @validator ||= Validator.mixin(self)
12
24
  end
13
25
 
14
26
  def validator=(validator)
@@ -18,6 +30,7 @@ module Dao
18
30
  %w(
19
31
  validations
20
32
  validates
33
+ validates_each
21
34
  ).each do |method|
22
35
  module_eval <<-__, __FILE__, __LINE__
23
36
  def self.#{ method }(*args, &block)
@@ -29,7 +42,7 @@ module Dao
29
42
 
30
43
  InstanceMethods = proc do
31
44
  def validator
32
- @validator ||= Validator.new(self)
45
+ @validator ||= Validator.mixin(self)
33
46
  end
34
47
 
35
48
  def validator=(validator)
@@ -39,14 +52,16 @@ module Dao
39
52
  %w(
40
53
  validations
41
54
  validates
55
+ validates_each
42
56
  validated?
43
57
  validated!
44
58
  validate
45
59
  validate!
60
+ run_validations
46
61
  run_validations!
47
62
  valid!
48
- forcing_validity?
49
63
  valid?
64
+ forcing_validity?
50
65
  errors
51
66
  status
52
67
  ).each do |method|
@@ -65,5 +80,8 @@ module Dao
65
80
  other.send(:extend, Common)
66
81
  super
67
82
  end
83
+
68
84
  end
85
+
86
+ Dao.load('validations/instance.rb')
69
87
  end