dm-paperclip-r3 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
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