plowdawg-carrierwave 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/README.md +674 -0
  2. data/lib/carrierwave.rb +109 -0
  3. data/lib/carrierwave/compatibility/paperclip.rb +95 -0
  4. data/lib/carrierwave/locale/en.yml +5 -0
  5. data/lib/carrierwave/mount.rb +382 -0
  6. data/lib/carrierwave/orm/activerecord.rb +46 -0
  7. data/lib/carrierwave/processing/mime_types.rb +58 -0
  8. data/lib/carrierwave/processing/mini_magick.rb +253 -0
  9. data/lib/carrierwave/processing/rmagick.rb +279 -0
  10. data/lib/carrierwave/sanitized_file.rb +302 -0
  11. data/lib/carrierwave/storage/abstract.rb +30 -0
  12. data/lib/carrierwave/storage/cloud_files.rb +188 -0
  13. data/lib/carrierwave/storage/file.rb +47 -0
  14. data/lib/carrierwave/storage/fog.rb +332 -0
  15. data/lib/carrierwave/storage/right_s3.rb +1 -0
  16. data/lib/carrierwave/storage/s3.rb +240 -0
  17. data/lib/carrierwave/test/matchers.rb +164 -0
  18. data/lib/carrierwave/uploader.rb +44 -0
  19. data/lib/carrierwave/uploader/cache.rb +160 -0
  20. data/lib/carrierwave/uploader/callbacks.rb +35 -0
  21. data/lib/carrierwave/uploader/configuration.rb +162 -0
  22. data/lib/carrierwave/uploader/default_url.rb +19 -0
  23. data/lib/carrierwave/uploader/download.rb +75 -0
  24. data/lib/carrierwave/uploader/extension_whitelist.rb +49 -0
  25. data/lib/carrierwave/uploader/mountable.rb +39 -0
  26. data/lib/carrierwave/uploader/processing.rb +90 -0
  27. data/lib/carrierwave/uploader/proxy.rb +77 -0
  28. data/lib/carrierwave/uploader/remove.rb +23 -0
  29. data/lib/carrierwave/uploader/store.rb +113 -0
  30. data/lib/carrierwave/uploader/url.rb +45 -0
  31. data/lib/carrierwave/uploader/versions.rb +237 -0
  32. data/lib/carrierwave/validations/active_model.rb +79 -0
  33. data/lib/carrierwave/version.rb +3 -0
  34. data/lib/generators/templates/uploader.rb +49 -0
  35. data/lib/generators/uploader_generator.rb +7 -0
  36. metadata +215 -0
