module Mongoid

Constants

MIME_TYPES

Public Class Methods

build_chunk_model_for(namespace) click to toggle source
# File lib/mongoid/grid_fs.rb, line 407
def self.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

    store_in collection: "#{prefix}.chunks"

    field(:n, type: Integer, default: 0)
    field(:data, type: (defined?(Moped::BSON) ? 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
build_file_model_for(namespace) click to toggle source
# File lib/mongoid/grid_fs.rb, line 237
def self.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
    include Mongoid::Attributes::Dynamic if Mongoid::VERSION.to_i >= 4

    singleton_class = class << self; self; end

    singleton_class.instance_eval do
      define_method(:name) { file_model_name }
      attr_accessor :namespace
      attr_accessor :chunk_model
      attr_accessor :defaults
    end

    store_in collection: "#{prefix}.files"

    self.defaults = Defaults.new

    defaults.chunkSize = 4 * (mb = 2**20)
    defaults.contentType = 'application/octet-stream'

    field(:length, type: Integer, default: 0)
    field(:chunkSize, type: Integer, default: defaults.chunkSize)
    field(:uploadDate, type: Time, default: Time.now.utc)
    field(:md5, type: String, default: Digest::MD5.hexdigest(''))

    field(:filename, type: String)
    field(:contentType, type: String, default: defaults.contentType)
    field(:aliases, type: Array)
    begin
        field(:metadata)
      rescue
        nil
      end

    required = %w(length chunkSize uploadDate md5)

    required.each do |f|
      validates_presence_of(f)
    end

    index(filename: 1)
    index(aliases: 1)
    index(uploadDate: 1)
    index(md5: 1)

    has_many(:chunks, class_name: chunk_model_name, inverse_of: :files, dependent: :destroy, order: [:n, :asc])

    def path
      filename
    end

    def basename
      ::File.basename(filename) if filename
    end

    def attachment_filename(*paths)
      return basename if basename

      if paths.empty?
        paths.push('attachment')
        paths.push(id.to_s)
        paths.push(updateDate.iso8601)
      end

      path = paths.join('--')
      base = ::File.basename(path).split('.', 2).first
      ext = GridFs.extract_extension(contentType)

      "#{base}.#{ext}"
    end

    def prefix
      self.class.namespace.prefix
    end

    def each
      fetched = 0
      limit = 7

      while fetched < chunks.size
        chunks.where(:n.lt => fetched + limit, :n.gte => fetched)
              .order_by([:n, :asc]).each do |chunk|
          yield(chunk.to_s)
        end

        fetched += limit
      end
    end

    def slice(*args)
      case args.first
      when Range
        range = args.first
        first_chunk = (range.min / chunkSize).floor
        last_chunk = (range.max / chunkSize).floor
        offset = range.min % chunkSize
        length = range.max - range.min + 1
      when Integer
        start = args.first
        start = self.length + start if start < 0
        length = args.size == 2 ? args.last : 1
        first_chunk = (start / chunkSize).floor
        last_chunk = ((start + length) / chunkSize).floor
        offset = start % chunkSize
      end

      data = ''

      chunks.where(n: (first_chunk..last_chunk)).order_by(n: 'asc').each do |chunk|
        data << chunk
      end

      data[offset, length]
    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| yield(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
chunking(io, chunk_size, &block) click to toggle source
# File lib/mongoid/grid_fs.rb, line 458
def self.chunking(io, chunk_size, &block)
  if io.method(:read).arity == 0
    data = io.read
    i = 0
    loop do
      offset = i * chunk_size
      length = i + chunk_size < data.size ? chunk_size : data.size - offset

      break if offset >= data.size

      buf = data[offset, length]
      block.call(buf)
      i += 1
    end
  else
    while (buf = io.read(chunk_size))
      block.call(buf)
    end
  end
end
cleanname(pathname) click to toggle source
# File lib/mongoid/grid_fs.rb, line 552
def self.cleanname(pathname)
  basename = ::File.basename(pathname.to_s)
  CGI.unescape(basename).gsub(/[^0-9a-zA-Z_@)(~.-]/, '_').gsub(/_+/, '_')
end
extract_basename(object) click to toggle source
# File lib/mongoid/grid_fs.rb, line 499
def self.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
extract_content_type(filename, options = {}) click to toggle source
# File lib/mongoid/grid_fs.rb, line 520
def self.extract_content_type(filename, options = {})
  options.to_options!

  basename = ::File.basename(filename.to_s)
  parts = basename.split('.')
  parts.shift
  ext = parts.pop

  default =
    if options[:default] == false
      nil
    elsif options[:default] == true
      'application/octet-stream'
    else
      (options[:default] || 'application/octet-stream').to_s
    end

  content_type = mime_types[ext] || MIME::Types.type_for(::File.basename(filename.to_s)).first

  if content_type
    content_type.to_s
  else
    default
  end
end
extract_extension(content_type) click to toggle source
# File lib/mongoid/grid_fs.rb, line 546
def self.extract_extension(content_type)
  list = MIME::Types[content_type.to_s]
  type = list.first
  type.extensions.first if type
end
mime_types() click to toggle source
# File lib/mongoid/grid_fs.rb, line 516
def self.mime_types
  MIME_TYPES
end
reading(arg, &block) click to toggle source
# File lib/mongoid/grid_fs.rb, line 446
def self.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
rewind(io, &block) click to toggle source
# File lib/mongoid/grid_fs.rb, line 479
def self.rewind(io, &block)
  begin
    pos = io.pos
    io.flush
    io.rewind
  rescue
    nil
  end

  begin
    block.call(io)
  ensure
    begin
      io.pos = pos
    rescue
      nil
    end
  end
end

Public Instance Methods

attachment_filename(*paths) click to toggle source
# File lib/mongoid/grid_fs.rb, line 297
def attachment_filename(*paths)
  return basename if basename

  if paths.empty?
    paths.push('attachment')
    paths.push(id.to_s)
    paths.push(updateDate.iso8601)
  end

  path = paths.join('--')
  base = ::File.basename(path).split('.', 2).first
  ext = GridFs.extract_extension(contentType)

  "#{base}.#{ext}"
end
base64() click to toggle source
# File lib/mongoid/grid_fs.rb, line 363
def base64
  Array(to_s).pack('m')
end
basename() click to toggle source
# File lib/mongoid/grid_fs.rb, line 293
def basename
  ::File.basename(filename) if filename
end
bytes() { |data| ... } click to toggle source
# File lib/mongoid/grid_fs.rb, line 372
def bytes(&block)
  if block
    each { |data| yield(data) }
    length
  else
    bytes = []
    each { |data| bytes.push(*data) }
    bytes
  end
end
close() click to toggle source
# File lib/mongoid/grid_fs.rb, line 383
def close
  self
end
content_type() click to toggle source
# File lib/mongoid/grid_fs.rb, line 387
def content_type
  contentType
end
created_at() click to toggle source
# File lib/mongoid/grid_fs.rb, line 395
def created_at
  updateDate
end
data() click to toggle source
# File lib/mongoid/grid_fs.rb, line 357
def data
  data = ''
  each { |chunk| data << chunk }
  data
end
data_uri(_options = {}) click to toggle source
# File lib/mongoid/grid_fs.rb, line 367
def data_uri(_options = {})
  data = base64.chomp
  "data:#{content_type};base64,".concat(data)
end
each() { |chunk| ... } click to toggle source
# File lib/mongoid/grid_fs.rb, line 317
def each
  fetched = 0
  limit = 7

  while fetched < chunks.size
    chunks.where(:n.lt => fetched + limit, :n.gte => fetched)
          .order_by([:n, :asc]).each do |chunk|
      yield(chunk.to_s)
    end

    fetched += limit
  end
end
namespace() click to toggle source
# File lib/mongoid/grid_fs.rb, line 399
def namespace
  self.class.namespace
end
path() click to toggle source
# File lib/mongoid/grid_fs.rb, line 289
def path
  filename
end
prefix() click to toggle source
# File lib/mongoid/grid_fs.rb, line 313
def prefix
  self.class.namespace.prefix
end
slice(*args) click to toggle source
# File lib/mongoid/grid_fs.rb, line 331
def slice(*args)
  case args.first
  when Range
    range = args.first
    first_chunk = (range.min / chunkSize).floor
    last_chunk = (range.max / chunkSize).floor
    offset = range.min % chunkSize
    length = range.max - range.min + 1
  when Integer
    start = args.first
    start = self.length + start if start < 0
    length = args.size == 2 ? args.last : 1
    first_chunk = (start / chunkSize).floor
    last_chunk = ((start + length) / chunkSize).floor
    offset = start % chunkSize
  end

  data = ''

  chunks.where(n: (first_chunk..last_chunk)).order_by(n: 'asc').each do |chunk|
    data << chunk
  end

  data[offset, length]
end
to_s() click to toggle source
# File lib/mongoid/grid_fs.rb, line 436
def to_s
  data.data
end
update_date() click to toggle source
# File lib/mongoid/grid_fs.rb, line 391
def update_date
  updateDate
end