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
@@ -0,0 +1,114 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Test
|
5
|
+
|
6
|
+
##
|
7
|
+
# These are some matchers that can be used in RSpec specs, to simplify the testing
|
8
|
+
# of uploaders.
|
9
|
+
#
|
10
|
+
module Matchers
|
11
|
+
|
12
|
+
class BeIdenticalTo # :nodoc:
|
13
|
+
def initialize(expected)
|
14
|
+
@expected = expected
|
15
|
+
end
|
16
|
+
def matches?(actual)
|
17
|
+
@actual = actual
|
18
|
+
FileUtils.identical?(@actual, @expected)
|
19
|
+
end
|
20
|
+
def failure_message
|
21
|
+
"expected #{@actual.inspect} to be identical to #{@expected.inspect}"
|
22
|
+
end
|
23
|
+
def negative_failure_message
|
24
|
+
"expected #{@actual.inspect} to not be identical to #{@expected.inspect}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def be_identical_to(expected)
|
29
|
+
BeIdenticalTo.new(expected)
|
30
|
+
end
|
31
|
+
|
32
|
+
class HavePermissions # :nodoc:
|
33
|
+
def initialize(expected)
|
34
|
+
@expected = expected
|
35
|
+
end
|
36
|
+
|
37
|
+
def matches?(actual)
|
38
|
+
@actual = actual
|
39
|
+
# Satisfy expectation here. Return false or raise an error if it's not met.
|
40
|
+
(File.stat(@actual.path).mode & 0777) == @expected
|
41
|
+
end
|
42
|
+
|
43
|
+
def failure_message
|
44
|
+
"expected #{@actual.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0777).to_s(8)}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def negative_failure_message
|
48
|
+
"expected #{@actual.inspect} not to have permissions #{@expected.to_s(8)}, but it did"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def have_permissions(expected)
|
53
|
+
HavePermissions.new(expected)
|
54
|
+
end
|
55
|
+
|
56
|
+
class BeNoLargerThan # :nodoc:
|
57
|
+
def initialize(width, height)
|
58
|
+
@width, @height = width, height
|
59
|
+
end
|
60
|
+
|
61
|
+
def matches?(actual)
|
62
|
+
@actual = actual
|
63
|
+
# Satisfy expectation here. Return false or raise an error if it's not met.
|
64
|
+
require 'RMagick'
|
65
|
+
img = ::Magick::Image.read(@actual.path).first
|
66
|
+
@actual_width = img.columns
|
67
|
+
@actual_height = img.rows
|
68
|
+
@actual_width <= @width && @actual_height <= @height
|
69
|
+
end
|
70
|
+
|
71
|
+
def failure_message
|
72
|
+
"expected #{@actual.inspect} to be no larger than #{@width} by #{@height}, but it was #{@actual_height} by #{@actual_width}."
|
73
|
+
end
|
74
|
+
|
75
|
+
def negative_failure_message
|
76
|
+
"expected #{@actual.inspect} to be larger than #{@width} by #{@height}, but it wasn't."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def be_no_larger_than(width, height)
|
81
|
+
BeNoLargerThan.new(width, height)
|
82
|
+
end
|
83
|
+
|
84
|
+
class HaveDimensions # :nodoc:
|
85
|
+
def initialize(width, height)
|
86
|
+
@width, @height = width, height
|
87
|
+
end
|
88
|
+
|
89
|
+
def matches?(actual)
|
90
|
+
@actual = actual
|
91
|
+
# Satisfy expectation here. Return false or raise an error if it's not met.
|
92
|
+
require 'RMagick'
|
93
|
+
img = ::Magick::Image.read(@actual.path).first
|
94
|
+
@actual_width = img.columns
|
95
|
+
@actual_height = img.rows
|
96
|
+
@actual_width == @width && @actual_height == @height
|
97
|
+
end
|
98
|
+
|
99
|
+
def failure_message
|
100
|
+
"expected #{@actual.inspect} to have an exact size of #{@width} by #{@height}, but it was #{@actual_height} by #{@actual_width}."
|
101
|
+
end
|
102
|
+
|
103
|
+
def negative_failure_message
|
104
|
+
"expected #{@actual.inspect} not to have an exact size of #{@width} by #{@height}, but it did."
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def have_dimensions(width, height)
|
109
|
+
HaveDimensions.new(width, height)
|
110
|
+
end
|
111
|
+
|
112
|
+
end # SpecHelper
|
113
|
+
end # Test
|
114
|
+
end # CarrierWave
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
|
5
|
+
##
|
6
|
+
# See CarrierWave::Uploader::Base
|
7
|
+
#
|
8
|
+
module Uploader
|
9
|
+
|
10
|
+
##
|
11
|
+
# An uploader is a class that allows you to easily handle the caching and storage of
|
12
|
+
# uploaded files. Please refer to the README for configuration options.
|
13
|
+
#
|
14
|
+
# Once you have an uploader you can use it in isolation:
|
15
|
+
#
|
16
|
+
# my_uploader = MyUploader.new
|
17
|
+
# my_uploader.cache!(File.open(path_to_file))
|
18
|
+
# my_uploader.retrieve_from_store!('monkey.png')
|
19
|
+
#
|
20
|
+
# Alternatively, you can mount it on an ORM or other persistence layer, with
|
21
|
+
# +CarrierWave::Mount#mount_uploader+. There are extensions for activerecord and datamapper
|
22
|
+
# these are *very* simple (they are only a dozen lines of code), so adding your own should
|
23
|
+
# be trivial.
|
24
|
+
#
|
25
|
+
class Base
|
26
|
+
attr_reader :file
|
27
|
+
|
28
|
+
use CarrierWave::Uploader::Paths
|
29
|
+
use CarrierWave::Uploader::Callbacks
|
30
|
+
use CarrierWave::Uploader::Proxy
|
31
|
+
use CarrierWave::Uploader::Url
|
32
|
+
use CarrierWave::Uploader::Mountable
|
33
|
+
use CarrierWave::Uploader::Cache
|
34
|
+
use CarrierWave::Uploader::Store
|
35
|
+
use CarrierWave::Uploader::Remove
|
36
|
+
use CarrierWave::Uploader::ExtensionWhitelist
|
37
|
+
use CarrierWave::Uploader::DefaultPath
|
38
|
+
use CarrierWave::Uploader::Processing
|
39
|
+
use CarrierWave::Uploader::Versions
|
40
|
+
end # Base
|
41
|
+
|
42
|
+
end # Uploader
|
43
|
+
end # CarrierWave
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Uploader
|
5
|
+
module Cache
|
6
|
+
|
7
|
+
depends_on CarrierWave::Uploader::Paths
|
8
|
+
depends_on CarrierWave::Uploader::Callbacks
|
9
|
+
|
10
|
+
##
|
11
|
+
# Returns true if the uploader has been cached
|
12
|
+
#
|
13
|
+
# === Returns
|
14
|
+
#
|
15
|
+
# [Bool] whether the current file is cached
|
16
|
+
#
|
17
|
+
def cached?
|
18
|
+
@cache_id
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Override this in your Uploader to change the directory where files are cached.
|
23
|
+
#
|
24
|
+
# === Returns
|
25
|
+
#
|
26
|
+
# [String] a directory
|
27
|
+
#
|
28
|
+
def cache_dir
|
29
|
+
CarrierWave.config[:cache_dir]
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Returns a String which uniquely identifies the currently cached file for later retrieval
|
34
|
+
#
|
35
|
+
# === Returns
|
36
|
+
#
|
37
|
+
# [String] a cache name, in the format YYYYMMDD-HHMM-PID-RND/filename.txt
|
38
|
+
#
|
39
|
+
def cache_name
|
40
|
+
File.join(cache_id, full_original_filename) if cache_id and original_filename
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Caches the given file. Calls process! to trigger any process callbacks.
|
45
|
+
#
|
46
|
+
# === Parameters
|
47
|
+
#
|
48
|
+
# [new_file (File, IOString, Tempfile)] any kind of file object
|
49
|
+
#
|
50
|
+
# === Raises
|
51
|
+
#
|
52
|
+
# [CarrierWave::FormNotMultipart] if the assigned parameter is a string
|
53
|
+
#
|
54
|
+
def cache!(new_file)
|
55
|
+
new_file = CarrierWave::SanitizedFile.new(new_file)
|
56
|
+
raise CarrierWave::FormNotMultipart if new_file.is_path?
|
57
|
+
|
58
|
+
unless new_file.empty?
|
59
|
+
with_callbacks(:cache, new_file) do
|
60
|
+
self.cache_id = CarrierWave.generate_cache_id unless cache_id
|
61
|
+
|
62
|
+
@filename = new_file.filename
|
63
|
+
self.original_filename = new_file.filename
|
64
|
+
|
65
|
+
if CarrierWave.config[:cache_to_cache_dir]
|
66
|
+
@file = new_file.copy_to(cache_path, CarrierWave.config[:permissions])
|
67
|
+
else
|
68
|
+
@file = new_file
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Retrieves the file with the given cache_name from the cache.
|
76
|
+
#
|
77
|
+
# === Parameters
|
78
|
+
#
|
79
|
+
# [cache_name (String)] uniquely identifies a cache file
|
80
|
+
#
|
81
|
+
# === Raises
|
82
|
+
#
|
83
|
+
# [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
|
84
|
+
#
|
85
|
+
def retrieve_from_cache!(cache_name)
|
86
|
+
with_callbacks(:retrieve_from_cache, cache_name) do
|
87
|
+
self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
|
88
|
+
@filename = original_filename
|
89
|
+
@file = CarrierWave::SanitizedFile.new(cache_path)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def cache_path
|
96
|
+
File.expand_path(File.join(cache_dir, cache_name), public)
|
97
|
+
end
|
98
|
+
|
99
|
+
attr_reader :cache_id, :original_filename
|
100
|
+
|
101
|
+
# We can override the full_original_filename method in other modules
|
102
|
+
alias_method :full_original_filename, :original_filename
|
103
|
+
|
104
|
+
def cache_id=(cache_id)
|
105
|
+
raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}\z/
|
106
|
+
@cache_id = cache_id
|
107
|
+
end
|
108
|
+
|
109
|
+
def original_filename=(filename)
|
110
|
+
raise CarrierWave::InvalidParameter, "invalid filename" unless filename =~ /\A[a-z0-9\.\-\+_]+\z/i
|
111
|
+
@original_filename = filename
|
112
|
+
end
|
113
|
+
|
114
|
+
end # Cache
|
115
|
+
end # Uploader
|
116
|
+
end # CarrierWave
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Uploader
|
5
|
+
module Callbacks
|
6
|
+
|
7
|
+
setup do
|
8
|
+
extlib_inheritable_accessor :_before_callbacks, :_after_callbacks
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_callbacks(kind, *args)
|
12
|
+
self.class._before_callbacks_for(kind).each { |callback| self.send(callback, *args) }
|
13
|
+
yield
|
14
|
+
self.class._after_callbacks_for(kind).each { |callback| self.send(callback, *args) }
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
|
19
|
+
def _before_callbacks_for(kind) #:nodoc:
|
20
|
+
self._before_callbacks ||= {}
|
21
|
+
self._before_callbacks[kind] ||= []
|
22
|
+
self._before_callbacks[kind]
|
23
|
+
end
|
24
|
+
|
25
|
+
def _after_callbacks_for(kind) #:nodoc:
|
26
|
+
self._after_callbacks ||= {}
|
27
|
+
self._after_callbacks[kind] ||= []
|
28
|
+
self._after_callbacks[kind]
|
29
|
+
end
|
30
|
+
|
31
|
+
def before(kind, callback)
|
32
|
+
_before_callbacks_for(kind) << callback
|
33
|
+
end
|
34
|
+
|
35
|
+
def after(kind, callback)
|
36
|
+
_after_callbacks_for(kind) << callback
|
37
|
+
end
|
38
|
+
end # ClassMethods
|
39
|
+
|
40
|
+
end # Callbacks
|
41
|
+
end # Uploader
|
42
|
+
end # CarrierWave
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Uploader
|
5
|
+
module DefaultPath
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
super
|
9
|
+
if default_path
|
10
|
+
@file = CarrierWave::SanitizedFile.new(File.expand_path(default_path, public))
|
11
|
+
def @file.blank?; true; end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Override this method in your uploader to provide a default path
|
17
|
+
# in case no file has been cached/stored yet.
|
18
|
+
#
|
19
|
+
def default_path; end
|
20
|
+
|
21
|
+
end # DefaultPath
|
22
|
+
end # Uploader
|
23
|
+
end # CarrierWave
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Uploader
|
5
|
+
module ExtensionWhitelist
|
6
|
+
|
7
|
+
setup do
|
8
|
+
before :cache, :check_whitelist!
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
# Override this method in your uploader to provide a white list of extensions which
|
13
|
+
# are allowed to be uploaded.
|
14
|
+
#
|
15
|
+
# === Returns
|
16
|
+
#
|
17
|
+
# [NilClass, Array[String]] a white list of extensions which are allowed to be uploaded
|
18
|
+
#
|
19
|
+
# === Examples
|
20
|
+
#
|
21
|
+
# def extension_white_list
|
22
|
+
# %w(jpg jpeg gif png)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
def extension_white_list; end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def check_whitelist!(new_file)
|
30
|
+
if extension_white_list and not extension_white_list.include?(new_file.extension.to_s)
|
31
|
+
raise CarrierWave::IntegrityError, "You are not allowed to upload #{new_file.extension.inspect} files, allowed types: #{extension_white_list.inspect}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end # ExtensionWhitelist
|
36
|
+
end # Uploader
|
37
|
+
end # CarrierWave
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Uploader
|
5
|
+
module Mountable
|
6
|
+
|
7
|
+
attr_reader :model, :mounted_as
|
8
|
+
|
9
|
+
##
|
10
|
+
# If a model is given as the first parameter, it will stored in the uploader, and
|
11
|
+
# available throught +#model+. Likewise, mounted_as stores the name of the column
|
12
|
+
# where this instance of the uploader is mounted. These values can then be used inside
|
13
|
+
# your uploader.
|
14
|
+
#
|
15
|
+
# If you do not wish to mount your uploaders with the ORM extensions in -more then you
|
16
|
+
# can override this method inside your uploader. Just be sure to call +super+
|
17
|
+
#
|
18
|
+
# === Parameters
|
19
|
+
#
|
20
|
+
# [model (Object)] Any kind of model object
|
21
|
+
# [mounted_as (Symbol)] The name of the column where this uploader is mounted
|
22
|
+
#
|
23
|
+
# === Examples
|
24
|
+
#
|
25
|
+
# class MyUploader < CarrierWave::Uploader::Base
|
26
|
+
#
|
27
|
+
# def store_dir
|
28
|
+
# File.join('public', 'files', mounted_as, model.permalink)
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
def initialize(model=nil, mounted_as=nil)
|
33
|
+
@model = model
|
34
|
+
@mounted_as = mounted_as
|
35
|
+
end
|
36
|
+
|
37
|
+
end # Mountable
|
38
|
+
end # Uploader
|
39
|
+
end # CarrierWave
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CarrierWave
|
4
|
+
module Uploader
|
5
|
+
module Paths
|
6
|
+
|
7
|
+
##
|
8
|
+
# === Returns
|
9
|
+
#
|
10
|
+
# [String] the directory that is the root of the application
|
11
|
+
#
|
12
|
+
def root
|
13
|
+
CarrierWave.config[:root]
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# === Returns
|
18
|
+
#
|
19
|
+
# [String] the directory where files will be publically accessible
|
20
|
+
#
|
21
|
+
def public
|
22
|
+
CarrierWave.config[:public]
|
23
|
+
end
|
24
|
+
|
25
|
+
end # Paths
|
26
|
+
end # Uploader
|
27
|
+
end # CarrierWave
|