durran-carrierwave 0.3.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Generators +4 -0
- data/History.txt +66 -0
- data/LICENSE +8 -0
- data/Manifest.txt +89 -0
- data/README.rdoc +342 -0
- data/Rakefile +30 -0
- data/carrierwave.gemspec +57 -0
- data/cucumber.yml +2 -0
- data/features/caching.feature +28 -0
- data/features/file_storage.feature +37 -0
- data/features/file_storage_overridden_filename.feature +38 -0
- data/features/file_storage_overridden_store_dir.feature +38 -0
- data/features/file_storage_reversing_processor.feature +43 -0
- data/features/fixtures/bork.txt +1 -0
- data/features/fixtures/monkey.txt +1 -0
- data/features/mount_activerecord.feature +46 -0
- data/features/mount_datamapper.feature +46 -0
- data/features/step_definitions/activerecord_steps.rb +22 -0
- data/features/step_definitions/caching_steps.rb +14 -0
- data/features/step_definitions/datamapper_steps.rb +29 -0
- data/features/step_definitions/file_steps.rb +42 -0
- data/features/step_definitions/general_steps.rb +80 -0
- data/features/step_definitions/mount_steps.rb +19 -0
- data/features/step_definitions/store_steps.rb +18 -0
- data/features/support/activerecord.rb +30 -0
- data/features/support/datamapper.rb +7 -0
- data/features/support/env.rb +35 -0
- data/features/versions_basics.feature +50 -0
- data/features/versions_nested_versions.feature +70 -0
- data/features/versions_overridden_filename.feature +51 -0
- data/features/versions_overriden_store_dir.feature +41 -0
- data/lib/carrierwave.rb +145 -0
- data/lib/carrierwave/compatibility/paperclip.rb +95 -0
- data/lib/carrierwave/core_ext/blank.rb +46 -0
- data/lib/carrierwave/core_ext/inheritable_attributes.rb +104 -0
- data/lib/carrierwave/core_ext/module_setup.rb +51 -0
- data/lib/carrierwave/mount.rb +332 -0
- data/lib/carrierwave/orm/activerecord.rb +73 -0
- data/lib/carrierwave/orm/datamapper.rb +27 -0
- data/lib/carrierwave/orm/mongomapper.rb +27 -0
- data/lib/carrierwave/orm/sequel.rb +57 -0
- data/lib/carrierwave/processing/image_science.rb +72 -0
- data/lib/carrierwave/processing/rmagick.rb +286 -0
- data/lib/carrierwave/sanitized_file.rb +272 -0
- data/lib/carrierwave/storage/abstract.rb +32 -0
- data/lib/carrierwave/storage/file.rb +50 -0
- data/lib/carrierwave/storage/s3.rb +215 -0
- data/lib/carrierwave/test/matchers.rb +114 -0
- data/lib/carrierwave/uploader.rb +43 -0
- data/lib/carrierwave/uploader/cache.rb +116 -0
- data/lib/carrierwave/uploader/callbacks.rb +42 -0
- data/lib/carrierwave/uploader/default_path.rb +23 -0
- data/lib/carrierwave/uploader/extension_whitelist.rb +37 -0
- data/lib/carrierwave/uploader/mountable.rb +39 -0
- data/lib/carrierwave/uploader/paths.rb +27 -0
- data/lib/carrierwave/uploader/processing.rb +81 -0
- data/lib/carrierwave/uploader/proxy.rb +62 -0
- data/lib/carrierwave/uploader/remove.rb +23 -0
- data/lib/carrierwave/uploader/store.rb +156 -0
- data/lib/carrierwave/uploader/url.rb +24 -0
- data/lib/carrierwave/uploader/versions.rb +147 -0
- data/lib/generators/uploader_generator.rb +22 -0
- data/rails_generators/uploader/USAGE +2 -0
- data/rails_generators/uploader/templates/uploader.rb +47 -0
- data/rails_generators/uploader/uploader_generator.rb +21 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/compatibility/paperclip_spec.rb +43 -0
- data/spec/fixtures/bork.txt +1 -0
- data/spec/fixtures/test.jpeg +1 -0
- data/spec/fixtures/test.jpg +1 -0
- data/spec/mount_spec.rb +517 -0
- data/spec/orm/activerecord_spec.rb +271 -0
- data/spec/orm/datamapper_spec.rb +161 -0
- data/spec/orm/mongomapper_spec.rb +184 -0
- data/spec/orm/sequel_spec.rb +192 -0
- data/spec/sanitized_file_spec.rb +612 -0
- data/spec/spec_helper.rb +99 -0
- data/spec/uploader/cache_spec.rb +196 -0
- data/spec/uploader/default_path_spec.rb +68 -0
- data/spec/uploader/extension_whitelist_spec.rb +44 -0
- data/spec/uploader/mountable_spec.rb +33 -0
- data/spec/uploader/paths_spec.rb +22 -0
- data/spec/uploader/processing_spec.rb +62 -0
- data/spec/uploader/proxy_spec.rb +54 -0
- data/spec/uploader/remove_spec.rb +70 -0
- data/spec/uploader/store_spec.rb +274 -0
- data/spec/uploader/url_spec.rb +87 -0
- data/spec/uploader/versions_spec.rb +306 -0
- metadata +228 -0
data/lib/carrierwave.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'carrierwave/core_ext/blank'
|
5
|
+
require 'carrierwave/core_ext/module_setup'
|
6
|
+
require 'carrierwave/core_ext/inheritable_attributes'
|
7
|
+
|
8
|
+
module CarrierWave
|
9
|
+
|
10
|
+
VERSION = "0.3.2.3"
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :config, :logger
|
14
|
+
|
15
|
+
def logger
|
16
|
+
return @logger if @logger
|
17
|
+
require 'logger'
|
18
|
+
@logger = Logger.new(STDOUT)
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Generates a unique cache id for use in the caching system
|
23
|
+
#
|
24
|
+
# === Returns
|
25
|
+
#
|
26
|
+
# [String] a cache id in the format YYYYMMDD-HHMM-PID-RND
|
27
|
+
#
|
28
|
+
def generate_cache_id
|
29
|
+
Time.now.strftime('%Y%m%d-%H%M') + '-' + Process.pid.to_s + '-' + ("%04d" % rand(9999))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class UploadError < StandardError; end
|
34
|
+
class NoFileError < UploadError; end
|
35
|
+
class FormNotMultipart < UploadError
|
36
|
+
def message
|
37
|
+
"You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.\n\n If this is a file upload, please check that your upload form is multipart encoded."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
class IntegrityError < UploadError; end
|
41
|
+
class InvalidParameter < UploadError; end
|
42
|
+
# Should be used by methods used as process callbacks.
|
43
|
+
class ProcessingError < UploadError; end
|
44
|
+
|
45
|
+
autoload :SanitizedFile, 'carrierwave/sanitized_file'
|
46
|
+
autoload :Mount, 'carrierwave/mount'
|
47
|
+
autoload :RMagick, 'carrierwave/processing/rmagick'
|
48
|
+
autoload :ImageScience, 'carrierwave/processing/image_science'
|
49
|
+
|
50
|
+
module Storage
|
51
|
+
autoload :Abstract, 'carrierwave/storage/abstract'
|
52
|
+
autoload :File, 'carrierwave/storage/file'
|
53
|
+
autoload :S3, 'carrierwave/storage/s3'
|
54
|
+
end
|
55
|
+
|
56
|
+
module Uploader
|
57
|
+
autoload :Base, 'carrierwave/uploader'
|
58
|
+
autoload :Cache, 'carrierwave/uploader/cache'
|
59
|
+
autoload :Store, 'carrierwave/uploader/store'
|
60
|
+
autoload :Callbacks, 'carrierwave/uploader/callbacks'
|
61
|
+
autoload :Processing, 'carrierwave/uploader/processing'
|
62
|
+
autoload :Versions, 'carrierwave/uploader/versions'
|
63
|
+
autoload :Remove, 'carrierwave/uploader/remove'
|
64
|
+
autoload :Paths, 'carrierwave/uploader/paths'
|
65
|
+
autoload :ExtensionWhitelist, 'carrierwave/uploader/extension_whitelist'
|
66
|
+
autoload :DefaultPath, 'carrierwave/uploader/default_path'
|
67
|
+
autoload :Proxy, 'carrierwave/uploader/proxy'
|
68
|
+
autoload :Url, 'carrierwave/uploader/url'
|
69
|
+
autoload :Mountable, 'carrierwave/uploader/mountable'
|
70
|
+
end
|
71
|
+
|
72
|
+
module Compatibility
|
73
|
+
autoload :Paperclip, 'carrierwave/compatibility/paperclip'
|
74
|
+
end
|
75
|
+
|
76
|
+
module Test
|
77
|
+
autoload :Matchers, 'carrierwave/test/matchers'
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
CarrierWave.config = {
|
83
|
+
:permissions => 0644,
|
84
|
+
:storage => :file,
|
85
|
+
:use_cache => true,
|
86
|
+
:storage_engines => {
|
87
|
+
:file => "CarrierWave::Storage::File",
|
88
|
+
:s3 => "CarrierWave::Storage::S3"
|
89
|
+
},
|
90
|
+
:s3 => {
|
91
|
+
:access => :public_read
|
92
|
+
},
|
93
|
+
:store_dir => 'uploads',
|
94
|
+
:cache_dir => 'uploads/tmp',
|
95
|
+
:cache_to_cache_dir => true,
|
96
|
+
:mount => {
|
97
|
+
:ignore_integrity_errors => true,
|
98
|
+
:ignore_processing_errors => true,
|
99
|
+
:validate_integrity => true,
|
100
|
+
:validate_processing => true
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
if defined?(Merb::Plugins)
|
105
|
+
CarrierWave.config[:root] = Merb.root
|
106
|
+
CarrierWave.config[:public] = Merb.dir_for(:public)
|
107
|
+
|
108
|
+
Merb::BootLoader.before_app_loads do
|
109
|
+
# Set logger
|
110
|
+
CarrierWave.logger ||= Merb.logger
|
111
|
+
# Setup path for uploaders and load all of them before classes are loaded
|
112
|
+
Merb.push_path(:uploaders, Merb.root / 'app' / 'uploaders', '*.rb')
|
113
|
+
Dir.glob(File.join(Merb.load_paths[:uploaders])).each {|f| require f }
|
114
|
+
end
|
115
|
+
|
116
|
+
orm_path = File.dirname(__FILE__) / 'carrierwave' / 'orm' / Merb.orm
|
117
|
+
require orm_path if File.exist?(orm_path + '.rb')
|
118
|
+
|
119
|
+
Merb.add_generators File.dirname(__FILE__) / 'generators' / 'uploader_generator'
|
120
|
+
|
121
|
+
elsif defined?(Rails)
|
122
|
+
begin
|
123
|
+
CarrierWave.logger = Rails.logger
|
124
|
+
rescue
|
125
|
+
# Rails < 2.1
|
126
|
+
CarrierWave.logger = RAILS_DEFAULT_LOGGER
|
127
|
+
end
|
128
|
+
CarrierWave.config[:root] = Rails.root
|
129
|
+
CarrierWave.config[:public] = File.join(Rails.root, 'public')
|
130
|
+
|
131
|
+
require File.join(File.dirname(__FILE__), "carrierwave", "orm", 'activerecord')
|
132
|
+
|
133
|
+
ActiveSupport::Dependencies.load_paths << File.join(Rails.root, "app", "uploaders")
|
134
|
+
|
135
|
+
elsif defined?(Sinatra)
|
136
|
+
|
137
|
+
CarrierWave.config[:root] = Sinatra::Application.root
|
138
|
+
CarrierWave.config[:public] = Sinatra::Application.public
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
# MongoMapper is framework agnostic so we could need this in any environment.
|
143
|
+
if defined?(MongoMapper)
|
144
|
+
require File.join(File.dirname(__FILE__), "carrierwave", "orm", "mongomapper")
|
145
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Compatibility
|
5
|
+
|
6
|
+
##
|
7
|
+
# Mix this module into an Uploader to make it mimic Paperclip's storage paths
|
8
|
+
# This will make your Uploader use the same default storage path as paperclip
|
9
|
+
# does. If you need to override it, you can override the +paperclip_path+ method
|
10
|
+
# and provide a Paperclip style path:
|
11
|
+
#
|
12
|
+
# class MyUploader < CarrierWave::Uploader::Base
|
13
|
+
# include CarrierWave::Compatibility::Paperclip
|
14
|
+
#
|
15
|
+
# def paperclip_path
|
16
|
+
# ":rails_root/public/uploads/:id/:attachment/:style_:basename.:extension"
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# ---
|
21
|
+
#
|
22
|
+
# This file contains code taken from Paperclip
|
23
|
+
#
|
24
|
+
# LICENSE
|
25
|
+
#
|
26
|
+
# The MIT License
|
27
|
+
#
|
28
|
+
# Copyright (c) 2008 Jon Yurek and thoughtbot, inc.
|
29
|
+
#
|
30
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
31
|
+
# of this software and associated documentation files (the "Software"), to deal
|
32
|
+
# in the Software without restriction, including without limitation the rights
|
33
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
34
|
+
# copies of the Software, and to permit persons to whom the Software is
|
35
|
+
# furnished to do so, subject to the following conditions:
|
36
|
+
#
|
37
|
+
# The above copyright notice and this permission notice shall be included in
|
38
|
+
# all copies or substantial portions of the Software.
|
39
|
+
#
|
40
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
41
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
42
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
43
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
44
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
45
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
46
|
+
# THE SOFTWARE.
|
47
|
+
#
|
48
|
+
module Paperclip
|
49
|
+
|
50
|
+
def store_path(for_file=filename)
|
51
|
+
path = paperclip_path
|
52
|
+
path ||= File.join(*[store_dir, paperclip_style.to_s, for_file].compact)
|
53
|
+
interpolate_paperclip_path(path, for_file)
|
54
|
+
end
|
55
|
+
|
56
|
+
def store_dir
|
57
|
+
":rails_root/public/system/:attachment/:id"
|
58
|
+
end
|
59
|
+
|
60
|
+
def paperclip_default_style
|
61
|
+
:original
|
62
|
+
end
|
63
|
+
|
64
|
+
def paperclip_path
|
65
|
+
end
|
66
|
+
|
67
|
+
def paperclip_style
|
68
|
+
version_name || paperclip_default_style
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def interpolate_paperclip_path(path, filename)
|
74
|
+
mappings.inject(path) do |agg, pair|
|
75
|
+
agg.gsub(":#{pair[0]}", pair[1].call(self, filename).to_s)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def mappings
|
80
|
+
{
|
81
|
+
:rails_root => lambda{|u, f| CarrierWave.config[:root] },
|
82
|
+
:rails_env => lambda{|u, f| CarrierWave.config[:env] },
|
83
|
+
:class => lambda{|u, f| u.model.class.name.underscore.pluralize},
|
84
|
+
:id => lambda{|u, f| u.model.id },
|
85
|
+
:id_partition => lambda{|u, f| ("%09d" % u.model.id).scan(/\d{3}/).join("/")},
|
86
|
+
:attachment => lambda{|u, f| u.mounted_as.to_s.downcase.pluralize },
|
87
|
+
:style => lambda{|u, f| u.paperclip_style },
|
88
|
+
:basename => lambda{|u, f| f.gsub(/#{File.extname(f)}$/, "") },
|
89
|
+
:extension => lambda{|u, f| File.extname(f).gsub(/^\.+/, "")}
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
end # Paperclip
|
94
|
+
end # Compatibility
|
95
|
+
end # CarrierWave
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
unless "".respond_to?(:blank?)
|
4
|
+
# blank? methods for several different class types
|
5
|
+
class Object
|
6
|
+
# Returns true if the object is nil or empty (if applicable)
|
7
|
+
def blank?
|
8
|
+
nil? || (respond_to?(:empty?) && empty?)
|
9
|
+
end
|
10
|
+
end # class Object
|
11
|
+
|
12
|
+
class Numeric
|
13
|
+
# Numerics can't be blank
|
14
|
+
def blank?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end # class Numeric
|
18
|
+
|
19
|
+
class NilClass
|
20
|
+
# Nils are always blank
|
21
|
+
def blank?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end # class NilClass
|
25
|
+
|
26
|
+
class TrueClass
|
27
|
+
# True is not blank.
|
28
|
+
def blank?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end # class TrueClass
|
32
|
+
|
33
|
+
class FalseClass
|
34
|
+
# False is always blank.
|
35
|
+
def blank?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
end # class FalseClass
|
39
|
+
|
40
|
+
class String
|
41
|
+
# Strips out whitespace then tests if the string is empty.
|
42
|
+
def blank?
|
43
|
+
strip.empty?
|
44
|
+
end
|
45
|
+
end # class String
|
46
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Stolen from Rails 3
|
4
|
+
|
5
|
+
# Copyright (c) 2005-2009 David Heinemeier Hansson
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
# Retain for backward compatibility. Methods are now included in Class.
|
27
|
+
class Class
|
28
|
+
# Defines class-level inheritable attribute reader. Attributes are available to subclasses,
|
29
|
+
# each subclass has a copy of parent's attribute.
|
30
|
+
#
|
31
|
+
# @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
|
32
|
+
# @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
|
33
|
+
#
|
34
|
+
# @api public
|
35
|
+
#
|
36
|
+
# @todo Do we want to block instance_reader via :instance_reader => false
|
37
|
+
# @todo It would be preferable that we do something with a Hash passed in
|
38
|
+
# (error out or do the same as other methods above) instead of silently
|
39
|
+
# moving on). In particular, this makes the return value of this function
|
40
|
+
# less useful.
|
41
|
+
def extlib_inheritable_reader(*ivars)
|
42
|
+
instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
|
43
|
+
|
44
|
+
ivars.each do |ivar|
|
45
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
46
|
+
def self.#{ivar}
|
47
|
+
return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
|
48
|
+
ivar = superclass.#{ivar}
|
49
|
+
return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
|
50
|
+
@#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) ? ivar.dup : ivar
|
51
|
+
end
|
52
|
+
RUBY
|
53
|
+
unless instance_reader == false
|
54
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
55
|
+
def #{ivar}
|
56
|
+
self.class.#{ivar}
|
57
|
+
end
|
58
|
+
RUBY
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Defines class-level inheritable attribute writer. Attributes are available to subclasses,
|
64
|
+
# each subclass has a copy of parent's attribute.
|
65
|
+
#
|
66
|
+
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
67
|
+
# define inheritable writer for.
|
68
|
+
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
69
|
+
# @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
#
|
73
|
+
# @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
|
74
|
+
# class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
|
75
|
+
def extlib_inheritable_writer(*ivars)
|
76
|
+
instance_writer = ivars.pop[:writer] if ivars.last.is_a?(Hash)
|
77
|
+
ivars.each do |ivar|
|
78
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
79
|
+
def self.#{ivar}=(obj)
|
80
|
+
@#{ivar} = obj
|
81
|
+
end
|
82
|
+
RUBY
|
83
|
+
unless instance_writer == false
|
84
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
85
|
+
def #{ivar}=(obj) self.class.#{ivar} = obj end
|
86
|
+
RUBY
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
|
92
|
+
# each subclass has a copy of parent's attribute.
|
93
|
+
#
|
94
|
+
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
95
|
+
# define inheritable accessor for.
|
96
|
+
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
97
|
+
# @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
|
98
|
+
#
|
99
|
+
# @api public
|
100
|
+
def extlib_inheritable_accessor(*syms)
|
101
|
+
extlib_inheritable_reader(*syms)
|
102
|
+
extlib_inheritable_writer(*syms)
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Stolen from Rails 3
|
4
|
+
|
5
|
+
# Copyright (c) 2005-2009 David Heinemeier Hansson
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
class Module
|
27
|
+
attr_accessor :_setup_block
|
28
|
+
attr_accessor :_dependencies
|
29
|
+
|
30
|
+
def setup(&blk)
|
31
|
+
@_setup_block = blk
|
32
|
+
end
|
33
|
+
|
34
|
+
def use(mod)
|
35
|
+
return if self < mod
|
36
|
+
|
37
|
+
(mod._dependencies || []).each do |dep|
|
38
|
+
use dep
|
39
|
+
end
|
40
|
+
# raise "Circular dependencies" if self < mod
|
41
|
+
include mod
|
42
|
+
extend mod.const_get("ClassMethods") if mod.const_defined?("ClassMethods")
|
43
|
+
class_eval(&mod._setup_block) if mod._setup_block
|
44
|
+
end
|
45
|
+
|
46
|
+
def depends_on(mod)
|
47
|
+
return if self < mod
|
48
|
+
@_dependencies ||= []
|
49
|
+
@_dependencies << mod
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,332 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
|
5
|
+
##
|
6
|
+
# If a Class is extended with this module, it gains the mount_uploader
|
7
|
+
# method, which is used for mapping attributes to uploaders and allowing
|
8
|
+
# easy assignment.
|
9
|
+
#
|
10
|
+
# You can use mount_uploader with pretty much any class, however it is
|
11
|
+
# intended to be used with some kind of persistent storage, like an ORM.
|
12
|
+
# If you want to persist the uploaded files in a particular Class, it
|
13
|
+
# needs to implement a `read_uploader` and a `write_uploader` method.
|
14
|
+
#
|
15
|
+
module Mount
|
16
|
+
|
17
|
+
##
|
18
|
+
# === Returns
|
19
|
+
#
|
20
|
+
# [Hash{Symbol => CarrierWave}] what uploaders are mounted on which columns
|
21
|
+
#
|
22
|
+
def uploaders
|
23
|
+
@uploaders ||= {}
|
24
|
+
@uploaders = superclass.uploaders.merge(@uploaders)
|
25
|
+
rescue NoMethodError
|
26
|
+
@uploaders
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# === Returns
|
31
|
+
#
|
32
|
+
# [Hash{Symbol => Hash}] options for mounted uploaders
|
33
|
+
#
|
34
|
+
def uploader_options
|
35
|
+
@uploader_options ||= {}
|
36
|
+
@uploader_options = superclass.uploader_options.merge(@uploader_options)
|
37
|
+
rescue NoMethodError
|
38
|
+
@uploader_options
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Mounts the given uploader on the given column. This means that assigning
|
43
|
+
# and reading from the column will upload and retrieve files. Supposing
|
44
|
+
# that a User class has an uploader mounted on image, you can assign and
|
45
|
+
# retrieve files like this:
|
46
|
+
#
|
47
|
+
# @user.image # => <Uploader>
|
48
|
+
# @user.image = some_file_object
|
49
|
+
#
|
50
|
+
# @user.store_image!
|
51
|
+
#
|
52
|
+
# @user.image.url # => '/some_url.png'
|
53
|
+
#
|
54
|
+
# It is also possible (but not recommended) to ommit the uploader, which
|
55
|
+
# will create an anonymous uploader class. Passing a block to this method
|
56
|
+
# makes it possible to customize it. This can be convenient for brevity,
|
57
|
+
# but if there is any significatnt logic in the uploader, you should do
|
58
|
+
# the right thing and have it in its own file.
|
59
|
+
#
|
60
|
+
# === Added instance methods
|
61
|
+
#
|
62
|
+
# Supposing a class has used +mount_uploader+ to mount an uploader on a column
|
63
|
+
# named +image+, in that case the following methods will be added to the class:
|
64
|
+
#
|
65
|
+
# [image] Returns an instance of the uploader only if anything has been uploaded
|
66
|
+
# [image=] Caches the given file
|
67
|
+
#
|
68
|
+
# [image_url] Returns the url to the uploaded file
|
69
|
+
#
|
70
|
+
# [image_cache] Returns a string that identifies the cache location of the file
|
71
|
+
# [image_cache=] Retrieves the file from the cache based on the given cache name
|
72
|
+
#
|
73
|
+
# [image_uploader] Returns an instance of the uploader
|
74
|
+
# [image_uploader=] Sets the uploader (be careful!)
|
75
|
+
#
|
76
|
+
# [remove_image] An attribute reader that can be used with a checkbox to mark a file for removal
|
77
|
+
# [remove_image=] An attribute writer that can be used with a checkbox to mark a file for removal
|
78
|
+
# [remove_image?] Whether the file should be removed when store_image! is called.
|
79
|
+
#
|
80
|
+
# [store_image!] Stores a file that has been assigned with +image=+
|
81
|
+
# [remove_image!] Removes the uploaded file from the filesystem.
|
82
|
+
#
|
83
|
+
# [image_integrity_error] Returns an error object if the last file to be assigned caused an integrty error
|
84
|
+
# [image_processing_error] Returns an error object if the last file to be assigned caused a processing error
|
85
|
+
#
|
86
|
+
# [write_image_identifier] Uses the write_uploader method to set the identifier.
|
87
|
+
#
|
88
|
+
# === Parameters
|
89
|
+
#
|
90
|
+
# [column (Symbol)] the attribute to mount this uploader on
|
91
|
+
# [uploader (CarrierWave::Uploader)] the uploader class to mount
|
92
|
+
# [options (Hash{Symbol => Object})] a set of options
|
93
|
+
# [&block (Proc)] customize anonymous uploaders
|
94
|
+
#
|
95
|
+
# === Options
|
96
|
+
#
|
97
|
+
# [:mount_on => Symbol] if the name of the column to be serialized to differs you can override it using this option
|
98
|
+
# [:ignore_integrity_errors => Boolean] if set to true, integrity errors will result in caching failing silently
|
99
|
+
# [:ignore_processing_errors => Boolean] if set to true, processing errors will result in caching failing silently
|
100
|
+
#
|
101
|
+
# === Examples
|
102
|
+
#
|
103
|
+
# Mounting uploaders on different columns.
|
104
|
+
#
|
105
|
+
# class Song
|
106
|
+
# mount_uploader :lyrics, LyricsUploader
|
107
|
+
# mount_uploader :alternative_lyrics, LyricsUploader
|
108
|
+
# mount_uploader :file, SongUploader
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# This will add an anonymous uploader with only the default settings:
|
112
|
+
#
|
113
|
+
# class Data
|
114
|
+
# mount_uploader :csv
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# this will add an anonymous uploader overriding the store_dir:
|
118
|
+
#
|
119
|
+
# class Product
|
120
|
+
# mount_uploader :blueprint do
|
121
|
+
# def store_dir
|
122
|
+
# 'blueprints'
|
123
|
+
# end
|
124
|
+
# end
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
def mount_uploader(column, uploader=nil, options={}, &block)
|
128
|
+
unless uploader
|
129
|
+
uploader = Class.new(CarrierWave::Uploader::Base)
|
130
|
+
uploader.class_eval(&block)
|
131
|
+
end
|
132
|
+
|
133
|
+
uploaders[column.to_sym] = uploader
|
134
|
+
uploader_options[column.to_sym] = CarrierWave.config[:mount].merge(options)
|
135
|
+
|
136
|
+
include CarrierWave::Mount::Extension
|
137
|
+
|
138
|
+
# Make sure to write over accessors directly defined on the class.
|
139
|
+
# Simply super to the included module below.
|
140
|
+
class_eval <<-RUBY, __FILE__, __LINE__+1
|
141
|
+
def #{column}; super; end
|
142
|
+
def #{column}=(new_file); super; end
|
143
|
+
RUBY
|
144
|
+
|
145
|
+
# Mixing this in as a Module instead of class_evaling directly, so we
|
146
|
+
# can maintain the ability to super to any of these methods from within
|
147
|
+
# the class.
|
148
|
+
mod = Module.new
|
149
|
+
include mod
|
150
|
+
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
|
151
|
+
|
152
|
+
def #{column}
|
153
|
+
_mounter(:#{column}).uploader
|
154
|
+
end
|
155
|
+
|
156
|
+
def #{column}=(new_file)
|
157
|
+
_mounter(:#{column}).cache(new_file)
|
158
|
+
end
|
159
|
+
|
160
|
+
def #{column}?
|
161
|
+
!_mounter(:#{column}).blank?
|
162
|
+
end
|
163
|
+
|
164
|
+
def #{column}_url(*args)
|
165
|
+
_mounter(:#{column}).url(*args)
|
166
|
+
end
|
167
|
+
|
168
|
+
def #{column}_uploader
|
169
|
+
_mounter(:#{column}).uploader
|
170
|
+
end
|
171
|
+
|
172
|
+
def #{column}_uploader=(uploader)
|
173
|
+
_mounter(:#{column}).uploader = uploader
|
174
|
+
end
|
175
|
+
|
176
|
+
def #{column}_cache
|
177
|
+
_mounter(:#{column}).cache_name
|
178
|
+
end
|
179
|
+
|
180
|
+
def #{column}_cache=(cache_name)
|
181
|
+
_mounter(:#{column}).cache_name = cache_name
|
182
|
+
end
|
183
|
+
|
184
|
+
def remove_#{column}
|
185
|
+
_mounter(:#{column}).remove
|
186
|
+
end
|
187
|
+
|
188
|
+
def remove_#{column}!
|
189
|
+
_mounter(:#{column}).remove!
|
190
|
+
end
|
191
|
+
|
192
|
+
def remove_#{column}=(value)
|
193
|
+
_mounter(:#{column}).remove = value
|
194
|
+
end
|
195
|
+
|
196
|
+
def remove_#{column}?
|
197
|
+
_mounter(:#{column}).remove?
|
198
|
+
end
|
199
|
+
|
200
|
+
def store_#{column}!
|
201
|
+
_mounter(:#{column}).store!
|
202
|
+
end
|
203
|
+
|
204
|
+
def #{column}_integrity_error
|
205
|
+
_mounter(:#{column}).integrity_error
|
206
|
+
end
|
207
|
+
|
208
|
+
def #{column}_processing_error
|
209
|
+
_mounter(:#{column}).processing_error
|
210
|
+
end
|
211
|
+
|
212
|
+
def write_#{column}_identifier
|
213
|
+
_mounter(:#{column}).write_identifier
|
214
|
+
end
|
215
|
+
|
216
|
+
RUBY
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
module Extension
|
221
|
+
|
222
|
+
##
|
223
|
+
# overwrite this to read from a serialized attribute
|
224
|
+
#
|
225
|
+
def read_uploader(column); end
|
226
|
+
|
227
|
+
##
|
228
|
+
# overwrite this to write to a serialized attribute
|
229
|
+
#
|
230
|
+
def write_uploader(column, identifier); end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def _mounter(column)
|
235
|
+
@_mounters ||= {}
|
236
|
+
@_mounters[column] ||= Mounter.new(self, column)
|
237
|
+
end
|
238
|
+
|
239
|
+
end # Extension
|
240
|
+
|
241
|
+
# this is an internal class, used by CarrierWave::Mount so that
|
242
|
+
# we don't pollute the model with a lot of methods.
|
243
|
+
class Mounter #:nodoc:
|
244
|
+
|
245
|
+
attr_reader :column, :record, :options
|
246
|
+
|
247
|
+
attr_accessor :uploader, :integrity_error, :processing_error, :remove
|
248
|
+
|
249
|
+
def initialize(record, column, options={})
|
250
|
+
@record = record
|
251
|
+
@column = column
|
252
|
+
@options = record.class.uploader_options[column]
|
253
|
+
end
|
254
|
+
|
255
|
+
def write_identifier
|
256
|
+
if remove?
|
257
|
+
record.write_uploader(serialization_column, '')
|
258
|
+
elsif not uploader.identifier.blank?
|
259
|
+
record.write_uploader(serialization_column, uploader.identifier)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def identifier
|
264
|
+
record.read_uploader(serialization_column)
|
265
|
+
end
|
266
|
+
|
267
|
+
def uploader
|
268
|
+
@uploader ||= record.class.uploaders[column].new(record, column)
|
269
|
+
|
270
|
+
if @uploader.blank? and not identifier.blank?
|
271
|
+
@uploader.retrieve_from_store!(identifier)
|
272
|
+
end
|
273
|
+
return @uploader
|
274
|
+
end
|
275
|
+
|
276
|
+
def cache(new_file)
|
277
|
+
uploader.cache!(new_file)
|
278
|
+
self.integrity_error = nil
|
279
|
+
self.processing_error = nil
|
280
|
+
rescue CarrierWave::IntegrityError => e
|
281
|
+
self.integrity_error = e
|
282
|
+
raise e unless options[:ignore_integrity_errors]
|
283
|
+
rescue CarrierWave::ProcessingError => e
|
284
|
+
self.processing_error = e
|
285
|
+
raise e unless options[:ignore_processing_errors]
|
286
|
+
end
|
287
|
+
|
288
|
+
def cache_name
|
289
|
+
uploader.cache_name
|
290
|
+
end
|
291
|
+
|
292
|
+
def cache_name=(cache_name)
|
293
|
+
uploader.retrieve_from_cache!(cache_name) unless uploader.cached?
|
294
|
+
rescue CarrierWave::InvalidParameter
|
295
|
+
end
|
296
|
+
|
297
|
+
def store!
|
298
|
+
unless uploader.blank?
|
299
|
+
if remove?
|
300
|
+
uploader.remove!
|
301
|
+
else
|
302
|
+
uploader.store!
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def url(*args)
|
308
|
+
uploader.url(*args)
|
309
|
+
end
|
310
|
+
|
311
|
+
def blank?
|
312
|
+
uploader.blank?
|
313
|
+
end
|
314
|
+
|
315
|
+
def remove?
|
316
|
+
!remove.blank? and remove !~ /\A0|false$\z/
|
317
|
+
end
|
318
|
+
|
319
|
+
def remove!
|
320
|
+
uploader.remove!
|
321
|
+
end
|
322
|
+
|
323
|
+
private
|
324
|
+
|
325
|
+
def serialization_column
|
326
|
+
options[:mount_on] || column
|
327
|
+
end
|
328
|
+
|
329
|
+
end # Mounter
|
330
|
+
|
331
|
+
end # Mount
|
332
|
+
end # CarrierWave
|