durran-carrierwave 0.3.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|