-
-
Save yfeldblum/3117687 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "mongo" | |
require "open-uri" | |
require "mime/types" | |
require "digest/md5" | |
class Upload | |
## | |
# | |
include Mongoid::Document | |
include Mongoid::Timestamps | |
## | |
# | |
belongs_to(:context, :polymorphic => true) | |
## | |
# | |
field(:basename) | |
field(:tmp, :type => Boolean, :default => false) | |
## | |
# | |
validates_presence_of(:basename) | |
## | |
# | |
class Variant | |
## | |
# | |
include Mongoid::Document | |
include Mongoid::Timestamps | |
## | |
# | |
field(:name, :default => 'original', :type => String) | |
field(:default, :default => false) | |
field(:content_type, :type => String) | |
field(:length, :type => Integer) | |
field(:md5, :type => String) | |
field(:grid_fs, :type => Hash) | |
field(:s3, :type => Hash) | |
## | |
# | |
validates_presence_of(:name) | |
validates_presence_of(:content_type) | |
validates_presence_of(:length) | |
validates_presence_of(:md5) | |
## | |
# | |
embedded_in(:upload, :class_name => '::Upload') | |
## | |
# | |
before_destroy do |variant| | |
if variant.grid_fs? | |
variant.grid_fs_file.destroy rescue nil | |
end | |
if variant.s3? | |
variant.s3_object.destroy rescue nil | |
end | |
true | |
end | |
## | |
# | |
def grid_fs_file | |
if grid_fs? | |
namespace = GridFS.namespace_for(grid_fs['prefix']) | |
namespace.get(grid_fs['file_id']) | |
end | |
end | |
def grid_fs? | |
!grid_fs.blank? | |
end | |
## | |
# | |
def s3? | |
!s3.blank? | |
end | |
def s3_object | |
nil | |
end | |
## | |
# | |
def url_for(*args, &block) | |
case | |
when grid_fs? | |
"#{ Upload.route }/#{ upload.id }/#{ name }/#{ basename }" | |
end | |
end | |
def url(*args, &block) | |
url_for(*args, &block) | |
end | |
def to_s(*args, &block) | |
if grid_fs? | |
return grid_fs_file.to_s(*args, &block) | |
end | |
if s3? | |
return s3_object.to_s(*args, &block) | |
end | |
end | |
def image? | |
content_type.to_s.split('/').include?('image') | |
end | |
end | |
## | |
# | |
embeds_many(:variants, :class_name => '::Upload::Variant') do | |
def find_by_name(name) | |
all.detect{|variant| variant.name.to_s == name.to_s} | |
end | |
alias_method(:for, :find_by_name) | |
def original | |
find_by_name(:original) | |
end | |
def default | |
all.detect{|variant| variant.default} || original | |
end | |
def url_for(*args, &block) | |
options = args.extract_options!.to_options! | |
name = args.shift || options.delete(:name) | |
variant = find_by_name(name) || original | |
variant.url_for(options) if variant | |
end | |
def url(*args, &block) | |
url_for(*args, &block) | |
end | |
end | |
before_destroy do |upload| | |
upload.variants.destroy_all | |
true | |
end | |
## | |
# upload support | |
# | |
# /system/uploads/ 1234567890/original/logo.png | |
# /system/uploads/ 1234567890/small/logo.png | |
# | |
def add_variant(name, io_or_path, options = {}) | |
name = name.to_s.downcase.to_sym | |
basename = Upload.extract_basename(io_or_path) | |
content_type = Upload.extract_content_type(basename) | |
filename = "#{ Upload.route }/#{ id }/#{ name }/#{ basename }" | |
options.to_options! | |
options[:filename] = filename | |
grid_fs_file = Upload.grid.put(io_or_path, options) | |
attributes = { | |
'name' => name, | |
'basename' => basename, | |
'grid_fs' => { | |
'prefix' => grid_fs_file.namespace.prefix, | |
'file_id' => grid_fs_file.id | |
}, | |
'content_type' => grid_fs_file.content_type, | |
'length' => grid_fs_file.length, | |
'md5' => grid_fs_file.md5 | |
} | |
variant = variants.build(attributes) | |
self.basename = variant.basename if self.basename.blank? | |
variant | |
end | |
def Upload.upload(*args, &block) | |
new.tap{|u| u.upload(*args, &block)} | |
end | |
def Upload.upload!(*args, &block) | |
new.tap{|u| u.upload!(*args, &block)} | |
end | |
def upload(*args, &block) | |
variants.destroy_all | |
add_variant(:original, *args, &block) | |
end | |
def upload!(*args, &block) | |
upload(*args, &block) | |
ensure | |
save! | |
end | |
def io=(io) | |
upload(io) | |
end | |
def path=(path) | |
upload(path) | |
end | |
def url_for(*args, &block) | |
variants.url_for(*args, &block) | |
end | |
def url(*args, &block) | |
url_for(*args, &block) | |
end | |
def urls(*args, &block) | |
variants.map{|variant| variant.url(*args, &block)} | |
end | |
def grid_fs_files | |
variants.map{|variant| variant.grid_fs_file}.compact | |
end | |
def grid_fs_file_chunks | |
grid_fs_files.map{|grid_fs_file| grid_fs_file.chunks}.compact.flatten | |
end | |
def image? | |
variants.any?{|variant| variant.image?} | |
end | |
def Upload.route | |
"/system/uploads" | |
end | |
def Upload.id | |
new.id | |
end | |
def Upload.grid | |
@grid ||= GridFS.namespace_for(:fs) | |
end | |
def Upload.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 | |
cleanname(filename || object.to_s) | |
end | |
def Upload.cleanname(pathname) | |
basename = ::File.basename(pathname.to_s) | |
CGI.unescape(basename).gsub(%r/[^0-9a-zA-Z_@)(~.-]/, '_').gsub(%r/_+/,'_') | |
end | |
def Upload.extract_content_type(filename) | |
content_type = MIME::Types.type_for(::File.basename(filename.to_s)).first | |
content_type.to_s if content_type | |
end | |
## | |
# tmpwatch support - nukes old files... | |
# | |
def Upload.tmpwatch!(conditions = {}) | |
conditions.to_options! | |
if conditions.empty? | |
conditions.update(:updated_at.lt => 1.week.ago) | |
end | |
Upload.where(conditions.merge(:tmp => true)).each do |upload| | |
upload.destroy unless upload.context_id | |
end | |
end | |
at_exit{ Upload.tmpwatch! } if Rails.env.production? | |
end | |
class Upload | |
class SIO < StringIO | |
attr_accessor :pathname | |
def initialize(string, options = {}) | |
super(string) | |
ensure | |
@pathname = options[:pathname]||options['pathname'] | |
end | |
def basename | |
File.basename(pathname) | |
end | |
def dirname | |
File.dirname(pathname) | |
end | |
end | |
def Upload.sio(*args, &block) | |
SIO.new(*args, &block) | |
end | |
end | |
class Upload | |
## | |
# dao upload cache support | |
# | |
# mount(::Upload::Cache, :upload, :placeholder => 'han-solo.jpg') | |
# | |
def Upload.cache!(upload) | |
case | |
when upload.respond_to?(:read) | |
create!(:io => upload, :tmp => true) | |
else | |
create!(:path => upload.to_s, :tmp => true) | |
end | |
end | |
def cache!(io) | |
self.tmp = true | |
upload(io) | |
save! | |
self | |
end | |
def cache | |
id.to_s if persisted? | |
end | |
class Cache < ::Map | |
attr :conducer | |
attr :upload | |
attr :key | |
class << self | |
def mount(*args, &block) | |
new(*args, &block) | |
end | |
end | |
def initialize(conducer, *args) | |
@conducer = conducer | |
@options = Map.options_for!(args) | |
@key = args.flatten | |
@upload = Upload.new :placeholder => @options[:placeholder] | |
update(:url => @upload.placeholder.url, :file => nil, :cache => nil) | |
end | |
def _set(params = {}) | |
if !params.blank? | |
if !params[:file].blank? | |
file = params[:file] | |
if !params[:cache].blank? | |
cache = params[:cache] | |
begin | |
@upload = Upload.find(cache).cache!(file) | |
rescue | |
@upload = Upload.cache!(file) | |
end | |
else | |
@upload = Upload.cache!(file) | |
end | |
else | |
if !params[:cache].blank? | |
cache = params[:cache] | |
begin | |
@upload = Upload.find(cache) | |
rescue | |
nil | |
end | |
end | |
end | |
end | |
unless @upload.new_record? | |
update( | |
:url => @upload.url, | |
:cache => @upload.cache | |
) | |
end | |
end | |
def value | |
@upload.id unless @upload.new_record? | |
end | |
def clear! | |
@upload.set(:tmp => false) if @upload.persisted? | |
end | |
def object | |
@upload | |
end | |
def _key | |
@key | |
end | |
def _value | |
@upload | |
end | |
def _clear | |
clear! | |
end | |
end | |
## | |
# placeholder support | |
# | |
class Placeholder < ::String | |
def Placeholder.route | |
"/assets" | |
end | |
def Placeholder.root | |
File.join(Rails.root, "app", "assets", "placeholders") | |
end | |
attr_accessor :url | |
attr_accessor :path | |
def initialize(placeholder = '', options = {}) | |
replace(placeholder.to_s) | |
options.to_options! | |
@url = options[:url] || default_url | |
@path = options[:path] || default_path | |
end | |
def default_url | |
return nil if blank? | |
absolute? ? self : File.join(Placeholder.route, self) | |
end | |
def default_path | |
return nil if blank? | |
absolute? ? nil : File.join(Placeholder.root, self) | |
end | |
def basename | |
File.basename(self) | |
end | |
def absolute? | |
self =~ %r|\A([^:/]++:/)?/| | |
end | |
end | |
def placeholder | |
@placeholder ||= Placeholder.new | |
end | |
def placeholder=(placeholder) | |
@placeholder = placeholder.is_a?(Placeholder) ? placeholder : Placeholder.new(placeholder) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment