dm-paperclip-r3 2.4.1

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/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.rdoc ADDED
@@ -0,0 +1,118 @@
1
+ =DataMapper Paperclip R3
2
+
3
+ "DM-Paperclip R3" is based on Ken Robertson's wonderful DM-Paperclip and includes patches for compatability with rails > 3.0.0.
4
+
5
+ DM-Paperclip is a port of Thoughtbot's Paperclip plugin to work with DataMapper. This plugin is fully compatible with
6
+ the original ActiveRecord-oriented Paperclip. You could take an existing ActiveRecord database and use it with DataMapper.
7
+ The module also includes updates validation handling and automatic including of the necessary 'property' fields into
8
+ your model.
9
+
10
+ To use it within your models, you need to ensure the three database fields are included. They are {name}_file_name,
11
+ {name}_content_type, and {name}_file_size. The first two are strings, the final _file_size column is an integer. So
12
+ if your user model has an avatar field, then you would add avatar_file_name, avatar_content_type, and avatar_file_size.
13
+
14
+ As with the original Paperclip plugin, it allows processing of thumbnails at the time the record is saved though ImageMagick.
15
+ It processes the thumbnails through the command-line applications instead of using RMagick.
16
+
17
+ See the documentation for the +has_attached_file+ method for options.
18
+
19
+ ==Code
20
+
21
+ The code DM-Paperclip-R3 is available at Github:
22
+
23
+ git clone git://github.com/joelwreed/dm-paperclip.git
24
+
25
+ It is regularly updated to keep in sync with the latest from Thoughtbot.
26
+
27
+ Releases are tagged within the repository and versioned the same as the original model. You can also get the latest release
28
+ packaged as a gem through Rubyforge:
29
+
30
+ sudo gem install dm-paperclip-r3
31
+
32
+ ==Usage
33
+
34
+ In your model:
35
+
36
+ class User
37
+ include DataMapper::Resource
38
+ include Paperclip::Resource
39
+ property :id, Serial
40
+ property :username, String
41
+ has_attached_file :avatar,
42
+ :styles => { :medium => "300x300>",
43
+ :thumb => "100x100>" }
44
+ end
45
+
46
+ You will need to add an initializer to configure Paperclip. If on Rails, can add a config/initializers/paperclip.rb, on Merb
47
+ can use config/init.rb and add it to the Merb::BootLoader.after_app_loads section. Can also use environment configs, rackup
48
+ file, Rake task, wherever.
49
+
50
+ Paperclip.configure do |config|
51
+ config.root = Rails.root # the application root to anchor relative urls (defaults to Dir.pwd)
52
+ config.env = Rails.env # server env support, defaults to ENV['RACK_ENV'] or 'development'
53
+ config.use_dm_validations = true # validate attachment sizes and such, defaults to false
54
+ config.processors_path = 'lib/pc' # relative path to look for processors, defaults to 'lib/paperclip_processors'
55
+ end
56
+
57
+ Your database will need to add four columns, avatar_file_name (varchar), avatar_content_type (varchar), and
58
+ avatar_file_size (integer), and avatar_updated_at (datetime). You can either add these manually, auto-
59
+ migrate, or use the following migration:
60
+
61
+ migration( 1, :add_user_paperclip_fields ) do
62
+ up do
63
+ modify_table :users do
64
+ add_column :avatar_file_name, "varchar(255)"
65
+ add_column :avatar_content_type, "varchar(255)"
66
+ add_column :avatar_file_size, "integer"
67
+ add_column :avatar_updated_at, "datetime"
68
+ end
69
+ end
70
+ down do
71
+ modify_table :users do
72
+ drop_columns :avatar_file_name, :avatar_content_type, :avatar_file_size, :avatar_updated_at
73
+ end
74
+ end
75
+ end
76
+
77
+ In your edit and new views:
78
+
79
+ <% form_for @user, { :action => url(:user), :multipart => true } do %>
80
+ <%= file_field :name => 'avatar' %>
81
+ <% end %>
82
+
83
+ In your controller:
84
+
85
+ def create
86
+ ...
87
+ @user.avatar = params[:avatar]
88
+ end
89
+
90
+ In your show view:
91
+
92
+ <%= image_tag @user.avatar.url %>
93
+ <%= image_tag @user.avatar.url(:medium) %>
94
+ <%= image_tag @user.avatar.url(:thumb) %>
95
+
96
+ The following validations are available:
97
+
98
+ validates_attachment_presence :avatar
99
+ validates_attachment_content_type :avatar, :content_type => "image/png"
100
+ validates_attachment_size :avatar, :in => 1..10240
101
+ validates_attachment_thumbnails :avatar
102
+
103
+ In order to use validations, you must have loaded the 'dm-validations' gem into your app
104
+ (available as a part of dm-more). If the gem isn't loaded before DM-Paperclip is loaded,
105
+ the validation methods will be excluded. You will also need to include DataMapper::Validate
106
+ into your mode:
107
+
108
+ class User
109
+ include DataMapper::Resource
110
+ include DataMapper::Validate
111
+ include Paperclip::Resource
112
+ property :id, Serial
113
+ property :username, String
114
+ has_attached_file :avatar,
115
+ :styles => { :medium => "300x300>",
116
+ :thumb => "100x100>" }
117
+ validates_attachment_size :avatar, :in => 1..5120
118
+ end
data/Rakefile ADDED
@@ -0,0 +1,104 @@
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 'dm-core'
8
+ require 'dm-validations'
9
+ require 'dm-paperclip-r3'
10
+
11
+ desc 'Default: run unit tests.'
12
+ task :default => [:clean, :test]
13
+
14
+ # Test tasks
15
+ desc 'Test the DM-Paperclip-R3 library.'
16
+ Rake::TestTask.new(:test) do |t|
17
+ t.libs << 'dm-paperclip-r3'
18
+ t.pattern = 'test/**/*_test.rb'
19
+ t.verbose = true
20
+ end
21
+
22
+ # Console
23
+ desc "Open an irb session preloaded with this library"
24
+ task :console do
25
+ sh "irb -rubygems -r dm-validations -r dm-migrations -r ./lib/dm-paperclip.rb"
26
+ end
27
+
28
+ # Rdoc
29
+ desc 'Generate documentation for the paperclip plugin.'
30
+ Rake::RDocTask.new(:doc) do |rdoc|
31
+ rdoc.rdoc_dir = 'doc'
32
+ rdoc.title = 'DM-Paperclip-R3'
33
+ rdoc.options << '--line-numbers' << '--inline-source'
34
+ rdoc.rdoc_files.include('README.rdoc')
35
+ rdoc.rdoc_files.include('lib/**/*.rb')
36
+ end
37
+
38
+ # Code coverage
39
+ task :coverage do
40
+ system("rm -fr coverage")
41
+ system("rcov test/test_*.rb")
42
+ system("open coverage/index.html")
43
+ end
44
+
45
+ # Clean house
46
+ desc 'Clean up files.'
47
+ task :clean do |t|
48
+ FileUtils.rm_rf "doc"
49
+ FileUtils.rm_rf "coverage"
50
+ FileUtils.rm_rf "tmp"
51
+ FileUtils.rm_rf "pkg"
52
+ FileUtils.rm_rf "log"
53
+ end
54
+
55
+ spec = Gem::Specification.new do |s|
56
+ s.name = "dm-paperclip-r3"
57
+ s.version = Paperclip::VERSION
58
+ s.author = "Joel Reed"
59
+ s.email = "joelwreed@gmail.com"
60
+ s.homepage = "https://github.com/joelwreed/dm-paperclip"
61
+ s.platform = Gem::Platform::RUBY
62
+ s.summary = "File attachments as attributes for DataMapper, based on the original Paperclip by Jon Yurek at Thoughtbot"
63
+ s.files = FileList["README.rdoc",
64
+ "LICENSE",
65
+ "Rakefile",
66
+ "init.rb",
67
+ "{lib,tasks,test}/**/*"].to_a
68
+ s.require_path = "lib"
69
+ s.test_files = FileList["test/**/test_*.rb"].to_a
70
+ s.has_rdoc = true
71
+ s.extra_rdoc_files = ["README.rdoc"]
72
+ s.rdoc_options << '--line-numbers' << '--inline-source'
73
+ s.requirements << "ImageMagick"
74
+ s.requirements << "data_mapper"
75
+ end
76
+
77
+ Rake::GemPackageTask.new(spec) do |pkg|
78
+ pkg.need_tar = true
79
+ end
80
+
81
+ desc 'Generate gemspec'
82
+ task :gemspec do
83
+ File.open("#{spec.name}.gemspec", 'w') { |f| f.puts(spec.to_ruby) }
84
+ end
85
+
86
+ WIN32 = (PLATFORM =~ /win32|cygwin/) rescue nil
87
+ SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
88
+
89
+ desc "Install #{spec.name} #{spec.version}"
90
+ task :install => [ :package ] do
91
+ sh "#{SUDO} gem install pkg/#{spec.name}-#{spec.version} --no-update-sources", :verbose => false
92
+ end
93
+
94
+ desc "Release new version"
95
+ task :release => [:test, :gem] do
96
+ require 'rubygems'
97
+ require 'rubyforge'
98
+ r = RubyForge.new
99
+ r.login
100
+ r.add_release spec.rubyforge_project,
101
+ spec.name,
102
+ spec.version,
103
+ File.join("pkg", "#{spec.name}-#{spec.version}.gem")
104
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'lib', 'dm-paperclip')
@@ -0,0 +1,416 @@
1
+ module Paperclip
2
+ # The Attachment class manages the files for a given attachment. It saves
3
+ # when the model saves, deletes when the model is destroyed, and processes
4
+ # the file upon assignment.
5
+ class Attachment
6
+ include IOStream
7
+
8
+ def self.default_options
9
+ @default_options ||= {
10
+ :url => "/system/:attachment/:id/:style/:filename",
11
+ :path => ":rails_root/public:url",
12
+ :styles => {},
13
+ :default_url => "/:attachment/:style/missing.png",
14
+ :default_style => :original,
15
+ :validations => [],
16
+ :storage => :filesystem,
17
+ :whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails]
18
+ }
19
+ end
20
+
21
+ attr_reader :name, :instance, :styles, :default_style, :convert_options, :queued_for_write, :options
22
+
23
+ # Creates an Attachment object. +name+ is the name of the attachment,
24
+ # +instance+ is the ActiveRecord object instance it's attached to, and
25
+ # +options+ is the same as the hash passed to +has_attached_file+.
26
+ def initialize name, instance, options = {}
27
+ @name = name
28
+ @instance = instance
29
+
30
+ options = self.class.default_options.merge(options)
31
+
32
+ @url = options[:url]
33
+ @url = @url.call(self) if @url.is_a?(Proc)
34
+ @path = options[:path]
35
+ @path = @path.call(self) if @path.is_a?(Proc)
36
+ @styles = options[:styles]
37
+ @styles = @styles.call(self) if @styles.is_a?(Proc)
38
+ @default_url = options[:default_url]
39
+ @validations = options[:validations]
40
+ @default_style = options[:default_style]
41
+ @storage = options[:storage]
42
+ @whiny = options[:whiny_thumbnails] || options[:whiny]
43
+ @convert_options = options[:convert_options] || {}
44
+ @processors = options[:processors] || [:thumbnail]
45
+ @options = options
46
+ @queued_for_delete = []
47
+ @queued_for_write = {}
48
+ @errors = {}
49
+ @validation_errors = nil
50
+ @dirty = false
51
+
52
+ normalize_style_definition
53
+ initialize_storage
54
+ end
55
+
56
+ # What gets called when you call instance.attachment = File. It clears
57
+ # errors, assigns attributes, processes the file, and runs validations. It
58
+ # also queues up the previous file for deletion, to be flushed away on
59
+ # #save of its host. In addition to form uploads, you can also assign
60
+ # another Paperclip attachment:
61
+ # new_user.avatar = old_user.avatar
62
+ # If the file that is assigned is not valid, the processing (i.e.
63
+ # thumbnailing, etc) will NOT be run.
64
+ def assign uploaded_file
65
+
66
+ ensure_required_accessors!
67
+
68
+ if uploaded_file.is_a?(Paperclip::Attachment)
69
+ uploaded_file = uploaded_file.to_file(:original)
70
+ close_uploaded_file = uploaded_file.respond_to?(:close)
71
+ end
72
+
73
+ return nil unless valid_assignment?(uploaded_file)
74
+
75
+ uploaded_file.binmode if uploaded_file.respond_to? :binmode
76
+ self.clear
77
+
78
+ return nil if uploaded_file.nil?
79
+
80
+ if uploaded_file.respond_to?(:[])
81
+ uploaded_file = uploaded_file.to_mash
82
+
83
+ @queued_for_write[:original] = to_tempfile(uploaded_file)
84
+ instance_write(:file_name, uploaded_file['filename'].strip.gsub(/[^\w\d\.\-]+/, '_'))
85
+ instance_write(:content_type, uploaded_file['content_type'] ? uploaded_file['content_type'].strip : uploaded_file['tempfile'].content_type.to_s.strip)
86
+ instance_write(:file_size, uploaded_file['size'] ? uploaded_file['size'].to_i : uploaded_file['tempfile'].size.to_i)
87
+ instance_write(:updated_at, Time.now)
88
+ else
89
+ @queued_for_write[:original] = uploaded_file.to_tempfile
90
+ instance_write(:file_name, uploaded_file.original_filename.strip.gsub(/[^\w\d\.\-]+/, '_'))
91
+ instance_write(:content_type, uploaded_file.content_type.to_s.strip)
92
+ instance_write(:file_size, uploaded_file.size.to_i)
93
+ instance_write(:updated_at, Time.now)
94
+ end
95
+
96
+ @dirty = true
97
+
98
+ post_process if valid?
99
+
100
+ # Reset the file size if the original file was reprocessed.
101
+ instance_write(:file_size, @queued_for_write[:original].size.to_i)
102
+
103
+ ensure
104
+ uploaded_file.close if close_uploaded_file
105
+ validate
106
+ end
107
+
108
+ # Returns the public URL of the attachment, with a given style. Note that
109
+ # this does not necessarily need to point to a file that your web server
110
+ # can access and can point to an action in your app, if you need fine
111
+ # grained security. This is not recommended if you don't need the
112
+ # security, however, for performance reasons. set
113
+ # include_updated_timestamp to false if you want to stop the attachment
114
+ # update time appended to the url
115
+ def url style = default_style, include_updated_timestamp = true
116
+ the_url = original_filename.nil? ? interpolate(@default_url, style) : interpolate(@url, style)
117
+ include_updated_timestamp && updated_at ? [the_url, updated_at.to_time.to_i].compact.join(the_url.include?("?") ? "&" : "?") : the_url
118
+ end
119
+
120
+ # Returns the path of the attachment as defined by the :path option. If the
121
+ # file is stored in the filesystem the path refers to the path of the file
122
+ # on disk. If the file is stored in S3, the path is the "key" part of the
123
+ # URL, and the :bucket option refers to the S3 bucket.
124
+ def path style = default_style
125
+ original_filename.nil? ? nil : interpolate(@path, style)
126
+ end
127
+
128
+ # Alias to +url+
129
+ def to_s style = nil
130
+ url(style)
131
+ end
132
+
133
+ # Returns true if there are no errors on this attachment.
134
+ def valid?
135
+ validate
136
+ errors.empty?
137
+ end
138
+
139
+ # Returns an array containing the errors on this attachment.
140
+ def errors
141
+ @errors
142
+ end
143
+
144
+ # Returns true if there are changes that need to be saved.
145
+ def dirty?
146
+ @dirty
147
+ end
148
+
149
+ # Saves the file, if there are no errors. If there are, it flushes them to
150
+ # the instance's errors and returns false, cancelling the save.
151
+ def save
152
+ if valid?
153
+ flush_deletes
154
+ flush_writes
155
+ @dirty = false
156
+ true
157
+ else
158
+ flush_errors
159
+ false
160
+ end
161
+ end
162
+
163
+ # Clears out the attachment. Has the same effect as previously assigning
164
+ # nil to the attachment. Does NOT save. If you wish to clear AND save,
165
+ # use #destroy.
166
+ def clear
167
+ queue_existing_for_delete
168
+ @errors = {}
169
+ @validation_errors = nil
170
+ end
171
+
172
+ # Destroys the attachment. Has the same effect as previously assigning
173
+ # nil to the attachment *and saving*. This is permanent. If you wish to
174
+ # wipe out the existing attachment but not save, use #clear.
175
+ def destroy
176
+ clear
177
+ save
178
+ end
179
+
180
+ # Returns the name of the file as originally assigned, and lives in the
181
+ # <attachment>_file_name attribute of the model.
182
+ def original_filename
183
+ instance_read(:file_name)
184
+ end
185
+
186
+ # Returns the size of the file as originally assigned, and lives in the
187
+ # <attachment>_file_size attribute of the model.
188
+ def size
189
+ instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
190
+ end
191
+
192
+ # Returns the content_type of the file as originally assigned, and lives
193
+ # in the <attachment>_content_type attribute of the model.
194
+ def content_type
195
+ instance_read(:content_type)
196
+ end
197
+
198
+ # Returns the last modified time of the file as originally assigned, and
199
+ # lives in the <attachment>_updated_at attribute of the model.
200
+ def updated_at
201
+ instance_read(:updated_at)
202
+ end
203
+
204
+ # Paths and URLs can have a number of variables interpolated into them
205
+ # to vary the storage location based on name, id, style, class, etc.
206
+ # This method is a deprecated access into supplying and retrieving these
207
+ # interpolations. Future access should use either Paperclip.interpolates
208
+ # or extend the Paperclip::Interpolations module directly.
209
+ def self.interpolations
210
+ warn('[DEPRECATION] Paperclip::Attachment.interpolations is deprecated ' +
211
+ 'and will be removed from future versions. ' +
212
+ 'Use Paperclip.interpolates instead')
213
+ Paperclip::Interpolations
214
+ end
215
+
216
+ # This method really shouldn't be called that often. It's expected use is
217
+ # in the paperclip:refresh rake task and that's it. It will regenerate all
218
+ # thumbnails forcefully, by reobtaining the original file and going through
219
+ # the post-process again.
220
+ def reprocess!
221
+ new_original = Tempfile.new("paperclip-reprocess")
222
+ new_original.binmode
223
+ if old_original = to_file(:original)
224
+ new_original.write( old_original.read )
225
+ new_original.rewind
226
+
227
+ @queued_for_write = { :original => new_original }
228
+ post_process
229
+
230
+ old_original.close if old_original.respond_to?(:close)
231
+
232
+ save
233
+ else
234
+ true
235
+ end
236
+ end
237
+
238
+ # Returns true if a file has been assigned.
239
+ def file?
240
+ !original_filename.blank?
241
+ end
242
+
243
+ # Writes the attachment-specific attribute on the instance. For example,
244
+ # instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance's
245
+ # "avatar_file_name" field (assuming the attachment is called avatar).
246
+ def instance_write(attr, value)
247
+ setter = :"#{name}_#{attr}="
248
+ responds = instance.respond_to?(setter)
249
+ self.instance_variable_set("@_#{setter.to_s.chop}", value)
250
+ instance.send(setter, value) if responds || attr.to_s == "file_name"
251
+ end
252
+
253
+ # Reads the attachment-specific attribute on the instance. See instance_write
254
+ # for more details.
255
+ def instance_read(attr)
256
+ getter = :"#{name}_#{attr}"
257
+ responds = instance.respond_to?(getter)
258
+ cached = self.instance_variable_get("@_#{getter}")
259
+ return cached if cached
260
+ instance.send(getter) if responds || attr.to_s == "file_name"
261
+ end
262
+
263
+ private
264
+
265
+ def ensure_required_accessors! #:nodoc:
266
+ %w(file_name).each do |field|
267
+ unless @instance.respond_to?("#{name}_#{field}") && @instance.respond_to?("#{name}_#{field}=")
268
+ raise PaperclipError.new("#{@instance.class} model missing required attr_accessor for '#{name}_#{field}'")
269
+ end
270
+ end
271
+ end
272
+
273
+ def log message #:nodoc:
274
+ Paperclip.log(message)
275
+ end
276
+
277
+ def valid_assignment? file #:nodoc:
278
+ if file.is_a?(Hash) || (defined?(Mash) && file.is_a?(Mash))
279
+ file[:filename] || file['filename']
280
+ else
281
+ file.nil? || (file.respond_to?(:original_filename) && file.respond_to?(:content_type))
282
+ end
283
+ end
284
+
285
+ def validate #:nodoc:
286
+ unless @validation_errors
287
+ @validation_errors = @validations.inject({}) do |errors, validation|
288
+ name, options = validation
289
+ errors[name] = send(:"validate_#{name}", options) if allow_validation?(options)
290
+ errors
291
+ end
292
+ @validation_errors.reject!{|k,v| v == nil }
293
+ @errors.merge!(@validation_errors)
294
+ end
295
+ @validation_errors
296
+ end
297
+
298
+ def allow_validation? options #:nodoc:
299
+ (options[:if].nil? || check_guard(options[:if])) && (options[:unless].nil? || !check_guard(options[:unless]))
300
+ end
301
+
302
+ def check_guard guard #:nodoc:
303
+ if guard.respond_to? :call
304
+ guard.call(instance)
305
+ elsif ! guard.blank?
306
+ instance.send(guard.to_s)
307
+ end
308
+ end
309
+
310
+ def validate_size options #:nodoc:
311
+ if file? && !options[:range].include?(size.to_i)
312
+ options[:message].gsub(/:min/, options[:min].to_s).gsub(/:max/, options[:max].to_s)
313
+ end
314
+ end
315
+
316
+ def validate_presence options #:nodoc:
317
+ options[:message] unless file?
318
+ end
319
+
320
+ def validate_content_type options #:nodoc:
321
+ valid_types = [options[:content_type]].flatten
322
+ unless original_filename.blank?
323
+ unless valid_types.blank?
324
+ content_type = instance_read(:content_type)
325
+ unless valid_types.any?{|t| content_type.nil? || t === content_type }
326
+ options[:message] || "is not one of the allowed file types."
327
+ end
328
+ end
329
+ end
330
+ end
331
+
332
+ def normalize_style_definition #:nodoc:
333
+ @styles.each do |name, args|
334
+ unless args.is_a? Hash
335
+ dimensions, format = [args, nil].flatten[0..1]
336
+ format = nil if format.blank?
337
+ @styles[name] = {
338
+ :processors => @processors,
339
+ :geometry => dimensions,
340
+ :format => format,
341
+ :whiny => @whiny,
342
+ :convert_options => extra_options_for(name)
343
+ }
344
+ else
345
+ @styles[name] = {
346
+ :processors => @processors,
347
+ :whiny => @whiny,
348
+ :convert_options => extra_options_for(name)
349
+ }.merge(@styles[name])
350
+ end
351
+ end
352
+ end
353
+
354
+ def solidify_style_definitions #:nodoc:
355
+ @styles.each do |name, args|
356
+ @styles[name][:geometry] = @styles[name][:geometry].call(instance) if @styles[name][:geometry].respond_to?(:call)
357
+ @styles[name][:processors] = @styles[name][:processors].call(instance) if @styles[name][:processors].respond_to?(:call)
358
+ end
359
+ end
360
+
361
+ def initialize_storage #:nodoc:
362
+ @storage_module = Paperclip::Storage.const_get(@storage.to_s.capitalize)
363
+ self.extend(@storage_module)
364
+ end
365
+
366
+ def extra_options_for(style) #:nodoc:
367
+ all_options = convert_options[:all]
368
+ all_options = all_options.call(instance) if all_options.respond_to?(:call)
369
+ style_options = convert_options[style]
370
+ style_options = style_options.call(instance) if style_options.respond_to?(:call)
371
+
372
+ [ style_options, all_options ].compact.join(" ")
373
+ end
374
+
375
+ def post_process #:nodoc:
376
+ return if @queued_for_write[:original].nil?
377
+ solidify_style_definitions
378
+ post_process_styles
379
+ end
380
+
381
+ def post_process_styles #:nodoc:
382
+ @styles.each do |name, args|
383
+ begin
384
+ raise RuntimeError.new("Style #{name} has no processors defined.") if args[:processors].blank?
385
+ @queued_for_write[name] = args[:processors].inject(@queued_for_write[:original]) do |file, processor|
386
+ Paperclip.processor(processor).make(file, args, self)
387
+ end
388
+ rescue PaperclipError => e
389
+ log("An error was received while processing: #{e.inspect}")
390
+ (@errors[:processing] ||= []) << e.message if @whiny
391
+ end
392
+ end
393
+ end
394
+
395
+ def interpolate pattern, style = default_style #:nodoc:
396
+ Paperclip::Interpolations.interpolate(pattern, self, style)
397
+ end
398
+
399
+ def queue_existing_for_delete #:nodoc:
400
+ return unless file?
401
+ @queued_for_delete += [:original, *@styles.keys].uniq.map do |style|
402
+ path(style) if exists?(style)
403
+ end.compact
404
+ instance_write(:file_name, nil)
405
+ instance_write(:content_type, nil)
406
+ instance_write(:file_size, nil)
407
+ instance_write(:updated_at, nil)
408
+ end
409
+
410
+ def flush_errors #:nodoc:
411
+ @errors.each do |error, message|
412
+ [message].flatten.each {|m| instance.errors.add(name, m) }
413
+ end
414
+ end
415
+ end
416
+ end
@@ -0,0 +1,33 @@
1
+ module Paperclip
2
+ # This module is intended as a compatability shim for the differences in
3
+ # callbacks between Rails 2.0 and Rails 2.1.
4
+ module CallbackCompatability
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ base.send(:include, InstanceMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ # The implementation of this method is taken from the Rails 1.2.6 source,
12
+ # from rails/activerecord/lib/active_record/callbacks.rb, line 192.
13
+ def define_callbacks(*args)
14
+ args.each do |method|
15
+ self.class_eval <<-"end_eval"
16
+ def self.#{method}(*callbacks, &block)
17
+ callbacks << block if block_given?
18
+ write_inheritable_array(#{method.to_sym.inspect}, callbacks)
19
+ end
20
+ end_eval
21
+ end
22
+ end
23
+ end
24
+
25
+ module InstanceMethods
26
+ # The callbacks in < 2.1 don't worry about the extra options or the
27
+ # block, so just run what we have available.
28
+ def run_callbacks(meth, opts = nil, &blk)
29
+ callback(meth)
30
+ end
31
+ end
32
+ end
33
+ end