mongoid-grid_fs 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2 +1,2 @@
1
- require 'mongoid-grid_fs.rb' unless defined?(Mongoid::GridFS)
2
- Mongoid::GridFS
1
+ require 'mongoid/grid_fs' unless defined?(Mongoid::GridFs)
2
+ Mongoid::GridFs
@@ -1,2 +1,2 @@
1
- require 'mongoid-grid_fs.rb' unless defined?(Mongoid::GridFS::Fs::Chunk)
2
- Mongoid::GridFS::Fs::Chunk
1
+ require 'mongoid/grid_fs' unless defined?(Mongoid::GridFs::Fs::Chunk)
2
+ Mongoid::GridFs::Fs::Chunk
@@ -1,2 +1,2 @@
1
- require 'mongoid-grid_fs.rb' unless defined?(Mongoid::GridFS::Fs::File)
2
- Mongoid::GridFS::Fs::File
1
+ require 'mongoid/grid_fs' unless defined?(Mongoid::GridFs::Fs::File)
2
+ Mongoid::GridFs::Fs::File
@@ -1,644 +1 @@
1
- ##
2
- #
3
- module Mongoid
4
- class GridFS
5
- const_set :Version, '2.0.0'
6
-
7
- class << GridFS
8
- def version
9
- const_get :Version
10
- end
11
-
12
- def dependencies
13
- {
14
- 'mongoid' => [ 'mongoid' , '>= 3.0', '< 5.0' ] ,
15
- 'mime/types' => [ 'mime-types' , '>= 1.0', '< 3.0'] ,
16
- }
17
- end
18
-
19
- def libdir(*args, &block)
20
- @libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
21
- args.empty? ? @libdir : File.join(@libdir, *args)
22
- ensure
23
- if block
24
- begin
25
- $LOAD_PATH.unshift(@libdir)
26
- block.call()
27
- ensure
28
- $LOAD_PATH.shift()
29
- end
30
- end
31
- end
32
-
33
- def load(*libs)
34
- libs = libs.join(' ').scan(/[^\s+]+/)
35
- libdir{ libs.each{|lib| Kernel.load(lib) } }
36
- end
37
- end
38
-
39
- begin
40
- require 'rubygems'
41
- rescue LoadError
42
- nil
43
- end
44
-
45
- if defined?(gem)
46
- dependencies.each do |lib, dependency|
47
- gem(*dependency)
48
- require(lib)
49
- end
50
- end
51
-
52
- require "digest/md5"
53
- require "cgi"
54
- end
55
- end
56
-
57
- ##
58
- #
59
- module Mongoid
60
- class GridFS
61
- class << GridFS
62
- attr_accessor :namespace
63
- attr_accessor :file_model
64
- attr_accessor :chunk_model
65
-
66
- def init!
67
- GridFS.build_namespace_for(:Fs)
68
-
69
- GridFS.namespace = Fs
70
- GridFS.file_model = Fs.file_model
71
- GridFS.chunk_model = Fs.chunk_model
72
-
73
- const_set(:File, Fs.file_model)
74
- const_set(:Chunk, Fs.chunk_model)
75
-
76
- to_delegate = %w(
77
- put
78
- get
79
- delete
80
- find
81
- []
82
- []=
83
- clear
84
- )
85
-
86
- to_delegate.each do |method|
87
- class_eval <<-__
88
- def self.#{ method }(*args, &block)
89
- ::Mongoid::GridFS::Fs::#{ method }(*args, &block)
90
- end
91
- __
92
- end
93
- end
94
- end
95
-
96
- ##
97
- #
98
- def GridFS.namespace_for(prefix)
99
- prefix = prefix.to_s.downcase
100
- const = "::GridFS::#{ prefix.to_s.camelize }"
101
- namespace = const.split(/::/).last
102
- const_defined?(namespace) ? const_get(namespace) : build_namespace_for(namespace)
103
- end
104
-
105
- ##
106
- #
107
- def GridFS.build_namespace_for(prefix)
108
- prefix = prefix.to_s.downcase
109
- const = prefix.camelize
110
-
111
- namespace =
112
- Module.new do
113
- module_eval(&NamespaceMixin)
114
- self
115
- end
116
-
117
- const_set(const, namespace)
118
-
119
- file_model = build_file_model_for(namespace)
120
- chunk_model = build_chunk_model_for(namespace)
121
-
122
- file_model.namespace = namespace
123
- chunk_model.namespace = namespace
124
-
125
- file_model.chunk_model = chunk_model
126
- chunk_model.file_model = file_model
127
-
128
- namespace.prefix = prefix
129
- namespace.file_model = file_model
130
- namespace.chunk_model = chunk_model
131
-
132
- namespace.send(:const_set, :File, file_model)
133
- namespace.send(:const_set, :Chunk, chunk_model)
134
-
135
- #at_exit{ file_model.create_indexes rescue nil }
136
- #at_exit{ chunk_model.create_indexes rescue nil }
137
-
138
- const_get(const)
139
- end
140
-
141
- NamespaceMixin = proc do
142
- class << self
143
- attr_accessor :prefix
144
- attr_accessor :file_model
145
- attr_accessor :chunk_model
146
-
147
- def to_s
148
- prefix
149
- end
150
-
151
- def namespace
152
- prefix
153
- end
154
-
155
- def put(readable, attributes = {})
156
- chunks = []
157
- file = file_model.new
158
- attributes.to_options!
159
-
160
- if attributes.has_key?(:id)
161
- file.id = attributes.delete(:id)
162
- end
163
-
164
- if attributes.has_key?(:_id)
165
- file.id = attributes.delete(:_id)
166
- end
167
-
168
- if attributes.has_key?(:content_type)
169
- attributes[:contentType] = attributes.delete(:content_type)
170
- end
171
-
172
- if attributes.has_key?(:upload_date)
173
- attributes[:uploadDate] = attributes.delete(:upload_date)
174
- end
175
-
176
- if attributes.has_key?(:meta_data)
177
- attributes[:metadata] = attributes.delete(:meta_data)
178
- end
179
-
180
- if attributes.has_key?(:aliases)
181
- attributes[:aliases] = Array(attributes.delete(:aliases)).flatten.compact.map{|a| "#{ a }"}
182
- end
183
-
184
- md5 = Digest::MD5.new
185
- length = 0
186
- chunkSize = file.chunkSize
187
- n = 0
188
-
189
- GridFS.reading(readable) do |io|
190
- unless attributes.has_key?(:filename)
191
- attributes[:filename] =
192
- [file.id.to_s, GridFS.extract_basename(io)].join('/').squeeze('/')
193
- end
194
-
195
- unless attributes.has_key?(:contentType)
196
- attributes[:contentType] =
197
- GridFS.extract_content_type(attributes[:filename]) || file.contentType
198
- end
199
-
200
- GridFS.chunking(io, chunkSize) do |buf|
201
- md5 << buf
202
- length += buf.size
203
- chunk = file.chunks.build
204
- chunk.data = binary_for(buf)
205
- chunk.n = n
206
- n += 1
207
- chunk.save!
208
- chunks.push(chunk)
209
- end
210
- end
211
-
212
- attributes[:length] ||= length
213
- attributes[:uploadDate] ||= Time.now.utc
214
- attributes[:md5] ||= md5.hexdigest
215
-
216
- file.update_attributes(attributes)
217
-
218
- file.save!
219
- file
220
- rescue
221
- chunks.each{|chunk| chunk.destroy rescue nil}
222
- raise
223
- end
224
-
225
- def binary_for(*buf)
226
- if defined?(Moped::BSON)
227
- Moped::BSON::Binary.new(:generic, buf.join)
228
- else
229
- BSON::Binary.new(buf.join, :generic)
230
- end
231
- end
232
-
233
- def get(id)
234
- file_model.find(id)
235
- end
236
-
237
- def delete(id)
238
- file_model.find(id).destroy
239
- rescue
240
- nil
241
- end
242
-
243
- def where(conditions = {})
244
- case conditions
245
- when String
246
- file_model.where(:filename => conditions)
247
- else
248
- file_model.where(conditions)
249
- end
250
- end
251
-
252
- def find(*args)
253
- where(*args).first
254
- end
255
-
256
- def [](filename)
257
- file_model.
258
- where(:filename => filename.to_s).
259
- order_by(:uploadDate => :desc).
260
- limit(1).
261
- first
262
- end
263
-
264
- def []=(filename, readable)
265
- put(readable, :filename => filename.to_s)
266
- end
267
-
268
- def clear
269
- file_model.destroy_all
270
- end
271
-
272
- # TODO - opening with a mode = 'w' should return a GridIO::IOProxy
273
- # implementing a StringIO-like interface
274
- #
275
- def open(filename, mode = 'r', &block)
276
- raise NotImplementedError
277
- end
278
- end
279
- end
280
-
281
- ##
282
- #
283
- class Defaults < ::Hash
284
- def method_missing(method, *args, &block)
285
- case method.to_s
286
- when /(.*)=/
287
- key = $1
288
- val = args.first
289
- update(key => val)
290
- else
291
- key = method.to_s
292
- super unless has_key?(key)
293
- fetch(key)
294
- end
295
- end
296
- end
297
-
298
- ##
299
- #
300
- def GridFS.build_file_model_for(namespace)
301
- prefix = namespace.name.split(/::/).last.downcase
302
- file_model_name = "#{ namespace.name }::File"
303
- chunk_model_name = "#{ namespace.name }::Chunk"
304
-
305
- Class.new do
306
- include Mongoid::Document
307
- include Mongoid::Attributes::Dynamic if Mongoid::VERSION.to_i >= 4
308
-
309
- singleton_class = class << self; self; end
310
-
311
- singleton_class.instance_eval do
312
- define_method(:name){ file_model_name }
313
- attr_accessor :namespace
314
- attr_accessor :chunk_model
315
- attr_accessor :defaults
316
- end
317
-
318
- self.store_in :collection => "#{ prefix }.files"
319
-
320
- self.defaults = Defaults.new
321
-
322
- self.defaults.chunkSize = 4 * (mb = 2**20)
323
- self.defaults.contentType = 'application/octet-stream'
324
-
325
- field(:length, :type => Integer, :default => 0)
326
- field(:chunkSize, :type => Integer, :default => defaults.chunkSize)
327
- field(:uploadDate, :type => Time, :default => Time.now.utc)
328
- field(:md5, :type => String, :default => Digest::MD5.hexdigest(''))
329
-
330
- field(:filename, :type => String)
331
- field(:contentType, :type => String, :default => defaults.contentType)
332
- field(:aliases, :type => Array)
333
- field(:metadata) rescue nil
334
-
335
- required = %w( length chunkSize uploadDate md5 )
336
-
337
- required.each do |f|
338
- validates_presence_of(f)
339
- end
340
-
341
- index({:filename => 1})
342
- index({:aliases => 1})
343
- index({:uploadDate => 1})
344
- index({:md5 => 1})
345
-
346
- has_many(:chunks, :class_name => chunk_model_name, :inverse_of => :files, :dependent => :destroy, :order => [:n, :asc])
347
-
348
- def path
349
- filename
350
- end
351
-
352
- def basename
353
- ::File.basename(filename) if filename
354
- end
355
-
356
- def attachment_filename(*paths)
357
- return basename if basename
358
-
359
- if paths.empty?
360
- paths.push('attachment')
361
- paths.push(id.to_s)
362
- paths.push(updateDate.iso8601)
363
- end
364
-
365
- path = paths.join('--')
366
- base = ::File.basename(path).split('.', 2).first
367
- ext = GridFS.extract_extension(contentType)
368
-
369
- "#{ base }.#{ ext }"
370
- end
371
-
372
- def prefix
373
- self.class.namespace.prefix
374
- end
375
-
376
- def each(&block)
377
- fetched, limit = 0, 7
378
-
379
- while fetched < chunks.size
380
- chunks.where(:n.lt => fetched+limit, :n.gte => fetched).
381
- order_by([:n, :asc]).each do |chunk|
382
- block.call(chunk.to_s)
383
- end
384
-
385
- fetched += limit
386
- end
387
- end
388
-
389
- def slice(*args)
390
- case args.first
391
- when Range
392
- range = args.first
393
- first_chunk = (range.min / chunkSize).floor
394
- last_chunk = (range.max / chunkSize).floor
395
- offset = range.min % chunkSize
396
- length = range.max - range.min + 1
397
- when Fixnum
398
- start = args.first
399
- start = self.length + start if start < 0
400
- length = args.size == 2 ? args.last : 1
401
- first_chunk = (start / chunkSize).floor
402
- last_chunk = ((start + length) / chunkSize).floor
403
- offset = start % chunkSize
404
- end
405
-
406
- data = ''
407
-
408
- chunks.where(:n => (first_chunk..last_chunk)).order_by(n: 'asc').each do |chunk|
409
- data << chunk
410
- end
411
-
412
- data[offset, length]
413
- end
414
-
415
- def data
416
- data = ''
417
- each{|chunk| data << chunk}
418
- data
419
- end
420
-
421
- def base64
422
- Array(to_s).pack('m')
423
- end
424
-
425
- def data_uri(options = {})
426
- data = base64.chomp
427
- "data:#{ content_type };base64,".concat(data)
428
- end
429
-
430
- def bytes(&block)
431
- if block
432
- each{|data| block.call(data)}
433
- length
434
- else
435
- bytes = []
436
- each{|data| bytes.push(*data)}
437
- bytes
438
- end
439
- end
440
-
441
- def close
442
- self
443
- end
444
-
445
- def content_type
446
- contentType
447
- end
448
-
449
- def update_date
450
- updateDate
451
- end
452
-
453
- def created_at
454
- updateDate
455
- end
456
-
457
- def namespace
458
- self.class.namespace
459
- end
460
- end
461
- end
462
-
463
- ##
464
- #
465
- def GridFS.build_chunk_model_for(namespace)
466
- prefix = namespace.name.split(/::/).last.downcase
467
- file_model_name = "#{ namespace.name }::File"
468
- chunk_model_name = "#{ namespace.name }::Chunk"
469
-
470
- Class.new do
471
- include Mongoid::Document
472
-
473
- singleton_class = class << self; self; end
474
-
475
- singleton_class.instance_eval do
476
- define_method(:name){ chunk_model_name }
477
- attr_accessor :file_model
478
- attr_accessor :namespace
479
- end
480
-
481
- self.store_in :collection => "#{ prefix }.chunks"
482
-
483
- field(:n, :type => Integer, :default => 0)
484
- field(:data, :type => (defined?(Moped::BSON) ? Moped::BSON::Binary : BSON::Binary))
485
-
486
- belongs_to(:file, :foreign_key => :files_id, :class_name => file_model_name)
487
-
488
- index({:files_id => 1, :n => -1}, :unique => true)
489
-
490
- def namespace
491
- self.class.namespace
492
- end
493
-
494
- def to_s
495
- data.data
496
- end
497
-
498
- alias_method 'to_str', 'to_s'
499
- end
500
- end
501
-
502
- ##
503
- #
504
- def GridFS.reading(arg, &block)
505
- if arg.respond_to?(:read)
506
- rewind(arg) do |io|
507
- block.call(io)
508
- end
509
- else
510
- open(arg.to_s) do |io|
511
- block.call(io)
512
- end
513
- end
514
- end
515
-
516
- def GridFS.chunking(io, chunk_size, &block)
517
- if io.method(:read).arity == 0
518
- data = io.read
519
- i = 0
520
- loop do
521
- offset = i * chunk_size
522
- length = i + chunk_size < data.size ? chunk_size : data.size - offset
523
-
524
- break if offset >= data.size
525
-
526
- buf = data[offset, length]
527
- block.call(buf)
528
- i += 1
529
- end
530
- else
531
- while((buf = io.read(chunk_size)))
532
- block.call(buf)
533
- end
534
- end
535
- end
536
-
537
- def GridFS.rewind(io, &block)
538
- begin
539
- pos = io.pos
540
- io.flush
541
- io.rewind
542
- rescue
543
- nil
544
- end
545
-
546
- begin
547
- block.call(io)
548
- ensure
549
- begin
550
- io.pos = pos
551
- rescue
552
- nil
553
- end
554
- end
555
- end
556
-
557
- def GridFS.extract_basename(object)
558
- filename = nil
559
-
560
- [:original_path, :original_filename, :path, :filename, :pathname].each do |msg|
561
- if object.respond_to?(msg)
562
- filename = object.send(msg)
563
- break
564
- end
565
- end
566
-
567
- filename ? cleanname(filename) : nil
568
- end
569
-
570
- MIME_TYPES = {
571
- 'md' => 'text/x-markdown; charset=UTF-8'
572
- }
573
-
574
- def GridFS.mime_types
575
- MIME_TYPES
576
- end
577
-
578
- def GridFS.extract_content_type(filename, options = {})
579
- options.to_options!
580
-
581
- basename = ::File.basename(filename.to_s)
582
- parts = basename.split('.')
583
- parts.shift
584
- ext = parts.pop
585
-
586
- default =
587
- case
588
- when options[:default]==false
589
- nil
590
- when options[:default]==true
591
- "application/octet-stream"
592
- else
593
- (options[:default] || "application/octet-stream").to_s
594
- end
595
-
596
- content_type = mime_types[ext] || MIME::Types.type_for(::File.basename(filename.to_s)).first
597
-
598
- if content_type
599
- content_type.to_s
600
- else
601
- default
602
- end
603
- end
604
-
605
- def GridFS.extract_extension(content_type)
606
- list = MIME::Types[content_type.to_s]
607
- type = list.first
608
- if type
609
- type.extensions.first
610
- end
611
- end
612
-
613
- def GridFS.cleanname(pathname)
614
- basename = ::File.basename(pathname.to_s)
615
- CGI.unescape(basename).gsub(%r/[^0-9a-zA-Z_@)(~.-]/, '_').gsub(%r/_+/,'_')
616
- end
617
- end
618
-
619
- GridFs = GridFS
620
- GridFS.init!
621
- end
622
-
623
- ##
624
- #
625
- if defined?(Rails)
626
- class Mongoid::GridFS::Engine < Rails::Engine
627
- paths['app/models'] = File.dirname(__FILE__)
628
- end
629
-
630
- module Mongoid::GridFSHelper
631
- def grid_fs_render(grid_fs_file, options = {})
632
- options.to_options!
633
-
634
- if options[:inline] == false or options[:attachment] == true
635
- headers['Content-Disposition'] = "attachment; filename=#{ grid_fs_file.attachment_filename }"
636
- end
637
-
638
- self.content_type = grid_fs_file.content_type
639
- self.response_body = grid_fs_file
640
- end
641
- end
642
-
643
- Mongoid::GridFS::Helper = Mongoid::GridFSHelper
644
- end
1
+ require "mongoid/grid_fs"