@@ -0,0 +1,164 @@
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
+ image = ImageLoader.load_image(@actual.current_path)
65
+ @actual_width = image.width
66
+ @actual_height = image.height
67
+ @actual_width <= @width && @actual_height <= @height
68
+ end
69
+
70
+ def failure_message
71
+ "expected #{@actual.current_path.inspect} to be no larger than #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
72
+ end
73
+
74
+ def negative_failure_message
75
+ "expected #{@actual.current_path.inspect} to be larger than #{@width} by #{@height}, but it wasn't."
76
+ end
77
+
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
+ image = ImageLoader.load_image(@actual.current_path)
93
+ @actual_width = image.width
94
+ @actual_height = image.height
95
+ @actual_width == @width && @actual_height == @height
96
+ end
97
+
98
+ def failure_message
99
+ "expected #{@actual.current_path.inspect} to have an exact size of #{@width} by #{@height}, but it was #{@actual_width} by #{@actual_height}."
100
+ end
101
+
102
+ def negative_failure_message
103
+ "expected #{@actual.current_path.inspect} not to have an exact size of #{@width} by #{@height}, but it did."
104
+ end
105
+
106
+ end
107
+
108
+ def have_dimensions(width, height)
109
+ HaveDimensions.new(width, height)
110
+ end
111
+
112
+ class ImageLoader # :nodoc:
113
+ def self.load_image(filename)
114
+ if defined? ::MiniMagick
115
+ MiniMagickWrapper.new(filename)
116
+ else
117
+ unless defined? ::Magick
118
+ begin
119
+ require 'rmagick'
120
+ rescue LoadError
121
+ require 'RMagick'
122
+ rescue LoadError
123
+ puts "WARNING: Failed to require rmagick, image processing may fail!"
124
+ end
125
+ end
126
+ MagickWrapper.new(filename)
127
+ end
128
+ end
129
+ end
130
+
131
+ class MagickWrapper # :nodoc:
132
+ attr_reader :image
133
+ def width
134
+ image.columns
135
+ end
136
+
137
+ def height
138
+ image.rows
139
+ end
140
+
141
+ def initialize(filename)
142
+ @image = ::Magick::Image.read(filename).first
143
+ end
144
+ end
145
+
146
+ class MiniMagickWrapper # :nodoc:
147
+ attr_reader :image
148
+ def width
149
+ image[:width]
150
+ end
151
+
152
+ def height
153
+ image[:height]
154
+ end
155
+
156
+ def initialize(filename)
157
+ @image = ::MiniMagick::Image.open(filename)
158
+ end
159
+ end
160
+
161
+ end # Matchers
162
+ end # Test
163
+ end # CarrierWave
164
+
@@ -0,0 +1,44 @@
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
+ include CarrierWave::Uploader::Callbacks
29
+ include CarrierWave::Uploader::Proxy
30
+ include CarrierWave::Uploader::Url
31
+ include CarrierWave::Uploader::Mountable
32
+ include CarrierWave::Uploader::Cache
33
+ include CarrierWave::Uploader::Store
34
+ include CarrierWave::Uploader::Download
35
+ include CarrierWave::Uploader::Remove
36
+ include CarrierWave::Uploader::ExtensionWhitelist
37
+ include CarrierWave::Uploader::Processing
38
+ include CarrierWave::Uploader::Versions
39
+ include CarrierWave::Uploader::DefaultUrl
40
+ include CarrierWave::Uploader::Configuration
41
+ end # Base
42
+
43
+ end # Uploader
44
+ end # CarrierWave
@@ -0,0 +1,160 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWave
4
+
5
+ class FormNotMultipart < UploadError
6
+ def message
7
+ "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."
8
+ end
9
+ end
10
+
11
+ ##
12
+ # Generates a unique cache id for use in the caching system
13
+ #
14
+ # === Returns
15
+ #
16
+ # [String] a cache id in the format YYYYMMDD-HHMM-PID-RND
17
+ #
18
+ def self.generate_cache_id
19
+ Time.now.strftime('%Y%m%d-%H%M') + '-' + Process.pid.to_s + '-' + ("%04d" % rand(9999))
20
+ end
21
+
22
+ module Uploader
23
+ module Cache
24
+ extend ActiveSupport::Concern
25
+
26
+ include CarrierWave::Uploader::Callbacks
27
+ include CarrierWave::Uploader::Configuration
28
+
29
+ module ClassMethods
30
+
31
+ ##
32
+ # Removes cached files which are older than one day. You could call this method
33
+ # from a rake task to clean out old cached files.
34
+ #
35
+ # You can call this method directly on the module like this:
36
+ #
37
+ # CarrierWave.clean_cached_files!
38
+ #
39
+ # === Note
40
+ #
41
+ # This only works as long as you haven't done anything funky with your cache_dir.
42
+ # It's recommended that you keep cache files in one place only.
43
+ #
44
+ def clean_cached_files!(seconds=60*60*24)
45
+ Dir.glob(File.expand_path(File.join(cache_dir, '*'), CarrierWave.root)).each do |dir|
46
+ time = dir.scan(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})/).first.map { |t| t.to_i }
47
+ time = Time.utc(*time)
48
+ if time < (Time.now.utc - seconds)
49
+ FileUtils.rm_rf(dir)
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ ##
56
+ # Returns true if the uploader has been cached
57
+ #
58
+ # === Returns
59
+ #
60
+ # [Bool] whether the current file is cached
61
+ #
62
+ def cached?
63
+ @cache_id
64
+ end
65
+
66
+ ##
67
+ # Caches the remotely stored file
68
+ #
69
+ # This is useful when about to process images. Most processing solutions
70
+ # require the file to be stored on the local filesystem.
71
+ #
72
+ def cache_stored_file!
73
+ sanitized = SanitizedFile.new :tempfile => StringIO.new(file.read),
74
+ :filename => File.basename(path), :content_type => file.content_type
75
+
76
+ cache! sanitized
77
+ end
78
+
79
+ ##
80
+ # Returns a String which uniquely identifies the currently cached file for later retrieval
81
+ #
82
+ # === Returns
83
+ #
84
+ # [String] a cache name, in the format YYYYMMDD-HHMM-PID-RND/filename.txt
85
+ #
86
+ def cache_name
87
+ File.join(cache_id, full_original_filename) if cache_id and original_filename
88
+ end
89
+
90
+ ##
91
+ # Caches the given file. Calls process! to trigger any process callbacks.
92
+ #
93
+ # === Parameters
94
+ #
95
+ # [new_file (File, IOString, Tempfile)] any kind of file object
96
+ #
97
+ # === Raises
98
+ #
99
+ # [CarrierWave::FormNotMultipart] if the assigned parameter is a string
100
+ #
101
+ def cache!(new_file)
102
+ new_file = CarrierWave::SanitizedFile.new(new_file)
103
+
104
+ unless new_file.empty?
105
+ raise CarrierWave::FormNotMultipart if new_file.is_path? && ensure_multipart_form
106
+
107
+ with_callbacks(:cache, new_file) do
108
+ self.cache_id = CarrierWave.generate_cache_id unless cache_id
109
+
110
+ @filename = new_file.filename
111
+ self.original_filename = new_file.filename
112
+
113
+ @file = new_file.copy_to(cache_path, permissions)
114
+ end
115
+ end
116
+ end
117
+
118
+ ##
119
+ # Retrieves the file with the given cache_name from the cache.
120
+ #
121
+ # === Parameters
122
+ #
123
+ # [cache_name (String)] uniquely identifies a cache file
124
+ #
125
+ # === Raises
126
+ #
127
+ # [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
128
+ #
129
+ def retrieve_from_cache!(cache_name)
130
+ with_callbacks(:retrieve_from_cache, cache_name) do
131
+ self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
132
+ @filename = original_filename
133
+ @file = CarrierWave::SanitizedFile.new(cache_path)
134
+ end
135
+ end
136
+
137
+ private
138
+
139
+ def cache_path
140
+ File.expand_path(File.join(cache_dir, cache_name), root)
141
+ end
142
+
143
+ attr_reader :cache_id, :original_filename
144
+
145
+ # We can override the full_original_filename method in other modules
146
+ alias_method :full_original_filename, :original_filename
147
+
148
+ def cache_id=(cache_id)
149
+ raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}\z/
150
+ @cache_id = cache_id
151
+ end
152
+
153
+ def original_filename=(filename)
154
+ raise CarrierWave::InvalidParameter, "invalid filename" if filename =~ CarrierWave::SanitizedFile.sanitize_regexp
155
+ @original_filename = filename
156
+ end
157
+
158
+ end # Cache
159
+ end # Uploader
160
+ end # CarrierWave
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module CarrierWave
4
+ module Uploader
5
+ module Callbacks
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_before_callbacks, :_after_callbacks,
10
+ :instance_writer => false
11
+ self._before_callbacks = Hash.new []
12
+ self._after_callbacks = Hash.new []
13
+ end
14
+
15
+ def with_callbacks(kind, *args)
16
+ self.class._before_callbacks[kind].each { |c| send c, *args }
17
+ yield
18
+ self.class._after_callbacks[kind].each { |c| send c, *args }
19
+ end
20
+
21
+ module ClassMethods
22
+ def before(kind, callback)
23
+ self._before_callbacks = self._before_callbacks.
24
+ merge kind => _before_callbacks[kind] + [callback]
25
+ end
26
+
27
+ def after(kind, callback)
28
+ self._after_callbacks = self._after_callbacks.
29
+ merge kind => _after_callbacks[kind] + [callback]
30
+ end
31
+ end # ClassMethods
32
+
33
+ end # Callbacks
34
+ end # Uploader
35
+ end # CarrierWave
@@ -0,0 +1,162 @@
1
+ module CarrierWave
2
+
3
+ module Uploader
4
+ module Configuration
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :_storage, :instance_writer => false
9
+
10
+ add_config :root
11
+ add_config :permissions
12
+ add_config :storage_engines
13
+ add_config :s3_access_policy
14
+ add_config :s3_bucket
15
+ add_config :s3_access_key_id
16
+ add_config :s3_secret_access_key
17
+ add_config :s3_cnamed
18
+ add_config :s3_headers
19
+ add_config :s3_region
20
+ add_config :s3_use_ssl
21
+ add_config :s3_authentication_timeout
22
+ add_config :cloud_files_username
23
+ add_config :cloud_files_api_key
24
+ add_config :cloud_files_container
25
+ add_config :cloud_files_cdn_host
26
+ add_config :cloud_files_auth_url
27
+ add_config :cloud_files_snet
28
+ add_config :grid_fs_connection
29
+ add_config :grid_fs_database
30
+ add_config :grid_fs_host
31
+ add_config :grid_fs_port
32
+ add_config :grid_fs_username
33
+ add_config :grid_fs_password
34
+ add_config :grid_fs_access_url
35
+ add_config :store_dir
36
+ add_config :cache_dir
37
+ add_config :enable_processing
38
+ add_config :ensure_multipart_form
39
+ add_config :delete_tmp_file_after_storage
40
+ add_config :remove_previously_stored_files_after_update
41
+
42
+ # fog
43
+ add_config :fog_attributes
44
+ add_config :fog_credentials
45
+ add_config :fog_directory
46
+ add_config :fog_host
47
+ add_config :fog_public
48
+ add_config :fog_authenticated_url_expiration
49
+
50
+ # Mounting
51
+ add_config :ignore_integrity_errors
52
+ add_config :ignore_processing_errors
53
+ add_config :validate_integrity
54
+ add_config :validate_processing
55
+ add_config :mount_on
56
+
57
+ # set default values
58
+ reset_config
59
+ end
60
+
61
+ module ClassMethods
62
+
63
+ ##
64
+ # Sets the storage engine to be used when storing files with this uploader.
65
+ # Can be any class that implements a #store!(CarrierWave::SanitizedFile) and a #retrieve!
66
+ # method. See lib/carrierwave/storage/file.rb for an example. Storage engines should
67
+ # be added to CarrierWave::Uploader::Base.storage_engines so they can be referred
68
+ # to by a symbol, which should be more convenient
69
+ #
70
+ # If no argument is given, it will simply return the currently used storage engine.
71
+ #
72
+ # === Parameters
73
+ #
74
+ # [storage (Symbol, Class)] The storage engine to use for this uploader
75
+ #
76
+ # === Returns
77
+ #
78
+ # [Class] the storage engine to be used with this uploader
79
+ #
80
+ # === Examples
81
+ #
82
+ # storage :file
83
+ # storage CarrierWave::Storage::File
84
+ # storage MyCustomStorageEngine
85
+ #
86
+ def storage(storage = nil)
87
+ if storage
88
+ self._storage = storage.is_a?(Symbol) ? eval(storage_engines[storage]) : storage
89
+ end
90
+ _storage
91
+ end
92
+ alias_method :storage=, :storage
93
+
94
+ def add_config(name)
95
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
96
+ def self.#{name}(value=nil)
97
+ @#{name} = value if value
98
+ return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
99
+ name = superclass.#{name}
100
+ return nil if name.nil? && !instance_variable_defined?("@#{name}")
101
+ @#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
102
+ end
103
+
104
+ def self.#{name}=(value)
105
+ @#{name} = value
106
+ end
107
+
108
+ def #{name}
109
+ self.class.#{name}
110
+ end
111
+ RUBY
112
+ end
113
+
114
+ def configure
115
+ yield self
116
+ end
117
+
118
+ ##
119
+ # sets configuration back to default
120
+ #
121
+ def reset_config
122
+ configure do |config|
123
+ config.permissions = 0644
124
+ config.storage_engines = {
125
+ :file => "CarrierWave::Storage::File",
126
+ :fog => "CarrierWave::Storage::Fog",
127
+ :s3 => "CarrierWave::Storage::S3",
128
+ :grid_fs => "CarrierWave::Storage::GridFS",
129
+ :right_s3 => "CarrierWave::Storage::RightS3",
130
+ :cloud_files => "CarrierWave::Storage::CloudFiles"
131
+ }
132
+ config.storage = :file
133
+ config.s3_headers = {}
134
+ config.s3_access_policy = :public_read
135
+ config.s3_region = 'us-east-1'
136
+ config.s3_authentication_timeout = 600
137
+ config.grid_fs_database = 'carrierwave'
138
+ config.grid_fs_host = 'localhost'
139
+ config.grid_fs_port = 27017
140
+ config.fog_attributes = {}
141
+ config.fog_credentials = {}
142
+ config.fog_public = true
143
+ config.fog_authenticated_url_expiration = 600
144
+ config.store_dir = 'uploads'
145
+ config.cache_dir = 'uploads/tmp'
146
+ config.delete_tmp_file_after_storage = true
147
+ config.remove_previously_stored_files_after_update = true
148
+ config.ignore_integrity_errors = true
149
+ config.ignore_processing_errors = true
150
+ config.validate_integrity = true
151
+ config.validate_processing = true
152
+ config.root = CarrierWave.root
153
+ config.enable_processing = true
154
+ config.ensure_multipart_form = true
155
+ end
156
+ end
157
+ end
158
+
159
+ end
160
+ end
161
+ end
162
+