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