summaryrefslogtreecommitdiff
path: root/vendor/plugins/paperclip/lib/paperclip.rb
diff options
context:
space:
mode:
authorhukl <contact@smyck.org>2009-04-24 11:43:08 +0200
committerhukl <contact@smyck.org>2009-04-25 14:55:27 +0200
commitcf1b60e0cfa7d1a8f4a80d686649cc12e73a634e (patch)
treeb68bd845d290ce968892c4532bcff52083925834 /vendor/plugins/paperclip/lib/paperclip.rb
parentb2b78c06074046bd73cc3408a29386a976f0469c (diff)
Integrated basic Asset upload functionality. You can upload files now and use their url in pages.
Diffstat (limited to 'vendor/plugins/paperclip/lib/paperclip.rb')
-rw-r--r--vendor/plugins/paperclip/lib/paperclip.rb318
1 files changed, 318 insertions, 0 deletions
diff --git a/vendor/plugins/paperclip/lib/paperclip.rb b/vendor/plugins/paperclip/lib/paperclip.rb
new file mode 100644
index 0000000..74c3d79
--- /dev/null
+++ b/vendor/plugins/paperclip/lib/paperclip.rb
@@ -0,0 +1,318 @@
1# Paperclip allows file attachments that are stored in the filesystem. All graphical
2# transformations are done using the Graphics/ImageMagick command line utilities and
3# are stored in Tempfiles until the record is saved. Paperclip does not require a
4# separate model for storing the attachment's information, instead adding a few simple
5# columns to your table.
6#
7# Author:: Jon Yurek
8# Copyright:: Copyright (c) 2008 thoughtbot, inc.
9# License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
10#
11# Paperclip defines an attachment as any file, though it makes special considerations
12# for image files. You can declare that a model has an attached file with the
13# +has_attached_file+ method:
14#
15# class User < ActiveRecord::Base
16# has_attached_file :avatar, :styles => { :thumb => "100x100" }
17# end
18#
19# user = User.new
20# user.avatar = params[:user][:avatar]
21# user.avatar.url
22# # => "/users/avatars/4/original_me.jpg"
23# user.avatar.url(:thumb)
24# # => "/users/avatars/4/thumb_me.jpg"
25#
26# See the +has_attached_file+ documentation for more details.
27
28require 'tempfile'
29require 'paperclip/upfile'
30require 'paperclip/iostream'
31require 'paperclip/geometry'
32require 'paperclip/processor'
33require 'paperclip/thumbnail'
34require 'paperclip/storage'
35require 'paperclip/attachment'
36if defined? RAILS_ROOT
37 Dir.glob(File.join(File.expand_path(RAILS_ROOT), "lib", "paperclip_processors", "*.rb")).each do |processor|
38 require processor
39 end
40end
41
42# The base module that gets included in ActiveRecord::Base. See the
43# documentation for Paperclip::ClassMethods for more useful information.
44module Paperclip
45
46 VERSION = "2.2.8"
47
48 class << self
49 # Provides configurability to Paperclip. There are a number of options available, such as:
50 # * whiny_thumbnails: Will raise an error if Paperclip cannot process thumbnails of
51 # an uploaded image. Defaults to true.
52 # * log: Logs progress to the Rails log. Uses ActiveRecord's logger, so honors
53 # log levels, etc. Defaults to true.
54 # * command_path: Defines the path at which to find the command line
55 # programs if they are not visible to Rails the system's search path. Defaults to
56 # nil, which uses the first executable found in the user's search path.
57 # * image_magick_path: Deprecated alias of command_path.
58 def options
59 @options ||= {
60 :whiny_thumbnails => true,
61 :image_magick_path => nil,
62 :command_path => nil,
63 :log => true,
64 :swallow_stderr => true
65 }
66 end
67
68 def path_for_command command #:nodoc:
69 if options[:image_magick_path]
70 warn("[DEPRECATION] :image_magick_path is deprecated and will be removed. Use :command_path instead")
71 end
72 path = [options[:command_path] || options[:image_magick_path], command].compact
73 File.join(*path)
74 end
75
76 def interpolates key, &block
77 Paperclip::Attachment.interpolations[key] = block
78 end
79
80 # The run method takes a command to execute and a string of parameters
81 # that get passed to it. The command is prefixed with the :command_path
82 # option from Paperclip.options. If you have many commands to run and
83 # they are in different paths, the suggested course of action is to
84 # symlink them so they are all in the same directory.
85 #
86 # If the command returns with a result code that is not one of the
87 # expected_outcodes, a PaperclipCommandLineError will be raised. Generally
88 # a code of 0 is expected, but a list of codes may be passed if necessary.
89 def run cmd, params = "", expected_outcodes = 0
90 command = %Q<#{%Q[#{path_for_command(cmd)} #{params}].gsub(/\s+/, " ")}>
91 command = "#{command} 2>#{bit_bucket}" if Paperclip.options[:swallow_stderr]
92 output = `#{command}`
93 unless [expected_outcodes].flatten.include?($?.exitstatus)
94 raise PaperclipCommandLineError, "Error while running #{cmd}"
95 end
96 output
97 end
98
99 def bit_bucket #:nodoc:
100 File.exists?("/dev/null") ? "/dev/null" : "NUL"
101 end
102
103 def included base #:nodoc:
104 base.extend ClassMethods
105 unless base.respond_to?(:define_callbacks)
106 base.send(:include, Paperclip::CallbackCompatability)
107 end
108 end
109
110 def processor name #:nodoc:
111 name = name.to_s.camelize
112 processor = Paperclip.const_get(name)
113 unless processor.ancestors.include?(Paperclip::Processor)
114 raise PaperclipError.new("Processor #{name} was not found")
115 end
116 processor
117 end
118 end
119
120 class PaperclipError < StandardError #:nodoc:
121 end
122
123 class PaperclipCommandLineError < StandardError #:nodoc:
124 end
125
126 class NotIdentifiedByImageMagickError < PaperclipError #:nodoc:
127 end
128
129 module ClassMethods
130 # +has_attached_file+ gives the class it is called on an attribute that maps to a file. This
131 # is typically a file stored somewhere on the filesystem and has been uploaded by a user.
132 # The attribute returns a Paperclip::Attachment object which handles the management of
133 # that file. The intent is to make the attachment as much like a normal attribute. The
134 # thumbnails will be created when the new file is assigned, but they will *not* be saved
135 # until +save+ is called on the record. Likewise, if the attribute is set to +nil+ is
136 # called on it, the attachment will *not* be deleted until +save+ is called. See the
137 # Paperclip::Attachment documentation for more specifics. There are a number of options
138 # you can set to change the behavior of a Paperclip attachment:
139 # * +url+: The full URL of where the attachment is publically accessible. This can just
140 # as easily point to a directory served directly through Apache as it can to an action
141 # that can control permissions. You can specify the full domain and path, but usually
142 # just an absolute path is sufficient. The leading slash *must* be included manually for
143 # absolute paths. The default value is
144 # "/system/:attachment/:id/:style/:basename.:extension". See
145 # Paperclip::Attachment#interpolate for more information on variable interpolaton.
146 # :url => "/:class/:attachment/:id/:style_:basename.:extension"
147 # :url => "http://some.other.host/stuff/:class/:id_:extension"
148 # * +default_url+: The URL that will be returned if there is no attachment assigned.
149 # This field is interpolated just as the url is. The default value is
150 # "/:attachment/:style/missing.png"
151 # has_attached_file :avatar, :default_url => "/images/default_:style_avatar.png"
152 # User.new.avatar_url(:small) # => "/images/default_small_avatar.png"
153 # * +styles+: A hash of thumbnail styles and their geometries. You can find more about
154 # geometry strings at the ImageMagick website
155 # (http://www.imagemagick.org/script/command-line-options.php#resize). Paperclip
156 # also adds the "#" option (e.g. "50x50#"), which will resize the image to fit maximally
157 # inside the dimensions and then crop the rest off (weighted at the center). The
158 # default value is to generate no thumbnails.
159 # * +default_style+: The thumbnail style that will be used by default URLs.
160 # Defaults to +original+.
161 # has_attached_file :avatar, :styles => { :normal => "100x100#" },
162 # :default_style => :normal
163 # user.avatar.url # => "/avatars/23/normal_me.png"
164 # * +whiny_thumbnails+: Will raise an error if Paperclip cannot post_process an uploaded file due
165 # to a command line error. This will override the global setting for this attachment.
166 # Defaults to true.
167 # * +convert_options+: When creating thumbnails, use this free-form options
168 # field to pass in various convert command options. Typical options are "-strip" to
169 # remove all Exif data from the image (save space for thumbnails and avatars) or
170 # "-depth 8" to specify the bit depth of the resulting conversion. See ImageMagick
171 # convert documentation for more options: (http://www.imagemagick.org/script/convert.php)
172 # Note that this option takes a hash of options, each of which correspond to the style
173 # of thumbnail being generated. You can also specify :all as a key, which will apply
174 # to all of the thumbnails being generated. If you specify options for the :original,
175 # it would be best if you did not specify destructive options, as the intent of keeping
176 # the original around is to regenerate all the thumbnails when requirements change.
177 # has_attached_file :avatar, :styles => { :large => "300x300", :negative => "100x100" }
178 # :convert_options => {
179 # :all => "-strip",
180 # :negative => "-negate"
181 # }
182 # * +storage+: Chooses the storage backend where the files will be stored. The current
183 # choices are :filesystem and :s3. The default is :filesystem. Make sure you read the
184 # documentation for Paperclip::Storage::Filesystem and Paperclip::Storage::S3
185 # for backend-specific options.
186 def has_attached_file name, options = {}
187 include InstanceMethods
188
189 write_inheritable_attribute(:attachment_definitions, {}) if attachment_definitions.nil?
190 attachment_definitions[name] = {:validations => {}}.merge(options)
191
192 after_save :save_attached_files
193 before_destroy :destroy_attached_files
194
195 define_callbacks :before_post_process, :after_post_process
196 define_callbacks :"before_#{name}_post_process", :"after_#{name}_post_process"
197
198 define_method name do |*args|
199 a = attachment_for(name)
200 (args.length > 0) ? a.to_s(args.first) : a
201 end
202
203 define_method "#{name}=" do |file|
204 attachment_for(name).assign(file)
205 end
206
207 define_method "#{name}?" do
208 attachment_for(name).file?
209 end
210
211 validates_each(name) do |record, attr, value|
212 attachment = record.attachment_for(name)
213 attachment.send(:flush_errors) unless attachment.valid?
214 end
215 end
216
217 # Places ActiveRecord-style validations on the size of the file assigned. The
218 # possible options are:
219 # * +in+: a Range of bytes (i.e. +1..1.megabyte+),
220 # * +less_than+: equivalent to :in => 0..options[:less_than]
221 # * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
222 # * +message+: error message to display, use :min and :max as replacements
223 def validates_attachment_size name, options = {}
224 min = options[:greater_than] || (options[:in] && options[:in].first) || 0
225 max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0)
226 range = (min..max)
227 message = options[:message] || "file size must be between :min and :max bytes."
228
229 attachment_definitions[name][:validations][:size] = lambda do |attachment, instance|
230 if attachment.file? && !range.include?(attachment.size.to_i)
231 message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s)
232 end
233 end
234 end
235
236 # Adds errors if thumbnail creation fails. The same as specifying :whiny_thumbnails => true.
237 def validates_attachment_thumbnails name, options = {}
238 attachment_definitions[name][:whiny_thumbnails] = true
239 end
240
241 # Places ActiveRecord-style validations on the presence of a file.
242 def validates_attachment_presence name, options = {}
243 message = options[:message] || "must be set."
244 attachment_definitions[name][:validations][:presence] = lambda do |attachment, instance|
245 message unless attachment.file?
246 end
247 end
248
249 # Places ActiveRecord-style validations on the content type of the file
250 # assigned. The possible options are:
251 # * +content_type+: Allowed content types. Can be a single content type
252 # or an array. Each type can be a String or a Regexp. It should be
253 # noted that Internet Explorer upload files with content_types that you
254 # may not expect. For example, JPEG images are given image/pjpeg and
255 # PNGs are image/x-png, so keep that in mind when determining how you
256 # match. Allows all by default.
257 # * +message+: The message to display when the uploaded file has an invalid
258 # content type.
259 # NOTE: If you do not specify an [attachment]_content_type field on your
260 # model, content_type validation will work _ONLY upon assignment_ and
261 # re-validation after the instance has been reloaded will always succeed.
262 def validates_attachment_content_type name, options = {}
263 attachment_definitions[name][:validations][:content_type] = lambda do |attachment, instance|
264 valid_types = [options[:content_type]].flatten
265
266 unless attachment.original_filename.blank?
267 unless valid_types.blank?
268 content_type = attachment.instance_read(:content_type)
269 unless valid_types.any?{|t| content_type.nil? || t === content_type }
270 options[:message] || "is not one of the allowed file types."
271 end
272 end
273 end
274 end
275 end
276
277 # Returns the attachment definitions defined by each call to
278 # has_attached_file.
279 def attachment_definitions
280 read_inheritable_attribute(:attachment_definitions)
281 end
282 end
283
284 module InstanceMethods #:nodoc:
285 def attachment_for name
286 @_paperclip_attachments ||= {}
287 @_paperclip_attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
288 end
289
290 def each_attachment
291 self.class.attachment_definitions.each do |name, definition|
292 yield(name, attachment_for(name))
293 end
294 end
295
296 def save_attached_files
297 logger.info("[paperclip] Saving attachments.")
298 each_attachment do |name, attachment|
299 attachment.send(:save)
300 end
301 end
302
303 def destroy_attached_files
304 logger.info("[paperclip] Deleting attachments.")
305 each_attachment do |name, attachment|
306 attachment.send(:queue_existing_for_delete)
307 attachment.send(:flush_deletes)
308 end
309 end
310 end
311
312end
313
314# Set it all up.
315if Object.const_defined?("ActiveRecord")
316 ActiveRecord::Base.send(:include, Paperclip)
317 File.send(:include, Paperclip::Upfile)
318end