paperclip 2.1.0

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

Potentially problematic release.


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

data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+
2
+ LICENSE
3
+
4
+ The MIT License
5
+
6
+ Copyright (c) 2008 Jon Yurek and thoughtbot, inc.
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.
25
+
26
+
data/README ADDED
@@ -0,0 +1,48 @@
1
+ =Paperclip
2
+
3
+ Paperclip is intended as an easy file attachment library for ActiveRecord. The intent behind it was to keep setup as easy as possible and to treat files as much like other attributes as possible. This means they aren't saved to their final locations on disk, nor are they deleted if set to nil, until ActiveRecord::Base#save is called. It manages validations based on size and presence, if required. It can transform its assigned image into thumbnails if needed, and the prerequisites are as simple as installing ImageMagick (which, for most modern Unix-based systems, is as easy as installing the right packages). Attached files are saved to the filesystem and referenced in the browser by an easily understandable specification, which has sensible and useful defaults.
4
+
5
+ See the documentation for the +has_attached_file+ method for options.
6
+
7
+ ==Usage
8
+
9
+ In your model:
10
+
11
+ class User < ActiveRecord::Base
12
+ has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
13
+ end
14
+
15
+ In your migrations:
16
+
17
+ class AddAvatarColumsToUser < ActiveRecord::Migration
18
+ def self.up
19
+ add_column :users, :avatar_file_name, :string
20
+ add_column :users, :avatar_content_type, :string
21
+ add_column :users, :avatar_file_size, :integer
22
+ end
23
+
24
+ def self.down
25
+ remove_column :users, :avatar_file_name
26
+ remove_column :users, :avatar_content_type
27
+ remove_column :users, :avatar_file_size
28
+ end
29
+ end
30
+
31
+ In your edit and new views:
32
+
33
+ <% form_for :user, @user, :url => user_path, :html => { :multipart => true } do |form| %>
34
+ <%= form.file_field :avatar %>
35
+ <% end %>
36
+
37
+ In your controller:
38
+
39
+ def create
40
+ @user = User.create( params[:user] )
41
+ end
42
+
43
+ In your show view:
44
+
45
+ <%= image_tag @user.avatar.url %>
46
+ <%= image_tag @user.avatar.url(:medium) %>
47
+ <%= image_tag @user.avatar.url(:thumb) %>
48
+
@@ -0,0 +1,84 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+
6
+ $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
7
+ require 'paperclip'
8
+
9
+ desc 'Default: run unit tests.'
10
+ task :default => [:clean, :test]
11
+
12
+ desc 'Test the paperclip plugin.'
13
+ Rake::TestTask.new(:test) do |t|
14
+ t.libs << 'lib' << 'profile'
15
+ t.pattern = 'test/**/test_*.rb'
16
+ t.verbose = true
17
+ end
18
+
19
+ desc 'Start an IRB session with all necessary files required.'
20
+ task :shell do |t|
21
+ chdir File.dirname(__FILE__)
22
+ exec 'irb -I lib/ -I lib/paperclip -r rubygems -r active_record -r tempfile -r init'
23
+ end
24
+
25
+ desc 'Generate documentation for the paperclip plugin.'
26
+ Rake::RDocTask.new(:rdoc) do |rdoc|
27
+ rdoc.rdoc_dir = 'doc'
28
+ rdoc.title = 'Paperclip'
29
+ rdoc.options << '--line-numbers' << '--inline-source'
30
+ rdoc.rdoc_files.include('README')
31
+ rdoc.rdoc_files.include('lib/**/*.rb')
32
+ end
33
+
34
+ desc 'Update documentation on website'
35
+ task :sync_docs => 'rdoc' do
36
+ `rsync -ave ssh doc/ dev@dev.thoughtbot.com:/home/dev/www/dev.thoughtbot.com/paperclip`
37
+ end
38
+
39
+ desc 'Clean up files.'
40
+ task :clean do |t|
41
+ FileUtils.rm_rf "doc"
42
+ FileUtils.rm_rf "tmp"
43
+ FileUtils.rm_rf "pkg"
44
+ FileUtils.rm "test/debug.log" rescue nil
45
+ FileUtils.rm "test/paperclip.db" rescue nil
46
+ end
47
+
48
+ spec = Gem::Specification.new do |s|
49
+ s.name = "paperclip"
50
+ s.version = Paperclip::VERSION
51
+ s.author = "Jon Yurek"
52
+ s.email = "jyurek@thoughtbot.com"
53
+ s.homepage = "http://www.thoughtbot.com/"
54
+ s.platform = Gem::Platform::RUBY
55
+ s.summary = "File attachments as attributes for ActiveRecord"
56
+ s.files = FileList["README",
57
+ "LICENSE",
58
+ "Rakefile",
59
+ "init.rb",
60
+ "{generators,lib,tasks,test}/**/*"].to_a
61
+ s.require_path = "lib"
62
+ s.test_files = FileList["test/**/test_*.rb"].to_a
63
+ s.rubyforge_project = "paperclip"
64
+ s.has_rdoc = true
65
+ s.extra_rdoc_files = ["README"]
66
+ s.rdoc_options << '--line-numbers' << '--inline-source'
67
+ s.requirements << "ImageMagick"
68
+ end
69
+
70
+ Rake::GemPackageTask.new(spec) do |pkg|
71
+ pkg.need_tar = true
72
+ end
73
+
74
+ desc "Release new version"
75
+ task :release => [:test, :sync_docs, :gem] do
76
+ require 'rubygems'
77
+ require 'rubyforge'
78
+ r = RubyForge.new
79
+ r.login
80
+ r.add_release spec.rubyforge_project,
81
+ spec.name,
82
+ spec.version,
83
+ File.join("pkg", "#{spec.name}-#{spec.version}.gem")
84
+ end
@@ -0,0 +1,5 @@
1
+ Usage:
2
+
3
+ script/generate attachment Class attachment1 attachment2
4
+
5
+ This will create a migration that will add the proper columns to your class's table.
@@ -0,0 +1,27 @@
1
+ class PaperclipGenerator < Rails::Generator::NamedBase
2
+ attr_accessor :attachments, :migration_name
3
+
4
+ def initialize(args, options = {})
5
+ super
6
+ @class_name, @attachments = args[0], args[1..-1]
7
+ end
8
+
9
+ def manifest
10
+ file_name = generate_file_name
11
+ @migration_name = file_name.camelize
12
+ record do |m|
13
+ m.migration_template "paperclip_migration.rb",
14
+ File.join('db', 'migrate'),
15
+ :migration_file_name => file_name
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def generate_file_name
22
+ names = attachments.map{|a| a.underscore }
23
+ names = names[0..-2] + ["and", names[-1]] if names.length > 1
24
+ "add_attachments_#{names.join("_")}_to_#{@class_name.underscore}"
25
+ end
26
+
27
+ end
@@ -0,0 +1,17 @@
1
+ class <%= migration_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ <% attachments.each do |attachment| -%>
4
+ add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name, :string
5
+ add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type, :string
6
+ add_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_size, :integer
7
+ <% end -%>
8
+ end
9
+
10
+ def self.down
11
+ <% attachments.each do |attachment| -%>
12
+ remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_name
13
+ remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_content_type
14
+ remove_column :<%= class_name.underscore.camelize.tableize %>, :<%= attachment %>_file_size
15
+ <% end -%>
16
+ end
17
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "lib", "paperclip")
@@ -0,0 +1,209 @@
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
+
28
+ require 'tempfile'
29
+ require 'paperclip/upfile'
30
+ require 'paperclip/iostream'
31
+ require 'paperclip/geometry'
32
+ require 'paperclip/thumbnail'
33
+ require 'paperclip/storage'
34
+ require 'paperclip/attachment'
35
+
36
+ # The base module that gets included in ActiveRecord::Base.
37
+ module Paperclip
38
+
39
+ VERSION = "2.1.0"
40
+
41
+ class << self
42
+ # Provides configurability to Paperclip. There are a number of options available, such as:
43
+ # * whiny_thumbnails: Will raise an error if Paperclip cannot process thumbnails of
44
+ # an uploaded image. Defaults to true.
45
+ # * image_magick_path: Defines the path at which to find the +convert+ and +identify+
46
+ # programs if they are not visible to Rails the system's search path. Defaults to
47
+ # nil, which uses the first executable found in the search path.
48
+ def options
49
+ @options ||= {
50
+ :whiny_thumbnails => true,
51
+ :image_magick_path => nil
52
+ }
53
+ end
54
+
55
+ def path_for_command command #:nodoc:
56
+ path = [options[:image_magick_path], command].compact
57
+ File.join(*path)
58
+ end
59
+
60
+ def included base #:nodoc:
61
+ base.extend ClassMethods
62
+ end
63
+ end
64
+
65
+ class PaperclipError < StandardError #:nodoc:
66
+ end
67
+
68
+ module ClassMethods
69
+ # +has_attached_file+ gives the class it is called on an attribute that maps to a file. This
70
+ # is typically a file stored somewhere on the filesystem and has been uploaded by a user.
71
+ # The attribute returns a Paperclip::Attachment object which handles the management of
72
+ # that file. The intent is to make the attachment as much like a normal attribute. The
73
+ # thumbnails will be created when the new file is assigned, but they will *not* be saved
74
+ # until +save+ is called on the record. Likewise, if the attribute is set to +nil+ is
75
+ # called on it, the attachment will *not* be deleted until +save+ is called. See the
76
+ # Paperclip::Attachment documentation for more specifics. There are a number of options
77
+ # you can set to change the behavior of a Paperclip attachment:
78
+ # * +url+: The full URL of where the attachment is publically accessible. This can just
79
+ # as easily point to a directory served directly through Apache as it can to an action
80
+ # that can control permissions. You can specify the full domain and path, but usually
81
+ # just an absolute path is sufficient. The leading slash must be included manually for
82
+ # absolute paths. The default value is "/:class/:attachment/:id/:style_:filename". See
83
+ # Paperclip::Attachment#interpolate for more information on variable interpolaton.
84
+ # :url => "/:attachment/:id/:style_:basename:extension"
85
+ # :url => "http://some.other.host/stuff/:class/:id_:extension"
86
+ # * +default_url+: The URL that will be returned if there is no attachment assigned.
87
+ # This field is interpolated just as the url is. The default value is
88
+ # "/:class/:attachment/missing_:style.png"
89
+ # has_attached_file :avatar, :default_url => "/images/default_:style_avatar.png"
90
+ # User.new.avatar_url(:small) # => "/images/default_small_avatar.png"
91
+ # * +styles+: A hash of thumbnail styles and their geometries. You can find more about
92
+ # geometry strings at the ImageMagick website
93
+ # (http://www.imagemagick.org/script/command-line-options.php#resize). Paperclip
94
+ # also adds the "#" option (e.g. "50x50#"), which will resize the image to fit maximally
95
+ # inside the dimensions and then crop the rest off (weighted at the center). The
96
+ # default value is to generate no thumbnails.
97
+ # * +default_style+: The thumbnail style that will be used by default URLs.
98
+ # Defaults to +original+.
99
+ # has_attached_file :avatar, :styles => { :normal => "100x100#" },
100
+ # :default_style => :normal
101
+ # user.avatar.url # => "/avatars/23/normal_me.png"
102
+ # * +path+: The location of the repository of attachments on disk. This can be coordinated
103
+ # with the value of the +url+ option to allow files to be saved into a place where Apache
104
+ # can serve them without hitting your app. Defaults to
105
+ # ":rails_root/public/:class/:attachment/:id/:style_:filename".
106
+ # By default this places the files in the app's public directory which can be served
107
+ # directly. If you are using capistrano for deployment, a good idea would be to
108
+ # make a symlink to the capistrano-created system directory from inside your app's
109
+ # public directory.
110
+ # See Paperclip::Attachment#interpolate for more information on variable interpolaton.
111
+ # :path => "/var/app/attachments/:class/:id/:style/:filename"
112
+ # * +whiny_thumbnails+: Will raise an error if Paperclip cannot process thumbnails of an
113
+ # uploaded image. This will ovrride the global setting for this attachment.
114
+ # Defaults to true.
115
+ def has_attached_file name, options = {}
116
+ include InstanceMethods
117
+
118
+ write_inheritable_attribute(:attachment_definitions, {}) if attachment_definitions.nil?
119
+ attachment_definitions[name] = {:validations => []}.merge(options)
120
+
121
+ after_save :save_attached_files
122
+ before_destroy :destroy_attached_files
123
+
124
+ define_method name do |*args|
125
+ a = attachment_for(name)
126
+ (args.length > 0) ? a.to_s(args.first) : a
127
+ end
128
+
129
+ define_method "#{name}=" do |file|
130
+ attachment_for(name).assign(file)
131
+ end
132
+
133
+ define_method "#{name}?" do
134
+ ! attachment_for(name).file.nil?
135
+ end
136
+
137
+ validates_each(name) do |record, attr, value|
138
+ value.send(:flush_errors)
139
+ end
140
+ end
141
+
142
+ # Places ActiveRecord-style validations on the size of the file assigned. The
143
+ # possible options are:
144
+ # * +in+: a Range of bytes (i.e. +1..1.megabyte+),
145
+ # * +less_than+: equivalent to :in => 0..options[:less_than]
146
+ # * +greater_than+: equivalent to :in => options[:greater_than]..Infinity
147
+ def validates_attachment_size name, options = {}
148
+ attachment_definitions[name][:validations] << lambda do |attachment, instance|
149
+ unless options[:greater_than].nil?
150
+ options[:in] = (options[:greater_than]..(1/0)) # 1/0 => Infinity
151
+ end
152
+ unless options[:less_than].nil?
153
+ options[:in] = (0..options[:less_than])
154
+ end
155
+ unless options[:in].include? instance[:"#{name}_file_size"].to_i
156
+ "file size is not between #{options[:in].first} and #{options[:in].last} bytes."
157
+ end
158
+ end
159
+ end
160
+
161
+ # Places ActiveRecord-style validations on the presence of a file.
162
+ def validates_attachment_presence name
163
+ attachment_definitions[name][:validations] << lambda do |attachment, instance|
164
+ if attachment.file.nil? || !File.exist?(attachment.file.path)
165
+ "must be set."
166
+ end
167
+ end
168
+ end
169
+
170
+ # Returns the attachment definitions defined by each call to has_attached_file.
171
+ def attachment_definitions
172
+ read_inheritable_attribute(:attachment_definitions)
173
+ end
174
+
175
+ end
176
+
177
+ module InstanceMethods #:nodoc:
178
+ def attachment_for name
179
+ @attachments ||= {}
180
+ @attachments[name] ||= Attachment.new(name, self, self.class.attachment_definitions[name])
181
+ end
182
+
183
+ def each_attachment
184
+ self.class.attachment_definitions.each do |name, definition|
185
+ yield(name, attachment_for(name))
186
+ end
187
+ end
188
+
189
+ def save_attached_files
190
+ each_attachment do |name, attachment|
191
+ attachment.send(:save)
192
+ end
193
+ end
194
+
195
+ def destroy_attached_files
196
+ each_attachment do |name, attachment|
197
+ attachment.send(:queue_existing_for_delete)
198
+ attachment.send(:flush_deletes)
199
+ end
200
+ end
201
+ end
202
+
203
+ end
204
+
205
+ # Set it all up.
206
+ if Object.const_defined?("ActiveRecord")
207
+ ActiveRecord::Base.send(:include, Paperclip)
208
+ File.send(:include, Paperclip::Upfile)
209
+ end
@@ -0,0 +1,244 @@
1
+ module Paperclip
2
+ # The Attachment class manages the files for a given attachment. It saves when the model saves,
3
+ # deletes when the model is destroyed, and processes the file upon assignment.
4
+ class Attachment
5
+
6
+ def self.default_options
7
+ @default_options ||= {
8
+ :url => "/:attachment/:id/:style/:basename.:extension",
9
+ :path => ":rails_root/public/:attachment/:id/:style/:basename.:extension",
10
+ :styles => {},
11
+ :default_url => "/:attachment/:style/missing.png",
12
+ :default_style => :original,
13
+ :validations => [],
14
+ :storage => :filesystem
15
+ }
16
+ end
17
+
18
+ attr_reader :name, :instance, :file, :styles, :default_style
19
+
20
+ # Creates an Attachment object. +name+ is the name of the attachment, +instance+ is the
21
+ # ActiveRecord object instance it's attached to, and +options+ is the same as the hash
22
+ # passed to +has_attached_file+.
23
+ def initialize name, instance, options = {}
24
+ @name = name
25
+ @instance = instance
26
+
27
+ options = self.class.default_options.merge(options)
28
+
29
+ @url = options[:url]
30
+ @path = options[:path]
31
+ @styles = options[:styles]
32
+ @default_url = options[:default_url]
33
+ @validations = options[:validations]
34
+ @default_style = options[:default_style]
35
+ @storage = options[:storage]
36
+ @options = options
37
+ @queued_for_delete = []
38
+ @processed_files = {}
39
+ @errors = []
40
+ @file = nil
41
+ @validation_errors = nil
42
+ @dirty = false
43
+
44
+ normalize_style_definition
45
+ initialize_storage
46
+
47
+ if original_filename
48
+ @processed_files = locate_files
49
+ @file = @processed_files[@default_style]
50
+ end
51
+ end
52
+
53
+ # What gets called when you call instance.attachment = File. It clears errors,
54
+ # assigns attributes, processes the file, and runs validations. It also queues up
55
+ # the previous file for deletion, to be flushed away on #save of its host.
56
+ def assign uploaded_file
57
+ return nil unless valid_assignment?(uploaded_file)
58
+
59
+ queue_existing_for_delete
60
+ @errors = []
61
+ @validation_errors = nil
62
+
63
+ return nil if uploaded_file.nil?
64
+
65
+ @file = uploaded_file.to_tempfile
66
+ @instance[:"#{@name}_file_name"] = uploaded_file.original_filename
67
+ @instance[:"#{@name}_content_type"] = uploaded_file.content_type
68
+ @instance[:"#{@name}_file_size"] = uploaded_file.size
69
+
70
+ @dirty = true
71
+
72
+ post_process
73
+ ensure
74
+ validate
75
+ end
76
+
77
+ # Returns the public URL of the attachment, with a given style. Note that this
78
+ # does not necessarily need to point to a file that your web server can access
79
+ # and can point to an action in your app, if you need fine grained security.
80
+ # This is not recommended if you don't need the security, however, for
81
+ # performance reasons.
82
+ def url style = nil
83
+ @file ? interpolate(@url, style) : interpolate(@default_url, style)
84
+ end
85
+
86
+ # Returns the path of the attachment as defined by the :path optionn. If the
87
+ # file is stored in the filesystem the path refers to the path of the file on
88
+ # disk. If the file is stored in S3, the path is the "key" part of th URL,
89
+ # and the :bucket option refers to the S3 bucket.
90
+ def path style = nil #:nodoc:
91
+ interpolate(@path, style)
92
+ end
93
+
94
+ # Alias to +url+
95
+ def to_s style = nil
96
+ url(style)
97
+ end
98
+
99
+ # Returns true if there are any errors on this attachment.
100
+ def valid?
101
+ errors.length == 0
102
+ end
103
+
104
+ # Returns an array containing the errors on this attachment.
105
+ def errors
106
+ @errors.compact.uniq
107
+ end
108
+
109
+ # Returns true if there are changes that need to be saved.
110
+ def dirty?
111
+ @dirty
112
+ end
113
+
114
+ # Saves the file, if there are no errors. If there are, it flushes them to
115
+ # the instance's errors and returns false, cancelling the save.
116
+ def save
117
+ if valid?
118
+ flush_deletes
119
+ flush_writes
120
+ @dirty = false
121
+ @file = @processed_files[default_style]
122
+ true
123
+ else
124
+ flush_errors
125
+ false
126
+ end
127
+ end
128
+
129
+ # Returns representation of the data of the file assigned to the given
130
+ # style, in the format most representative of the current storage.
131
+ def to_file style = nil
132
+ @processed_files[style || default_style]
133
+ end
134
+
135
+ alias_method :to_io, :to_file
136
+
137
+ # Returns the name of the file as originally assigned, and as lives in the
138
+ # <attachment>_file_name attribute of the model.
139
+ def original_filename
140
+ instance[:"#{name}_file_name"]
141
+ end
142
+
143
+ # A hash of procs that are run during the interpolation of a path or url.
144
+ # A variable of the format :name will be replaced with the return value of
145
+ # the proc named ":name". Each lambda takes the attachment and the current
146
+ # style as arguments. This hash can be added to with your own proc if
147
+ # necessary.
148
+ def self.interpolations
149
+ @interpolations ||= {
150
+ :rails_root => lambda{|attachment,style| RAILS_ROOT },
151
+ :class => lambda do |attachment,style|
152
+ attachment.instance.class.to_s.downcase.pluralize
153
+ end,
154
+ :basename => lambda do |attachment,style|
155
+ attachment.original_filename.gsub(/\.(.*?)$/, "")
156
+ end,
157
+ :extension => lambda do |attachment,style|
158
+ ((style = attachment.styles[style]) && style.last) ||
159
+ File.extname(attachment.original_filename).gsub(/^\.+/, "")
160
+ end,
161
+ :id => lambda{|attachment,style| attachment.instance.id },
162
+ :id_partition => lambda do |attachment, style|
163
+ ("%09d" % attachment.instance.id).scan(/\d{3}/).join("/")
164
+ end,
165
+ :attachment => lambda{|attachment,style| attachment.name.to_s.downcase.pluralize },
166
+ :style => lambda{|attachment,style| style || attachment.default_style },
167
+ }
168
+ end
169
+
170
+ private
171
+
172
+ def valid_assignment? file #:nodoc:
173
+ file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
174
+ end
175
+
176
+ def validate #:nodoc:
177
+ unless @validation_errors
178
+ @validation_errors = @validations.collect do |v|
179
+ v.call(self, instance)
180
+ end.flatten.compact.uniq
181
+ @errors += @validation_errors
182
+ end
183
+ end
184
+
185
+ def normalize_style_definition
186
+ @styles.each do |name, args|
187
+ dimensions, format = [args, nil].flatten[0..1]
188
+ format = nil if format == ""
189
+ @styles[name] = [dimensions, format]
190
+ end
191
+ end
192
+
193
+ def initialize_storage
194
+ @storage_module = Paperclip::Storage.const_get(@storage.to_s.capitalize)
195
+ self.extend(@storage_module)
196
+ end
197
+
198
+ def post_process #:nodoc:
199
+ return nil if @file.nil?
200
+ @styles.each do |name, args|
201
+ begin
202
+ dimensions, format = args
203
+ @processed_files[name] = Thumbnail.make(self.file,
204
+ dimensions,
205
+ format,
206
+ @whiny_thumnails)
207
+ rescue Errno::ENOENT => e
208
+ @errors << "could not be processed because the file does not exist."
209
+ rescue PaperclipError => e
210
+ @errors << e.message
211
+ end
212
+ end
213
+ @processed_files[:original] = @file
214
+ end
215
+
216
+ def interpolate pattern, style = nil #:nodoc:
217
+ style ||= default_style
218
+ pattern = pattern.dup
219
+ self.class.interpolations.each do |tag, l|
220
+ pattern.gsub!(/:\b#{tag}\b/) do |match|
221
+ l.call( self, style )
222
+ end
223
+ end
224
+ pattern
225
+ end
226
+
227
+ def queue_existing_for_delete #:nodoc:
228
+ @queued_for_delete += @processed_files.values
229
+ @file = nil
230
+ @processed_files = {}
231
+ @instance[:"#{@name}_file_name"] = nil
232
+ @instance[:"#{@name}_content_type"] = nil
233
+ @instance[:"#{@name}_file_size"] = nil
234
+ end
235
+
236
+ def flush_errors #:nodoc:
237
+ @errors.each do |error|
238
+ instance.errors.add(name, error)
239
+ end
240
+ end
241
+
242
+ end
243
+ end
244
+