carrierwave 0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of carrierwave might be problematic. Click here for more details.

@@ -0,0 +1,20 @@
1
+ require 'activerecord'
2
+
3
+ module CarrierWave
4
+ module ActiveRecord
5
+
6
+ include CarrierWave::Mount
7
+
8
+ def after_mount(column, uploader)
9
+ alias_method :read_uploader, :read_attribute
10
+ alias_method :write_uploader, :write_attribute
11
+
12
+ before_save do |record|
13
+ record.send("store_#{column}!")
14
+ end
15
+ end
16
+
17
+ end # ActiveRecord
18
+ end # CarrierWave
19
+
20
+ ActiveRecord::Base.send(:extend, CarrierWave::ActiveRecord)
@@ -0,0 +1,20 @@
1
+ require 'dm-core'
2
+
3
+ module CarrierWave
4
+ module DataMapper
5
+
6
+ include CarrierWave::Mount
7
+
8
+ def after_mount(column, uploader)
9
+ alias_method :read_uploader, :attribute_get
10
+ alias_method :write_uploader, :attribute_set
11
+
12
+ before :save do
13
+ send("store_#{column}!")
14
+ end
15
+ end
16
+
17
+ end # DataMapper
18
+ end # CarrierWave
19
+
20
+ DataMapper::Model.send(:include, CarrierWave::DataMapper)
@@ -0,0 +1,70 @@
1
+ require "image_science"
2
+
3
+ module CarrierWave
4
+ module ImageScience
5
+
6
+ # Resize the image so that it will not exceed the dimensions passed
7
+ # via geometry, geometry should be a string, formatted like '200x100' where
8
+ # the first number is the height and the second is the width
9
+ def resize!( geometry )
10
+ ::ImageScience.with_image(self.current_path) do |img|
11
+ width, height = extract_dimensions(img.width, img.height, geometry)
12
+ img.resize( width, height ) do |file|
13
+ file.save( self.current_path )
14
+ end
15
+ end
16
+ end
17
+
18
+ # Resize and crop the image so that it will have the exact dimensions passed
19
+ # via geometry, geometry should be a string, formatted like '200x100' where
20
+ # the first number is the height and the second is the width
21
+ def crop_resized!( geometry )
22
+ ::ImageScience.with_image(self.current_path) do |img|
23
+ new_width, new_height = geometry.split('x').map{|i| i.to_i }
24
+
25
+ width, height = extract_dimensions_for_crop(img.width, img.height, geometry)
26
+ x_offset, y_offset = extract_placement_for_crop(width, height, geometry)
27
+
28
+ img.resize( width, height ) do |i2|
29
+
30
+ i2.with_crop( x_offset, y_offset, new_width + x_offset, new_height + y_offset) do |file|
31
+ file.save( self.current_path )
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def extract_dimensions(width, height, new_geometry, type = :resize)
40
+ new_width, new_height = convert_geometry(new_geometry)
41
+
42
+ aspect_ratio = width.to_f / height.to_f
43
+ new_aspect_ratio = new_width / new_height
44
+
45
+ if (new_aspect_ratio > aspect_ratio) ^ ( type == :crop ) # Image is too wide, the caret is the XOR operator
46
+ new_width, new_height = [ (new_height * aspect_ratio), new_height]
47
+ else #Image is too narrow
48
+ new_width, new_height = [ new_width, (new_width / aspect_ratio)]
49
+ end
50
+
51
+ [new_width, new_height].collect! { |v| v.round }
52
+ end
53
+
54
+ def extract_dimensions_for_crop(width, height, new_geometry)
55
+ extract_dimensions(width, height, new_geometry, :crop)
56
+ end
57
+
58
+ def extract_placement_for_crop(width, height, new_geometry)
59
+ new_width, new_height = convert_geometry(new_geometry)
60
+ x_offset = (width / 2.0) - (new_width / 2.0)
61
+ y_offset = (height / 2.0) - (new_height / 2.0)
62
+ [x_offset, y_offset].collect! { |v| v.round }
63
+ end
64
+
65
+ def convert_geometry(geometry)
66
+ geometry.split('x').map{|i| i.to_f }
67
+ end
68
+
69
+ end # ImageScience
70
+ end # CarrierWave
@@ -0,0 +1,161 @@
1
+ require 'rmagick'
2
+
3
+ module CarrierWave
4
+
5
+ ##
6
+ # This module simplifies manipulation with RMagick by providing a set
7
+ # of convenient helper methods. If you want to use them, you'll need to
8
+ # require this file
9
+ #
10
+ # require 'carrierwave/processing/rmagick'
11
+ #
12
+ # And then include it in your uploader
13
+ #
14
+ # MyUploade < CarrierWave::Uploader
15
+ # include CarrierWave::RMagick
16
+ # end
17
+ #
18
+ # You can now use the provided helpers:
19
+ #
20
+ # MyUploade < CarrierWave::Uploader
21
+ # include CarrierWave::RMagick
22
+ #
23
+ # process :resize_to_fit => [200, 200]
24
+ # end
25
+ #
26
+ # Or create your own helpers with the powerful manipulate! method. Check
27
+ # out the RMagick docs at http://www.imagemagick.org/RMagick/doc/ for more
28
+ # info
29
+ #
30
+ # MyUploade < CarrierWave::Uploader
31
+ # include CarrierWave::RMagick
32
+ #
33
+ # process :do_stuff => 10.0
34
+ #
35
+ # def do_stuff(blur_factor)
36
+ # manipulate! do |img|
37
+ # img = img.sepiatone
38
+ # img = img.auto_orient
39
+ # img = img.radial_blur(blur_factor)
40
+ # end
41
+ # end
42
+ # end
43
+ #
44
+ module RMagick
45
+
46
+ ##
47
+ # Changes the image encoding format to the given format
48
+ #
49
+ # @see http://www.imagemagick.org/RMagick/doc/magick.html#formats
50
+ # @param [#to_s] format an abreviation of the format
51
+ # @yieldparam [Magick::Image] img additional manipulations to perform
52
+ # @example
53
+ # image.convert(:png)
54
+ #
55
+ def convert(format)
56
+ manipulate! do |img|
57
+ img.format = format.to_s.upcase
58
+ img = yield(img) if block_given?
59
+ img
60
+ end
61
+ end
62
+
63
+ ##
64
+ # From the RMagick documentation: "Resize the image to fit within the
65
+ # specified dimensions while retaining the original aspect ratio. The
66
+ # image may be shorter or narrower than specified in the smaller dimension
67
+ # but will not be larger than the specified values."
68
+ #
69
+ # @see http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fit
70
+ #
71
+ # @param [Integer] width the width to scale the image to
72
+ # @param [Integer] height the height to scale the image to
73
+ # @yieldparam [Magick::Image] img additional manipulations to perform
74
+ #
75
+ def resize_to_fit(width, height)
76
+ manipulate! do |img|
77
+ img.resize_to_fit!(width, height)
78
+ img = yield(img) if block_given?
79
+ img
80
+ end
81
+ end
82
+
83
+ alias_method :resize, :resize_to_fit
84
+
85
+ ##
86
+ # From the RMagick documentation: "Resize the image to fit within the
87
+ # specified dimensions while retaining the aspect ratio of the original
88
+ # image. If necessary, crop the image in the larger dimension."
89
+ #
90
+ # @see http://www.imagemagick.org/RMagick/doc/image3.html#resize_to_fill
91
+ #
92
+ # @param [Integer] width the width to scale the image to
93
+ # @param [Integer] height the height to scale the image to
94
+ # @yieldparam [Magick::Image] img additional manipulations to perform
95
+ #
96
+ def resize_to_fill(width, height)
97
+ manipulate! do |img|
98
+ img.resize_to_fill!(width, height)
99
+ img = yield(img) if block_given?
100
+ img
101
+ end
102
+ end
103
+
104
+ alias_method :crop_resized, :resize_to_fill
105
+
106
+ ##
107
+ # Resize the image to fit within the specified dimensions while retaining
108
+ # the original aspect ratio. If necessary will pad the remaining area
109
+ # with the given color, which defaults to transparent (for gif and png,
110
+ # white for jpeg).
111
+ #
112
+ # @param [Integer] width the width to scale the image to
113
+ # @param [Integer] height the height to scale the image to
114
+ # @param [String, :transparent] background the color of the background as a hexcode, like "#ff45de"
115
+ # @param [Magick::GravityType] gravity how to position the image
116
+ # @yieldparam [Magick::Image] img additional manipulations to perform
117
+ #
118
+ def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
119
+ manipulate! do |img|
120
+ img.resize_to_fit!(width, height)
121
+ new_img = ::Magick::Image.new(width, height)
122
+ if background == :transparent
123
+ new_img = new_img.matte_floodfill(1, 1)
124
+ else
125
+ new_img = new_img.color_floodfill(1, 1, ::Magick::Pixel.from_color(background))
126
+ end
127
+ new_img = new_img.composite(img, gravity, ::Magick::OverCompositeOp)
128
+ new_img = yield(new_img) if block_given?
129
+ new_img
130
+ end
131
+ end
132
+
133
+ ##
134
+ # Manipulate the image with RMagick. This method will load up an image
135
+ # and then pass each of its frames to the supplied block. It will then
136
+ # save the image to disk.
137
+ #
138
+ # Note: This method assumes that the object responds to current_path
139
+ # any class that this is mixed into must have a current_path method.
140
+ #
141
+ # @yieldparam [Magick::Image] img manipulations to perform
142
+ # @raise [CarrierWave::ProcessingError] if manipulation failed.
143
+ #
144
+ def manipulate!
145
+ image = ::Magick::Image.read(current_path)
146
+
147
+ if image.size > 1
148
+ list = ::Magick::ImageList.new
149
+ image.each do |frame|
150
+ list << yield( frame )
151
+ end
152
+ list.write(current_path)
153
+ else
154
+ yield( image.first ).write(current_path)
155
+ end
156
+ rescue ::Magick::ImageMagickError => e
157
+ raise CarrierWave::ProcessingError.new("Failed to manipulate with rmagick, maybe it is not an image? Original Error: #{e}")
158
+ end
159
+
160
+ end # RMagick
161
+ end # CarrierWave
@@ -0,0 +1,242 @@
1
+ module CarrierWave
2
+
3
+ ##
4
+ # SanitizedFile is a base class which provides a common API around all
5
+ # the different quirky Ruby File libraries. It has support for Tempfile,
6
+ # File, StringIO, Merb-style upload Hashes, as well as paths given as
7
+ # Strings and Pathnames.
8
+ #
9
+ # It's probably needlessly comprehensive and complex. Help is appreciated.
10
+ #
11
+ class SanitizedFile
12
+
13
+ attr_accessor :file, :options
14
+
15
+ def initialize(file, options = {})
16
+ self.file = file
17
+ self.options = options
18
+ end
19
+
20
+ ##
21
+ # Returns the filename as is, without sanizting it.
22
+ #
23
+ # @return [String] the unsanitized filename
24
+ #
25
+ def original_filename
26
+ return @original_filename if @original_filename
27
+ if @file and @file.respond_to?(:original_filename)
28
+ @file.original_filename
29
+ elsif path
30
+ File.basename(path)
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Returns the filename, sanitized to strip out any evil characters.
36
+ #
37
+ # @return [String] the sanitized filename
38
+ #
39
+ def filename
40
+ sanitize(original_filename) if original_filename
41
+ end
42
+
43
+ alias_method :identifier, :filename
44
+
45
+ ##
46
+ # Returns the part of the filename before the extension. So if a file is called 'test.jpeg'
47
+ # this would return 'test'
48
+ #
49
+ # @return [String] the first part of the filename
50
+ #
51
+ def basename
52
+ split_extension(filename)[0] if filename
53
+ end
54
+
55
+ ##
56
+ # Returns the file extension
57
+ #
58
+ # @return [String] the extension
59
+ #
60
+ def extension
61
+ split_extension(filename)[1] if filename
62
+ end
63
+
64
+ ##
65
+ # Returns the file's size.
66
+ #
67
+ # @return [Integer] the file's size in bytes.
68
+ #
69
+ def size
70
+ if string?
71
+ exists? ? File.size(path) : 0
72
+ elsif @file.respond_to?(:size)
73
+ @file.size
74
+ elsif path
75
+ exists? ? File.size(path) : 0
76
+ else
77
+ 0
78
+ end
79
+ end
80
+
81
+ ##
82
+ # Returns the full path to the file. If the file has no path, it will return nil.
83
+ #
84
+ # @return [String, nil] the path where the file is located.
85
+ #
86
+ def path
87
+ unless @file.blank?
88
+ if string?
89
+ File.expand_path(@file)
90
+ elsif @file.respond_to?(:path) and not @file.path.blank?
91
+ File.expand_path(@file.path)
92
+ end
93
+ end
94
+ end
95
+
96
+ ##
97
+ # Returns true if the file is supplied as a pathname or as a string.
98
+ #
99
+ # @return [Boolean]
100
+ #
101
+ def string?
102
+ !!((@file.is_a?(String) || @file.is_a?(Pathname)) && !@file.blank?)
103
+ end
104
+
105
+ ##
106
+ # Checks if the file is valid and has a non-zero size
107
+ #
108
+ # @return [Boolean]
109
+ #
110
+ def empty?
111
+ @file.nil? || self.size.nil? || self.size.zero?
112
+ end
113
+
114
+ ##
115
+ # Checks if the file exists
116
+ #
117
+ # @return [Boolean]
118
+ #
119
+ def exists?
120
+ return File.exists?(self.path) if self.path
121
+ return false
122
+ end
123
+
124
+ ##
125
+ # Returns the contents of the file.
126
+ #
127
+ # @return [String] contents of the file
128
+ #
129
+ def read
130
+ if string?
131
+ File.read(@file)
132
+ else
133
+ @file.rewind if @file.respond_to?(:rewind)
134
+ @file.read
135
+ end
136
+ end
137
+
138
+ ##
139
+ # Moves the file to the given path
140
+ #
141
+ # @param [String] new_path The path where the file should be moved.
142
+ #
143
+ def move_to(new_path)
144
+ return if self.empty?
145
+ new_path = File.expand_path(new_path)
146
+
147
+ mkdir!(new_path)
148
+ if exists?
149
+ FileUtils.mv(path, new_path) unless new_path == path
150
+ else
151
+ File.open(new_path, "wb") { |f| f.write(read) }
152
+ end
153
+ chmod!(new_path)
154
+ self.file = new_path
155
+ end
156
+
157
+ ##
158
+ # Creates a copy of this file and moves it to the given path. Returns the copy.
159
+ #
160
+ # @param [String] new_path The path where the file should be copied to.
161
+ # @return [CarrierWave::SanitizedFile] the location where the file will be stored.
162
+ #
163
+ def copy_to(new_path)
164
+ return if self.empty?
165
+ new_path = File.expand_path(new_path)
166
+
167
+ mkdir!(new_path)
168
+ if exists?
169
+ FileUtils.cp(path, new_path) unless new_path == path
170
+ else
171
+ File.open(new_path, "wb") { |f| f.write(read) }
172
+ end
173
+ chmod!(new_path)
174
+ self.class.new(new_path)
175
+ end
176
+
177
+ ##
178
+ # Removes the file from the filesystem.
179
+ #
180
+ def delete
181
+ FileUtils.rm(self.path) if exists?
182
+ end
183
+
184
+ ##
185
+ # Returns the content type of the file.
186
+ #
187
+ # @return [String] the content type of the file
188
+ #
189
+ def content_type
190
+ return @content_type if @content_type
191
+ @file.content_type.chomp if @file.respond_to?(:content_type) and @file.content_type
192
+ end
193
+
194
+ private
195
+
196
+ def file=(file)
197
+ if file.is_a?(Hash)
198
+ @file = file["tempfile"]
199
+ @original_filename = file["filename"]
200
+ @content_type = file["content_type"]
201
+ else
202
+ @file = file
203
+ @original_filename = nil
204
+ @content_type = nil
205
+ end
206
+ end
207
+
208
+ # create the directory if it doesn't exist
209
+ def mkdir!(path)
210
+ FileUtils.mkdir_p(File.dirname(path)) unless File.exists?(File.dirname(path))
211
+ end
212
+
213
+ def chmod!(path)
214
+ File.chmod(@options[:permissions], path) if @options[:permissions]
215
+ end
216
+
217
+ # Sanitize the filename, to prevent hacking
218
+ def sanitize(name)
219
+ name = name.gsub("\\", "/") # work-around for IE
220
+ name = File.basename(name)
221
+ name = name.gsub(/[^a-zA-Z0-9\.\-\+_]/,"_")
222
+ name = "_#{name}" if name =~ /^\.+$/
223
+ name = "unnamed" if name.size == 0
224
+ return name.downcase
225
+ end
226
+
227
+ def split_extension(fn)
228
+ # regular expressions to try for identifying extensions
229
+ ext_regexps = [
230
+ /^(.+)\.([^\.]{1,3}\.[^\.]{1,4})$/, # matches "something.tar.gz"
231
+ /^(.+)\.([^\.]+)$/ # matches "something.jpg"
232
+ ]
233
+ ext_regexps.each do |regexp|
234
+ if fn =~ regexp
235
+ return $1, $2
236
+ end
237
+ end
238
+ return fn, "" # In case we weren't able to split the extension
239
+ end
240
+
241
+ end # SanitizedFile
242
+ end # CarrierWave