factor out deps. formatting code.
This commit is contained in:
parent
bf57261f19
commit
b024d8d707
@ -1,419 +1,466 @@
|
|||||||
require "mongoid"
|
|
||||||
require "mime/types"
|
|
||||||
require "digest/md5"
|
|
||||||
require "cgi"
|
|
||||||
|
|
||||||
class GridFS
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
class << GridFS
|
class GridFS
|
||||||
def version
|
const_set :Version, '1.1.0'
|
||||||
"1.0.0"
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_accessor :namespace
|
class << GridFS
|
||||||
attr_accessor :file_model
|
def version
|
||||||
attr_accessor :chunk_model
|
const_get :Version
|
||||||
|
|
||||||
def init!
|
|
||||||
GridFS.build_namespace_for(:Fs)
|
|
||||||
|
|
||||||
GridFS.namespace = Fs
|
|
||||||
GridFS.file_model = Fs.file_model
|
|
||||||
GridFS.chunk_model = Fs.chunk_model
|
|
||||||
|
|
||||||
const_set(:File, Fs.file_model)
|
|
||||||
const_set(:Chunk, Fs.chunk_model)
|
|
||||||
|
|
||||||
to_delegate = %w(
|
|
||||||
put
|
|
||||||
get
|
|
||||||
delete
|
|
||||||
find
|
|
||||||
[]
|
|
||||||
[]=
|
|
||||||
)
|
|
||||||
|
|
||||||
to_delegate.each do |method|
|
|
||||||
class_eval <<-__
|
|
||||||
def GridFS.#{ method }(*args, &block)
|
|
||||||
::GridFS::Fs::#{ method }(*args, &block)
|
|
||||||
end
|
|
||||||
__
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
#
|
|
||||||
def GridFS.namespace_for(prefix)
|
|
||||||
prefix = prefix.to_s.downcase
|
|
||||||
const = "::GridFS::#{ prefix.to_s.camelize }"
|
|
||||||
namespace = const.split(/::/).last
|
|
||||||
const_defined?(namespace) ? const_get(namespace) : build_namespace_for(namespace)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
#
|
|
||||||
def GridFS.build_namespace_for(prefix)
|
|
||||||
prefix = prefix.to_s.downcase
|
|
||||||
const = prefix.camelize
|
|
||||||
|
|
||||||
namespace =
|
|
||||||
Module.new do
|
|
||||||
module_eval(&NamespaceMixin)
|
|
||||||
self
|
|
||||||
end
|
end
|
||||||
|
|
||||||
const_set(const, namespace)
|
def dependencies
|
||||||
|
{
|
||||||
file_model = build_file_model_for(namespace)
|
'mongoid' => [ 'mongoid' , ' >= 3.0.1' ] ,
|
||||||
chunk_model = build_chunk_model_for(namespace)
|
'mime/types' => [ 'mime-types' , ' >= 1.19' ] ,
|
||||||
|
}
|
||||||
file_model.namespace = namespace
|
|
||||||
chunk_model.namespace = namespace
|
|
||||||
|
|
||||||
file_model.chunk_model = chunk_model
|
|
||||||
chunk_model.file_model = file_model
|
|
||||||
|
|
||||||
namespace.prefix = prefix
|
|
||||||
namespace.file_model = file_model
|
|
||||||
namespace.chunk_model = chunk_model
|
|
||||||
|
|
||||||
namespace.send(:const_set, :File, file_model)
|
|
||||||
namespace.send(:const_set, :Chunk, chunk_model)
|
|
||||||
|
|
||||||
#at_exit{ file_model.create_indexes rescue nil }
|
|
||||||
#at_exit{ chunk_model.create_indexes rescue nil }
|
|
||||||
|
|
||||||
const_get(const)
|
|
||||||
end
|
|
||||||
|
|
||||||
NamespaceMixin = proc do
|
|
||||||
class << self
|
|
||||||
attr_accessor :prefix
|
|
||||||
attr_accessor :file_model
|
|
||||||
attr_accessor :chunk_model
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
prefix
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def namespace
|
def libdir(*args, &block)
|
||||||
prefix
|
@libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
|
||||||
end
|
args.empty? ? @libdir : File.join(@libdir, *args)
|
||||||
|
|
||||||
def put(readable, attributes = {})
|
|
||||||
chunks = []
|
|
||||||
file = file_model.new
|
|
||||||
attributes.to_options!
|
|
||||||
|
|
||||||
if attributes.has_key?(:id)
|
|
||||||
file.id = attributes.delete(:id)
|
|
||||||
end
|
|
||||||
|
|
||||||
if attributes.has_key?(:_id)
|
|
||||||
file.id = attributes.delete(:_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
if attributes.has_key?(:content_type)
|
|
||||||
attributes[:contentType] = attributes.delete(:content_type)
|
|
||||||
end
|
|
||||||
|
|
||||||
if attributes.has_key?(:upload_date)
|
|
||||||
attributes[:uploadDate] = attributes.delete(:upload_date)
|
|
||||||
end
|
|
||||||
|
|
||||||
md5 = Digest::MD5.new
|
|
||||||
length = 0
|
|
||||||
chunkSize = file.chunkSize
|
|
||||||
n = 0
|
|
||||||
|
|
||||||
GridFS.reading(readable) do |io|
|
|
||||||
|
|
||||||
filename =
|
|
||||||
attributes[:filename] ||=
|
|
||||||
[file.id.to_s, GridFS.extract_basename(io)].join('/').squeeze('/')
|
|
||||||
|
|
||||||
content_type =
|
|
||||||
attributes[:contentType] ||=
|
|
||||||
GridFS.extract_content_type(filename) || file.contentType
|
|
||||||
|
|
||||||
while((buf = io.read(chunkSize)))
|
|
||||||
md5 << buf
|
|
||||||
length += buf.size
|
|
||||||
chunk = file.chunks.build
|
|
||||||
chunk.data = binary_for(buf)
|
|
||||||
chunk.n = n
|
|
||||||
n += 1
|
|
||||||
chunk.save!
|
|
||||||
chunks.push(chunk)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
attributes[:length] ||= length
|
|
||||||
attributes[:uploadDate] ||= Time.now.utc
|
|
||||||
attributes[:md5] ||= md5.hexdigest
|
|
||||||
|
|
||||||
file.update_attributes(attributes)
|
|
||||||
|
|
||||||
file.save!
|
|
||||||
file
|
|
||||||
ensure
|
ensure
|
||||||
chunks.each{|chunk| chunk.destroy rescue nil} if $!
|
|
||||||
end
|
|
||||||
|
|
||||||
if defined?(Moped)
|
|
||||||
def binary_for(*buf)
|
|
||||||
Moped::BSON::Binary.new(:generic, buf.join)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
def binary_for(buf)
|
|
||||||
BSON::Binary.new(buf.bytes.to_a)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get(id)
|
|
||||||
file_model.find(id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(id)
|
|
||||||
file_model.find(id).destroy
|
|
||||||
rescue
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def where(conditions = {})
|
|
||||||
case conditions
|
|
||||||
when String
|
|
||||||
file_model.where(:filename => conditions)
|
|
||||||
else
|
|
||||||
file_model.where(conditions)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def find(*args)
|
|
||||||
where(*args).first
|
|
||||||
end
|
|
||||||
|
|
||||||
def [](filename)
|
|
||||||
file_model.where(:filename => filename.to_s).first
|
|
||||||
end
|
|
||||||
|
|
||||||
def []=(filename, readable)
|
|
||||||
file = self[filename]
|
|
||||||
file.destroy if file
|
|
||||||
put(readable, :filename => filename.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO - opening with a mode = 'w' should return a GridIO::IOProxy
|
|
||||||
# implementing a StringIO-like interface
|
|
||||||
#
|
|
||||||
def open(filename, mode = 'r', &block)
|
|
||||||
raise NotImplementedError
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
#
|
|
||||||
def GridFS.build_file_model_for(namespace)
|
|
||||||
prefix = namespace.name.split(/::/).last.downcase
|
|
||||||
file_model_name = "#{ namespace.name }::File"
|
|
||||||
chunk_model_name = "#{ namespace.name }::Chunk"
|
|
||||||
|
|
||||||
Class.new do
|
|
||||||
include Mongoid::Document
|
|
||||||
|
|
||||||
singleton_class = class << self; self; end
|
|
||||||
|
|
||||||
singleton_class.instance_eval do
|
|
||||||
define_method(:name){ file_model_name }
|
|
||||||
attr_accessor :chunk_model
|
|
||||||
attr_accessor :namespace
|
|
||||||
end
|
|
||||||
|
|
||||||
self.default_collection_name = "#{ prefix }.files"
|
|
||||||
|
|
||||||
field(:filename, :type => String)
|
|
||||||
field(:contentType, :type => String, :default => 'application/octet-stream')
|
|
||||||
|
|
||||||
field(:length, :type => Integer, :default => 0)
|
|
||||||
field(:chunkSize, :type => Integer, :default => (256 * (2 ** 20)))
|
|
||||||
field(:uploadDate, :type => Date, :default => Time.now.utc)
|
|
||||||
field(:md5, :type => String, :default => Digest::MD5.hexdigest(''))
|
|
||||||
|
|
||||||
%w( filename contentType length chunkSize uploadDate md5 ).each do |f|
|
|
||||||
validates_presence_of(f)
|
|
||||||
end
|
|
||||||
validates_uniqueness_of(:filename)
|
|
||||||
|
|
||||||
has_many(:chunks, :class_name => chunk_model_name, :inverse_of => :files, :dependent => :destroy, :order => [:n, :asc])
|
|
||||||
|
|
||||||
index({:filename => 1}, :unique => true)
|
|
||||||
|
|
||||||
def path
|
|
||||||
filename
|
|
||||||
end
|
|
||||||
|
|
||||||
def basename
|
|
||||||
::File.basename(filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
def prefix
|
|
||||||
self.class.namespace.prefix
|
|
||||||
end
|
|
||||||
|
|
||||||
def each(&block)
|
|
||||||
chunks.all.order_by([:n, :asc]).each do |chunk|
|
|
||||||
block.call(chunk.to_s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def data
|
|
||||||
data = ''
|
|
||||||
each{|chunk| data << chunk}
|
|
||||||
data
|
|
||||||
end
|
|
||||||
|
|
||||||
def base64
|
|
||||||
Array(to_s).pack('m')
|
|
||||||
end
|
|
||||||
|
|
||||||
def data_uri(options = {})
|
|
||||||
data = base64.chomp
|
|
||||||
"data:#{ content_type };base64,".concat(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
def bytes(&block)
|
|
||||||
if block
|
if block
|
||||||
each{|data| block.call(data)}
|
begin
|
||||||
length
|
$LOAD_PATH.unshift(@libdir)
|
||||||
else
|
block.call()
|
||||||
bytes = []
|
ensure
|
||||||
each{|data| bytes.push(*data)}
|
$LOAD_PATH.shift()
|
||||||
bytes
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def close
|
def load(*libs)
|
||||||
self
|
libs = libs.join(' ').scan(/[^\s+]+/)
|
||||||
end
|
libdir{ libs.each{|lib| Kernel.load(lib) } }
|
||||||
|
|
||||||
def content_type
|
|
||||||
contentType
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_date
|
|
||||||
updateDate
|
|
||||||
end
|
|
||||||
|
|
||||||
def created_at
|
|
||||||
updateDate
|
|
||||||
end
|
|
||||||
|
|
||||||
def namespace
|
|
||||||
self.class.namespace
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
#
|
|
||||||
def GridFS.build_chunk_model_for(namespace)
|
|
||||||
prefix = namespace.name.split(/::/).last.downcase
|
|
||||||
file_model_name = "#{ namespace.name }::File"
|
|
||||||
chunk_model_name = "#{ namespace.name }::Chunk"
|
|
||||||
|
|
||||||
Class.new do
|
|
||||||
include Mongoid::Document
|
|
||||||
|
|
||||||
singleton_class = class << self; self; end
|
|
||||||
|
|
||||||
singleton_class.instance_eval do
|
|
||||||
define_method(:name){ chunk_model_name }
|
|
||||||
attr_accessor :file_model
|
|
||||||
attr_accessor :namespace
|
|
||||||
end
|
|
||||||
|
|
||||||
self.default_collection_name = "#{ prefix }.chunks"
|
|
||||||
|
|
||||||
field(:n, :type => Integer, :default => 0)
|
|
||||||
field(:data, :type => (defined?(Moped) ? Moped::BSON::Binary : BSON::Binary))
|
|
||||||
|
|
||||||
belongs_to(:file, :foreign_key => :files_id, :class_name => file_model_name)
|
|
||||||
|
|
||||||
index({:files_id => 1, :n => -1}, :unique => true)
|
|
||||||
|
|
||||||
def namespace
|
|
||||||
self.class.namespace
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
data.data
|
|
||||||
end
|
|
||||||
|
|
||||||
alias_method 'to_str', 'to_s'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
#
|
|
||||||
def GridFS.reading(arg, &block)
|
|
||||||
if arg.respond_to?(:read)
|
|
||||||
rewind(arg) do |io|
|
|
||||||
block.call(io)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
open(arg.to_s) do |io|
|
|
||||||
block.call(io)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def GridFS.rewind(io, &block)
|
|
||||||
begin
|
begin
|
||||||
pos = io.pos
|
require 'rubygems'
|
||||||
io.flush
|
rescue LoadError
|
||||||
io.rewind
|
|
||||||
rescue
|
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
if defined?(gem)
|
||||||
block.call(io)
|
dependencies.each do |lib, dependency|
|
||||||
ensure
|
gem(*dependency)
|
||||||
|
require(lib)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require "digest/md5"
|
||||||
|
require "cgi"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
class GridFS
|
||||||
|
class << GridFS
|
||||||
|
attr_accessor :namespace
|
||||||
|
attr_accessor :file_model
|
||||||
|
attr_accessor :chunk_model
|
||||||
|
|
||||||
|
def init!
|
||||||
|
GridFS.build_namespace_for(:Fs)
|
||||||
|
|
||||||
|
GridFS.namespace = Fs
|
||||||
|
GridFS.file_model = Fs.file_model
|
||||||
|
GridFS.chunk_model = Fs.chunk_model
|
||||||
|
|
||||||
|
const_set(:File, Fs.file_model)
|
||||||
|
const_set(:Chunk, Fs.chunk_model)
|
||||||
|
|
||||||
|
to_delegate = %w(
|
||||||
|
put
|
||||||
|
get
|
||||||
|
delete
|
||||||
|
find
|
||||||
|
[]
|
||||||
|
[]=
|
||||||
|
)
|
||||||
|
|
||||||
|
to_delegate.each do |method|
|
||||||
|
class_eval <<-__
|
||||||
|
def GridFS.#{ method }(*args, &block)
|
||||||
|
::GridFS::Fs::#{ method }(*args, &block)
|
||||||
|
end
|
||||||
|
__
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
def GridFS.namespace_for(prefix)
|
||||||
|
prefix = prefix.to_s.downcase
|
||||||
|
const = "::GridFS::#{ prefix.to_s.camelize }"
|
||||||
|
namespace = const.split(/::/).last
|
||||||
|
const_defined?(namespace) ? const_get(namespace) : build_namespace_for(namespace)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
def GridFS.build_namespace_for(prefix)
|
||||||
|
prefix = prefix.to_s.downcase
|
||||||
|
const = prefix.camelize
|
||||||
|
|
||||||
|
namespace =
|
||||||
|
Module.new do
|
||||||
|
module_eval(&NamespaceMixin)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
const_set(const, namespace)
|
||||||
|
|
||||||
|
file_model = build_file_model_for(namespace)
|
||||||
|
chunk_model = build_chunk_model_for(namespace)
|
||||||
|
|
||||||
|
file_model.namespace = namespace
|
||||||
|
chunk_model.namespace = namespace
|
||||||
|
|
||||||
|
file_model.chunk_model = chunk_model
|
||||||
|
chunk_model.file_model = file_model
|
||||||
|
|
||||||
|
namespace.prefix = prefix
|
||||||
|
namespace.file_model = file_model
|
||||||
|
namespace.chunk_model = chunk_model
|
||||||
|
|
||||||
|
namespace.send(:const_set, :File, file_model)
|
||||||
|
namespace.send(:const_set, :Chunk, chunk_model)
|
||||||
|
|
||||||
|
#at_exit{ file_model.create_indexes rescue nil }
|
||||||
|
#at_exit{ chunk_model.create_indexes rescue nil }
|
||||||
|
|
||||||
|
const_get(const)
|
||||||
|
end
|
||||||
|
|
||||||
|
NamespaceMixin = proc do
|
||||||
|
class << self
|
||||||
|
attr_accessor :prefix
|
||||||
|
attr_accessor :file_model
|
||||||
|
attr_accessor :chunk_model
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
def namespace
|
||||||
|
prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
def put(readable, attributes = {})
|
||||||
|
chunks = []
|
||||||
|
file = file_model.new
|
||||||
|
attributes.to_options!
|
||||||
|
|
||||||
|
if attributes.has_key?(:id)
|
||||||
|
file.id = attributes.delete(:id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if attributes.has_key?(:_id)
|
||||||
|
file.id = attributes.delete(:_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if attributes.has_key?(:content_type)
|
||||||
|
attributes[:contentType] = attributes.delete(:content_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
if attributes.has_key?(:upload_date)
|
||||||
|
attributes[:uploadDate] = attributes.delete(:upload_date)
|
||||||
|
end
|
||||||
|
|
||||||
|
md5 = Digest::MD5.new
|
||||||
|
length = 0
|
||||||
|
chunkSize = file.chunkSize
|
||||||
|
n = 0
|
||||||
|
|
||||||
|
GridFS.reading(readable) do |io|
|
||||||
|
|
||||||
|
filename =
|
||||||
|
attributes[:filename] ||=
|
||||||
|
[file.id.to_s, GridFS.extract_basename(io)].join('/').squeeze('/')
|
||||||
|
|
||||||
|
content_type =
|
||||||
|
attributes[:contentType] ||=
|
||||||
|
GridFS.extract_content_type(filename) || file.contentType
|
||||||
|
|
||||||
|
while((buf = io.read(chunkSize)))
|
||||||
|
md5 << buf
|
||||||
|
length += buf.size
|
||||||
|
chunk = file.chunks.build
|
||||||
|
chunk.data = binary_for(buf)
|
||||||
|
chunk.n = n
|
||||||
|
n += 1
|
||||||
|
chunk.save!
|
||||||
|
chunks.push(chunk)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
attributes[:length] ||= length
|
||||||
|
attributes[:uploadDate] ||= Time.now.utc
|
||||||
|
attributes[:md5] ||= md5.hexdigest
|
||||||
|
|
||||||
|
file.update_attributes(attributes)
|
||||||
|
|
||||||
|
file.save!
|
||||||
|
file
|
||||||
|
ensure
|
||||||
|
chunks.each{|chunk| chunk.destroy rescue nil} if $!
|
||||||
|
end
|
||||||
|
|
||||||
|
if defined?(Moped)
|
||||||
|
def binary_for(*buf)
|
||||||
|
Moped::BSON::Binary.new(:generic, buf.join)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
def binary_for(buf)
|
||||||
|
BSON::Binary.new(buf.bytes.to_a)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(id)
|
||||||
|
file_model.find(id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(id)
|
||||||
|
file_model.find(id).destroy
|
||||||
|
rescue
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def where(conditions = {})
|
||||||
|
case conditions
|
||||||
|
when String
|
||||||
|
file_model.where(:filename => conditions)
|
||||||
|
else
|
||||||
|
file_model.where(conditions)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find(*args)
|
||||||
|
where(*args).first
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](filename)
|
||||||
|
file_model.where(:filename => filename.to_s).first
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(filename, readable)
|
||||||
|
file = self[filename]
|
||||||
|
file.destroy if file
|
||||||
|
put(readable, :filename => filename.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO - opening with a mode = 'w' should return a GridIO::IOProxy
|
||||||
|
# implementing a StringIO-like interface
|
||||||
|
#
|
||||||
|
def open(filename, mode = 'r', &block)
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
def GridFS.build_file_model_for(namespace)
|
||||||
|
prefix = namespace.name.split(/::/).last.downcase
|
||||||
|
file_model_name = "#{ namespace.name }::File"
|
||||||
|
chunk_model_name = "#{ namespace.name }::Chunk"
|
||||||
|
|
||||||
|
Class.new do
|
||||||
|
include Mongoid::Document
|
||||||
|
|
||||||
|
singleton_class = class << self; self; end
|
||||||
|
|
||||||
|
singleton_class.instance_eval do
|
||||||
|
define_method(:name){ file_model_name }
|
||||||
|
attr_accessor :chunk_model
|
||||||
|
attr_accessor :namespace
|
||||||
|
end
|
||||||
|
|
||||||
|
self.default_collection_name = "#{ prefix }.files"
|
||||||
|
|
||||||
|
field(:filename, :type => String)
|
||||||
|
field(:contentType, :type => String, :default => 'application/octet-stream')
|
||||||
|
|
||||||
|
field(:length, :type => Integer, :default => 0)
|
||||||
|
field(:chunkSize, :type => Integer, :default => (256 * (2 ** 20)))
|
||||||
|
field(:uploadDate, :type => Date, :default => Time.now.utc)
|
||||||
|
field(:md5, :type => String, :default => Digest::MD5.hexdigest(''))
|
||||||
|
|
||||||
|
%w( filename contentType length chunkSize uploadDate md5 ).each do |f|
|
||||||
|
validates_presence_of(f)
|
||||||
|
end
|
||||||
|
validates_uniqueness_of(:filename)
|
||||||
|
|
||||||
|
has_many(:chunks, :class_name => chunk_model_name, :inverse_of => :files, :dependent => :destroy, :order => [:n, :asc])
|
||||||
|
|
||||||
|
index({:filename => 1}, :unique => true)
|
||||||
|
|
||||||
|
def path
|
||||||
|
filename
|
||||||
|
end
|
||||||
|
|
||||||
|
def basename
|
||||||
|
::File.basename(filename)
|
||||||
|
end
|
||||||
|
|
||||||
|
def prefix
|
||||||
|
self.class.namespace.prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
def each(&block)
|
||||||
|
chunks.all.order_by([:n, :asc]).each do |chunk|
|
||||||
|
block.call(chunk.to_s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def data
|
||||||
|
data = ''
|
||||||
|
each{|chunk| data << chunk}
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
def base64
|
||||||
|
Array(to_s).pack('m')
|
||||||
|
end
|
||||||
|
|
||||||
|
def data_uri(options = {})
|
||||||
|
data = base64.chomp
|
||||||
|
"data:#{ content_type };base64,".concat(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bytes(&block)
|
||||||
|
if block
|
||||||
|
each{|data| block.call(data)}
|
||||||
|
length
|
||||||
|
else
|
||||||
|
bytes = []
|
||||||
|
each{|data| bytes.push(*data)}
|
||||||
|
bytes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def content_type
|
||||||
|
contentType
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_date
|
||||||
|
updateDate
|
||||||
|
end
|
||||||
|
|
||||||
|
def created_at
|
||||||
|
updateDate
|
||||||
|
end
|
||||||
|
|
||||||
|
def namespace
|
||||||
|
self.class.namespace
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
def GridFS.build_chunk_model_for(namespace)
|
||||||
|
prefix = namespace.name.split(/::/).last.downcase
|
||||||
|
file_model_name = "#{ namespace.name }::File"
|
||||||
|
chunk_model_name = "#{ namespace.name }::Chunk"
|
||||||
|
|
||||||
|
Class.new do
|
||||||
|
include Mongoid::Document
|
||||||
|
|
||||||
|
singleton_class = class << self; self; end
|
||||||
|
|
||||||
|
singleton_class.instance_eval do
|
||||||
|
define_method(:name){ chunk_model_name }
|
||||||
|
attr_accessor :file_model
|
||||||
|
attr_accessor :namespace
|
||||||
|
end
|
||||||
|
|
||||||
|
self.default_collection_name = "#{ prefix }.chunks"
|
||||||
|
|
||||||
|
field(:n, :type => Integer, :default => 0)
|
||||||
|
field(:data, :type => (defined?(Moped) ? Moped::BSON::Binary : BSON::Binary))
|
||||||
|
|
||||||
|
belongs_to(:file, :foreign_key => :files_id, :class_name => file_model_name)
|
||||||
|
|
||||||
|
index({:files_id => 1, :n => -1}, :unique => true)
|
||||||
|
|
||||||
|
def namespace
|
||||||
|
self.class.namespace
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
data.data
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method 'to_str', 'to_s'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
def GridFS.reading(arg, &block)
|
||||||
|
if arg.respond_to?(:read)
|
||||||
|
rewind(arg) do |io|
|
||||||
|
block.call(io)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
open(arg.to_s) do |io|
|
||||||
|
block.call(io)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def GridFS.rewind(io, &block)
|
||||||
begin
|
begin
|
||||||
io.pos = pos
|
pos = io.pos
|
||||||
|
io.flush
|
||||||
|
io.rewind
|
||||||
rescue
|
rescue
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def GridFS.extract_basename(object)
|
begin
|
||||||
filename = nil
|
block.call(io)
|
||||||
[:original_path, :original_filename, :path, :filename, :pathname].each do |msg|
|
ensure
|
||||||
if object.respond_to?(msg)
|
begin
|
||||||
filename = object.send(msg)
|
io.pos = pos
|
||||||
break
|
rescue
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
filename ? cleanname(filename) : nil
|
|
||||||
|
def GridFS.extract_basename(object)
|
||||||
|
filename = nil
|
||||||
|
[:original_path, :original_filename, :path, :filename, :pathname].each do |msg|
|
||||||
|
if object.respond_to?(msg)
|
||||||
|
filename = object.send(msg)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
filename ? cleanname(filename) : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def GridFS.extract_content_type(filename)
|
||||||
|
content_type = MIME::Types.type_for(::File.basename(filename.to_s)).first
|
||||||
|
content_type.to_s if content_type
|
||||||
|
end
|
||||||
|
|
||||||
|
def GridFS.cleanname(pathname)
|
||||||
|
basename = ::File.basename(pathname.to_s)
|
||||||
|
CGI.unescape(basename).gsub(%r/[^0-9a-zA-Z_@)(~.-]/, '_').gsub(%r/_+/,'_')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def GridFS.extract_content_type(filename)
|
##
|
||||||
content_type = MIME::Types.type_for(::File.basename(filename.to_s)).first
|
#
|
||||||
content_type.to_s if content_type
|
GridFs = GridFS
|
||||||
end
|
|
||||||
|
|
||||||
def GridFS.cleanname(pathname)
|
GridFS.init!
|
||||||
basename = ::File.basename(pathname.to_s)
|
|
||||||
CGI.unescape(basename).gsub(%r/[^0-9a-zA-Z_@)(~.-]/, '_').gsub(%r/_+/,'_')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
GridFs = GridFS
|
|
||||||
|
|
||||||
GridFS.init!
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
Gem::Specification::new do |spec|
|
Gem::Specification::new do |spec|
|
||||||
spec.name = "mongoid-grid_fs"
|
spec.name = "mongoid-grid_fs"
|
||||||
spec.version = "1.0.0"
|
spec.version = "1.1.0"
|
||||||
spec.platform = Gem::Platform::RUBY
|
spec.platform = Gem::Platform::RUBY
|
||||||
spec.summary = "mongoid-grid_fs"
|
spec.summary = "mongoid-grid_fs"
|
||||||
spec.description = "a mongoid 3/moped compatible implementation of the grid_fs specification"
|
spec.description = "a mongoid 3/moped compatible implementation of the grid_fs specification"
|
||||||
@ -26,6 +26,10 @@ Gem::Specification::new do |spec|
|
|||||||
spec.test_files = nil
|
spec.test_files = nil
|
||||||
|
|
||||||
|
|
||||||
|
spec.add_dependency(*["mongoid", " >= 3.0.1"])
|
||||||
|
|
||||||
|
spec.add_dependency(*["mime-types", " >= 1.19"])
|
||||||
|
|
||||||
|
|
||||||
spec.extensions.push(*[])
|
spec.extensions.push(*[])
|
||||||
|
|
||||||
|
Binary file not shown.
@ -142,6 +142,8 @@ Testing GridFs do
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
def object_id_re
|
def object_id_re
|
||||||
%r| \w{#{ BSON::ObjectId.new.to_s.size }} |iomx
|
object_id = defined?(Moped) ? Moped::BSON::ObjectId.new : BSON::ObjectId.new
|
||||||
|
|
||||||
|
%r| \w{#{ object_id.to_s.size }} |iomx
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user