paperclip-cloudfiles 2.3.2 → 2.3.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +10 -3
- data/Rakefile +8 -4
- data/generators/paperclip/USAGE +2 -2
- data/generators/paperclip/paperclip_generator.rb +8 -8
- data/lib/generators/paperclip/USAGE +8 -0
- data/lib/generators/paperclip/paperclip_generator.rb +31 -0
- data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +19 -0
- data/lib/paperclip/attachment.rb +38 -18
- data/lib/paperclip/command_line.rb +80 -0
- data/lib/paperclip/geometry.rb +7 -7
- data/lib/paperclip/interpolations.rb +8 -2
- data/lib/paperclip/iostream.rb +11 -25
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +3 -3
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +0 -1
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +0 -1
- data/lib/paperclip/processor.rb +15 -6
- data/lib/paperclip/railtie.rb +24 -0
- data/lib/paperclip/storage/cloud_files.rb +131 -0
- data/lib/paperclip/storage/filesystem.rb +73 -0
- data/lib/paperclip/storage/s3.rb +192 -0
- data/lib/paperclip/storage.rb +3 -371
- data/lib/paperclip/style.rb +11 -11
- data/lib/paperclip/thumbnail.rb +16 -15
- data/lib/paperclip/upfile.rb +5 -3
- data/lib/paperclip/version.rb +3 -0
- data/lib/paperclip.rb +78 -92
- data/lib/tasks/paperclip.rake +72 -0
- data/rails/init.rb +2 -0
- data/shoulda_macros/paperclip.rb +1 -2
- data/test/attachment_test.rb +74 -28
- data/test/command_line_test.rb +133 -0
- data/test/geometry_test.rb +2 -2
- data/test/helper.rb +22 -24
- data/test/integration_test.rb +10 -11
- data/test/interpolations_test.rb +7 -4
- data/test/iostream_test.rb +6 -13
- data/test/matchers/have_attached_file_matcher_test.rb +1 -1
- data/test/matchers/validate_attachment_content_type_matcher_test.rb +11 -1
- data/test/matchers/validate_attachment_presence_matcher_test.rb +1 -1
- data/test/matchers/validate_attachment_size_matcher_test.rb +1 -1
- data/test/paperclip_test.rb +54 -80
- data/test/processor_test.rb +1 -1
- data/test/storage_test.rb +32 -12
- data/test/style_test.rb +17 -17
- data/test/thumbnail_test.rb +18 -18
- data/test/upfile_test.rb +1 -1
- metadata +58 -31
- data/tasks/paperclip_tasks.rake +0 -79
data/README.rdoc
CHANGED
@@ -16,12 +16,18 @@ See the documentation for +has_attached_file+ in Paperclip::ClassMethods for
|
|
16
16
|
more detailed options.
|
17
17
|
|
18
18
|
This fork has support for Rackspace Cloud Files. It requires the "cloudfiles"
|
19
|
-
gem, >= 1.4.
|
19
|
+
gem, >= 1.4.9, from Gemcutter.org. The Thoughtbot guys have indicated that they
|
20
20
|
don't want to pull any code into the official Paperclip mainline that they don't
|
21
21
|
personally use on projects, so until they discover the joy of Cloud Files, this
|
22
22
|
fork is available as the {paperclip-cloudfiles gem}[http://gemcutter.org/gems/paperclip-cloudfiles]
|
23
23
|
on Gemcutter's gem server.
|
24
24
|
|
25
|
+
==Installation
|
26
|
+
|
27
|
+
Include the gem in your Gemfile:
|
28
|
+
|
29
|
+
gem "paperclip-cloudfiles", "~> 2.3"
|
30
|
+
|
25
31
|
==Quick Start
|
26
32
|
|
27
33
|
In your environment.rb:
|
@@ -150,7 +156,7 @@ For example, assuming we had this definition:
|
|
150
156
|
has_attached_file :scan, :styles => { :text => { :quality => :better } },
|
151
157
|
:processors => [:rotator, :ocr]
|
152
158
|
|
153
|
-
then both the :rotator processor and the :ocr processor would receive the
|
159
|
+
then both the :rotator processor and the :ocr processor would receive the
|
154
160
|
options "{ :quality => :better }". This parameter may not mean anything to one
|
155
161
|
or more or the processors, and they are expected to ignore it.
|
156
162
|
|
@@ -185,7 +191,8 @@ If you'd like to contribute a feature or bugfix: Thanks! To make sure your
|
|
185
191
|
fix/feature has a high chance of being included, please read the following
|
186
192
|
guidelines:
|
187
193
|
|
188
|
-
1. Ask on the mailing list, or
|
194
|
+
1. Ask on the mailing list[http://groups.google.com/group/paperclip-plugin], or
|
195
|
+
post a new GitHub Issue[http://github.com/thoughtbot/paperclip/issues].
|
189
196
|
2. Make sure there are tests! We will not accept any patch that is not tested.
|
190
197
|
It's a rare time when explicit tests aren't needed. If you have questions
|
191
198
|
about writing tests for paperclip, please ask the mailing list.
|
data/Rakefile
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'appraisal'
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
1
5
|
require 'rake'
|
2
6
|
require 'rake/testtask'
|
3
7
|
require 'rake/rdoctask'
|
@@ -6,11 +10,11 @@ $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
|
|
6
10
|
require 'paperclip'
|
7
11
|
|
8
12
|
desc 'Default: run unit tests.'
|
9
|
-
task :default => [:clean, :
|
13
|
+
task :default => [:clean, :all]
|
10
14
|
|
11
15
|
desc 'Test the paperclip plugin under all supported Rails versions.'
|
12
16
|
task :all do |t|
|
13
|
-
exec('rake
|
17
|
+
exec('rake appraisal test')
|
14
18
|
end
|
15
19
|
|
16
20
|
desc 'Test the paperclip plugin.'
|
@@ -62,13 +66,13 @@ task :manifest => :clean do
|
|
62
66
|
puts file
|
63
67
|
end
|
64
68
|
end
|
65
|
-
|
69
|
+
|
66
70
|
desc "Generate a gemspec file for GitHub"
|
67
71
|
task :gemspec => :clean do
|
68
72
|
File.open("#{spec.name}.gemspec", 'w') do |f|
|
69
73
|
f.write spec.to_ruby
|
70
74
|
end
|
71
|
-
end
|
75
|
+
end
|
72
76
|
|
73
77
|
desc "Build the gem into the current directory"
|
74
78
|
task :gem => :gemspec do
|
data/generators/paperclip/USAGE
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
class PaperclipGenerator < Rails::Generator::NamedBase
|
2
2
|
attr_accessor :attachments, :migration_name
|
3
|
-
|
3
|
+
|
4
4
|
def initialize(args, options = {})
|
5
5
|
super
|
6
6
|
@class_name, @attachments = args[0], args[1..-1]
|
7
7
|
end
|
8
|
-
|
9
|
-
def manifest
|
8
|
+
|
9
|
+
def manifest
|
10
10
|
file_name = generate_file_name
|
11
11
|
@migration_name = file_name.camelize
|
12
12
|
record do |m|
|
@@ -14,14 +14,14 @@ class PaperclipGenerator < Rails::Generator::NamedBase
|
|
14
14
|
File.join('db', 'migrate'),
|
15
15
|
:migration_file_name => file_name
|
16
16
|
end
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
21
|
def generate_file_name
|
22
22
|
names = attachments.map{|a| a.underscore }
|
23
23
|
names = names[0..-2] + ["and", names[-1]] if names.length > 1
|
24
24
|
"add_attachments_#{names.join("_")}_to_#{@class_name.underscore}"
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
class PaperclipGenerator < ActiveRecord::Generators::Base
|
4
|
+
desc "Create a migration to add paperclip-specific fields to your model."
|
5
|
+
|
6
|
+
argument :attachment_names, :required => true, :type => :array, :desc => "The names of the attachment(s) to add.",
|
7
|
+
:banner => "attachment_one attachment_two attachment_three ..."
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
@source_root ||= File.expand_path('../templates', __FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate_migration
|
14
|
+
migration_template "paperclip_migration.rb.erb", "db/migrate/#{migration_file_name}"
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def migration_name
|
20
|
+
"add_attachment_#{attachment_names.join("_")}_to_#{name.underscore}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def migration_file_name
|
24
|
+
"#{migration_name}.rb"
|
25
|
+
end
|
26
|
+
|
27
|
+
def migration_class_name
|
28
|
+
migration_name.camelize
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
<% attachment_names.each do |attachment| -%>
|
4
|
+
add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_name, :string
|
5
|
+
add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_content_type, :string
|
6
|
+
add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_size, :integer
|
7
|
+
add_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at, :datetime
|
8
|
+
<% end -%>
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.down
|
12
|
+
<% attachment_names.each do |attachment| -%>
|
13
|
+
remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_name
|
14
|
+
remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_content_type
|
15
|
+
remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_file_size
|
16
|
+
remove_column :<%= name.underscore.camelize.tableize %>, :<%= attachment %>_updated_at
|
17
|
+
<% end -%>
|
18
|
+
end
|
19
|
+
end
|
data/lib/paperclip/attachment.rb
CHANGED
@@ -4,7 +4,8 @@ module Paperclip
|
|
4
4
|
# when the model saves, deletes when the model is destroyed, and processes
|
5
5
|
# the file upon assignment.
|
6
6
|
class Attachment
|
7
|
-
|
7
|
+
include IOStream
|
8
|
+
|
8
9
|
def self.default_options
|
9
10
|
@default_options ||= {
|
10
11
|
:url => "/system/:attachment/:id/:style/:filename",
|
@@ -16,6 +17,7 @@ module Paperclip
|
|
16
17
|
:default_style => :original,
|
17
18
|
:validations => [],
|
18
19
|
:storage => :filesystem,
|
20
|
+
:use_timestamp => true,
|
19
21
|
:whiny => Paperclip.options[:whiny] || Paperclip.options[:whiny_thumbnails]
|
20
22
|
}
|
21
23
|
end
|
@@ -41,6 +43,7 @@ module Paperclip
|
|
41
43
|
@validations = options[:validations]
|
42
44
|
@default_style = options[:default_style]
|
43
45
|
@storage = options[:storage]
|
46
|
+
@use_timestamp = options[:use_timestamp]
|
44
47
|
@whiny = options[:whiny_thumbnails] || options[:whiny]
|
45
48
|
@convert_options = options[:convert_options]
|
46
49
|
@processors = options[:processors]
|
@@ -53,17 +56,17 @@ module Paperclip
|
|
53
56
|
|
54
57
|
initialize_storage
|
55
58
|
end
|
56
|
-
|
59
|
+
|
57
60
|
def styles
|
58
61
|
unless @normalized_styles
|
59
62
|
@normalized_styles = {}
|
60
63
|
(@styles.respond_to?(:call) ? @styles.call(self) : @styles).each do |name, args|
|
61
|
-
@normalized_styles[name] = Paperclip::Style.new(name, args, self)
|
64
|
+
@normalized_styles[name] = Paperclip::Style.new(name, args.dup, self)
|
62
65
|
end
|
63
66
|
end
|
64
67
|
@normalized_styles
|
65
68
|
end
|
66
|
-
|
69
|
+
|
67
70
|
def processors
|
68
71
|
@processors.respond_to?(:call) ? @processors.call(instance) : @processors
|
69
72
|
end
|
@@ -72,7 +75,7 @@ module Paperclip
|
|
72
75
|
# errors, assigns attributes, processes the file, and runs validations. It
|
73
76
|
# also queues up the previous file for deletion, to be flushed away on
|
74
77
|
# #save of its host. In addition to form uploads, you can also assign
|
75
|
-
# another Paperclip attachment:
|
78
|
+
# another Paperclip attachment:
|
76
79
|
# new_user.avatar = old_user.avatar
|
77
80
|
# If the file that is assigned is not valid, the processing (i.e.
|
78
81
|
# thumbnailing, etc) will NOT be run.
|
@@ -91,10 +94,11 @@ module Paperclip
|
|
91
94
|
|
92
95
|
return nil if uploaded_file.nil?
|
93
96
|
|
94
|
-
@queued_for_write[:original] = uploaded_file
|
97
|
+
@queued_for_write[:original] = to_tempfile(uploaded_file)
|
95
98
|
instance_write(:file_name, uploaded_file.original_filename.strip)
|
96
99
|
instance_write(:content_type, uploaded_file.content_type.to_s.strip)
|
97
100
|
instance_write(:file_size, uploaded_file.size.to_i)
|
101
|
+
instance_write(:fingerprint, generate_fingerprint(uploaded_file))
|
98
102
|
instance_write(:updated_at, Time.now)
|
99
103
|
|
100
104
|
@dirty = true
|
@@ -102,7 +106,8 @@ module Paperclip
|
|
102
106
|
post_process if valid?
|
103
107
|
|
104
108
|
# Reset the file size if the original file was reprocessed.
|
105
|
-
instance_write(:file_size,
|
109
|
+
instance_write(:file_size, @queued_for_write[:original].size.to_i)
|
110
|
+
instance_write(:fingerprint, generate_fingerprint(@queued_for_write[:original]))
|
106
111
|
ensure
|
107
112
|
uploaded_file.close if close_uploaded_file
|
108
113
|
validate
|
@@ -112,12 +117,11 @@ module Paperclip
|
|
112
117
|
# this does not necessarily need to point to a file that your web server
|
113
118
|
# can access and can point to an action in your app, if you need fine
|
114
119
|
# grained security. This is not recommended if you don't need the
|
115
|
-
# security, however, for performance reasons.
|
116
|
-
#
|
117
|
-
|
118
|
-
def url style_name = default_style, include_updated_timestamp = true
|
120
|
+
# security, however, for performance reasons. Set use_timestamp to false
|
121
|
+
# if you want to stop the attachment update time appended to the url
|
122
|
+
def url(style_name = default_style, use_timestamp = @use_timestamp)
|
119
123
|
url = original_filename.nil? ? interpolate(@default_url, style_name) : interpolate(@url, style_name)
|
120
|
-
|
124
|
+
use_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
|
121
125
|
end
|
122
126
|
|
123
127
|
# Returns the path of the attachment as defined by the :path option. If the
|
@@ -192,19 +196,31 @@ module Paperclip
|
|
192
196
|
instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
|
193
197
|
end
|
194
198
|
|
199
|
+
# Returns the hash of the file as originally assigned, and lives in the
|
200
|
+
# <attachment>_fingerprint attribute of the model.
|
201
|
+
def fingerprint
|
202
|
+
instance_read(:fingerprint) || (@queued_for_write[:original] && generate_fingerprint(@queued_for_write[:original]))
|
203
|
+
end
|
204
|
+
|
195
205
|
# Returns the content_type of the file as originally assigned, and lives
|
196
206
|
# in the <attachment>_content_type attribute of the model.
|
197
207
|
def content_type
|
198
208
|
instance_read(:content_type)
|
199
209
|
end
|
200
|
-
|
201
|
-
# Returns the last modified time of the file as originally assigned, and
|
210
|
+
|
211
|
+
# Returns the last modified time of the file as originally assigned, and
|
202
212
|
# lives in the <attachment>_updated_at attribute of the model.
|
203
213
|
def updated_at
|
204
214
|
time = instance_read(:updated_at)
|
205
215
|
time && time.to_f.to_i
|
206
216
|
end
|
207
217
|
|
218
|
+
def generate_fingerprint(source)
|
219
|
+
data = source.read
|
220
|
+
source.rewind if source.respond_to?(:rewind)
|
221
|
+
Digest::MD5.hexdigest(data)
|
222
|
+
end
|
223
|
+
|
208
224
|
# Paths and URLs can have a number of variables interpolated into them
|
209
225
|
# to vary the storage location based on name, id, style, class, etc.
|
210
226
|
# This method is a deprecated access into supplying and retrieving these
|
@@ -225,7 +241,7 @@ module Paperclip
|
|
225
241
|
new_original = Tempfile.new("paperclip-reprocess")
|
226
242
|
new_original.binmode
|
227
243
|
if old_original = to_file(:original)
|
228
|
-
new_original.write( old_original.read )
|
244
|
+
new_original.write( old_original.respond_to?(:get) ? old_original.get : old_original.read )
|
229
245
|
new_original.rewind
|
230
246
|
|
231
247
|
@queued_for_write = { :original => new_original }
|
@@ -238,7 +254,7 @@ module Paperclip
|
|
238
254
|
true
|
239
255
|
end
|
240
256
|
end
|
241
|
-
|
257
|
+
|
242
258
|
# Returns true if a file has been assigned.
|
243
259
|
def file?
|
244
260
|
!original_filename.blank?
|
@@ -330,7 +346,12 @@ module Paperclip
|
|
330
346
|
end
|
331
347
|
|
332
348
|
def initialize_storage #:nodoc:
|
333
|
-
|
349
|
+
storage_class_name = @storage.to_s.capitalize
|
350
|
+
begin
|
351
|
+
@storage_module = Paperclip::Storage.const_get(storage_class_name)
|
352
|
+
rescue NameError
|
353
|
+
raise StorageMethodNotFound, "Cannot load storage module '#{storage_class_name}'"
|
354
|
+
end
|
334
355
|
self.extend(@storage_module)
|
335
356
|
end
|
336
357
|
|
@@ -389,4 +410,3 @@ module Paperclip
|
|
389
410
|
|
390
411
|
end
|
391
412
|
end
|
392
|
-
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class CommandLine
|
3
|
+
class << self
|
4
|
+
attr_accessor :path
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(binary, params = "", options = {})
|
8
|
+
@binary = binary.dup
|
9
|
+
@params = params.dup
|
10
|
+
@options = options.dup
|
11
|
+
@swallow_stderr = @options.has_key?(:swallow_stderr) ? @options.delete(:swallow_stderr) : Paperclip.options[:swallow_stderr]
|
12
|
+
@expected_outcodes = @options.delete(:expected_outcodes)
|
13
|
+
@expected_outcodes ||= [0]
|
14
|
+
end
|
15
|
+
|
16
|
+
def command
|
17
|
+
cmd = []
|
18
|
+
cmd << full_path(@binary)
|
19
|
+
cmd << interpolate(@params, @options)
|
20
|
+
cmd << bit_bucket if @swallow_stderr
|
21
|
+
cmd.join(" ")
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
Paperclip.log(command)
|
26
|
+
begin
|
27
|
+
output = self.class.send(:'`', command)
|
28
|
+
rescue Errno::ENOENT
|
29
|
+
raise Paperclip::CommandNotFoundError
|
30
|
+
end
|
31
|
+
if $?.exitstatus == 127
|
32
|
+
raise Paperclip::CommandNotFoundError
|
33
|
+
end
|
34
|
+
unless @expected_outcodes.include?($?.exitstatus)
|
35
|
+
raise Paperclip::PaperclipCommandLineError, "Command '#{command}' returned #{$?.exitstatus}. Expected #{@expected_outcodes.join(", ")}"
|
36
|
+
end
|
37
|
+
output
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def full_path(binary)
|
43
|
+
[self.class.path, binary].compact.join("/")
|
44
|
+
end
|
45
|
+
|
46
|
+
def interpolate(pattern, vars)
|
47
|
+
# interpolates :variables and :{variables}
|
48
|
+
pattern.gsub(%r#:(?:\w+|\{\w+\})#) do |match|
|
49
|
+
key = match[1..-1]
|
50
|
+
key = key[1..-2] if key[0,1] == '{'
|
51
|
+
if invalid_variables.include?(key)
|
52
|
+
raise PaperclipCommandLineError,
|
53
|
+
"Interpolation of #{key} isn't allowed."
|
54
|
+
end
|
55
|
+
shell_quote(vars[key.to_sym])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def invalid_variables
|
60
|
+
%w(expected_outcodes swallow_stderr)
|
61
|
+
end
|
62
|
+
|
63
|
+
def shell_quote(string)
|
64
|
+
return "" if string.nil? or string.blank?
|
65
|
+
if self.class.unix?
|
66
|
+
string.split("'").map{|m| "'#{m}'" }.join("\\'")
|
67
|
+
else
|
68
|
+
%{"#{string}"}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def bit_bucket
|
73
|
+
self.class.unix? ? "2>/dev/null" : "2>NUL"
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.unix?
|
77
|
+
File.exist?("/dev/null")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/paperclip/geometry.rb
CHANGED
@@ -16,7 +16,7 @@ module Paperclip
|
|
16
16
|
def self.from_file file
|
17
17
|
file = file.path if file.respond_to? "path"
|
18
18
|
geometry = begin
|
19
|
-
Paperclip.run("identify", "-format
|
19
|
+
Paperclip.run("identify", "-format %wx%h :file", :file => "#{file}[0]")
|
20
20
|
rescue PaperclipCommandLineError
|
21
21
|
""
|
22
22
|
end
|
@@ -75,12 +75,12 @@ module Paperclip
|
|
75
75
|
to_s
|
76
76
|
end
|
77
77
|
|
78
|
-
# Returns the scaling and cropping geometries (in string-based ImageMagick format)
|
79
|
-
# neccessary to transform this Geometry into the Geometry given. If crop is true,
|
80
|
-
# then it is assumed the destination Geometry will be the exact final resolution.
|
81
|
-
# In this case, the source Geometry is scaled so that an image containing the
|
82
|
-
# destination Geometry would be completely filled by the source image, and any
|
83
|
-
# overhanging image would be cropped. Useful for square thumbnail images. The cropping
|
78
|
+
# Returns the scaling and cropping geometries (in string-based ImageMagick format)
|
79
|
+
# neccessary to transform this Geometry into the Geometry given. If crop is true,
|
80
|
+
# then it is assumed the destination Geometry will be the exact final resolution.
|
81
|
+
# In this case, the source Geometry is scaled so that an image containing the
|
82
|
+
# destination Geometry would be completely filled by the source image, and any
|
83
|
+
# overhanging image would be cropped. Useful for square thumbnail images. The cropping
|
84
84
|
# is weighted at the center of the Geometry.
|
85
85
|
def transformation_to dst, crop = false
|
86
86
|
if crop
|
@@ -41,8 +41,9 @@ module Paperclip
|
|
41
41
|
# Returns the interpolated URL. Will raise an error if the url itself
|
42
42
|
# contains ":url" to prevent infinite recursion. This interpolation
|
43
43
|
# is used in the default :path to ease default specifications.
|
44
|
+
RIGHT_HERE = "#{__FILE__.gsub(%r{^\./}, "")}:#{__LINE__ + 3}"
|
44
45
|
def url attachment, style_name
|
45
|
-
raise InfiniteInterpolationError if
|
46
|
+
raise InfiniteInterpolationError if caller.any?{|b| b.index(RIGHT_HERE) }
|
46
47
|
attachment.url(style_name, false)
|
47
48
|
end
|
48
49
|
|
@@ -78,7 +79,7 @@ module Paperclip
|
|
78
79
|
# Returns the extension of the file. e.g. "jpg" for "file.jpg"
|
79
80
|
# If the style has a format defined, it will return the format instead
|
80
81
|
# of the actual extension.
|
81
|
-
def extension attachment, style_name
|
82
|
+
def extension attachment, style_name
|
82
83
|
((style = attachment.styles[style_name]) && style[:format]) ||
|
83
84
|
File.extname(attachment.original_filename).gsub(/^\.+/, "")
|
84
85
|
end
|
@@ -88,6 +89,11 @@ module Paperclip
|
|
88
89
|
attachment.instance.id
|
89
90
|
end
|
90
91
|
|
92
|
+
# Returns the fingerprint of the instance.
|
93
|
+
def fingerprint attachment, style_name
|
94
|
+
attachment.fingerprint
|
95
|
+
end
|
96
|
+
|
91
97
|
# Returns the id of the instance in a split path form. e.g. returns
|
92
98
|
# 000/001/234 for an id of 1234.
|
93
99
|
def id_partition attachment, style_name
|
data/lib/paperclip/iostream.rb
CHANGED
@@ -1,48 +1,34 @@
|
|
1
1
|
# Provides method that can be included on File-type objects (IO, StringIO, Tempfile, etc) to allow stream copying
|
2
2
|
# and Tempfile conversion.
|
3
3
|
module IOStream
|
4
|
-
|
5
4
|
# Returns a Tempfile containing the contents of the readable object.
|
6
|
-
def to_tempfile
|
7
|
-
|
8
|
-
|
5
|
+
def to_tempfile(object)
|
6
|
+
return object.to_tempfile if object.respond_to?(:to_tempfile)
|
7
|
+
name = object.respond_to?(:original_filename) ? object.original_filename : (object.respond_to?(:path) ? object.path : "stream")
|
8
|
+
tempfile = Paperclip::Tempfile.new(["stream", File.extname(name)])
|
9
9
|
tempfile.binmode
|
10
|
-
|
10
|
+
stream_to(object, tempfile)
|
11
11
|
end
|
12
12
|
|
13
13
|
# Copies one read-able object from one place to another in blocks, obviating the need to load
|
14
|
-
# the whole thing into memory. Defaults to 8k blocks.
|
15
|
-
#
|
16
|
-
|
17
|
-
# and returns the IO or Tempfile as passed in if one is sent as the destination.
|
18
|
-
def stream_to path_or_file, in_blocks_of = 8192
|
14
|
+
# the whole thing into memory. Defaults to 8k blocks. Returns a File if a String is passed
|
15
|
+
# in as the destination and returns the IO or Tempfile as passed in if one is sent as the destination.
|
16
|
+
def stream_to object, path_or_file, in_blocks_of = 8192
|
19
17
|
dstio = case path_or_file
|
20
18
|
when String then File.new(path_or_file, "wb+")
|
21
19
|
when IO then path_or_file
|
22
20
|
when Tempfile then path_or_file
|
23
21
|
end
|
24
22
|
buffer = ""
|
25
|
-
|
26
|
-
while
|
23
|
+
object.rewind
|
24
|
+
while object.read(in_blocks_of, buffer) do
|
27
25
|
dstio.write(buffer)
|
28
26
|
end
|
29
|
-
dstio.rewind
|
27
|
+
dstio.rewind
|
30
28
|
dstio
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
34
|
-
class IO #:nodoc:
|
35
|
-
include IOStream
|
36
|
-
end
|
37
|
-
|
38
|
-
%w( Tempfile StringIO ).each do |klass|
|
39
|
-
if Object.const_defined? klass
|
40
|
-
Object.const_get(klass).class_eval do
|
41
|
-
include IOStream
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
32
|
# Corrects a bug in Windows when asking for Tempfile size.
|
47
33
|
if defined? Tempfile
|
48
34
|
class Tempfile
|
@@ -42,7 +42,7 @@ module Paperclip
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def negative_failure_message
|
45
|
-
"Content types #{@allowed_types.join(", ")} should be rejected" +
|
45
|
+
"Content types #{@allowed_types.join(", ")} should be rejected" +
|
46
46
|
" and #{@rejected_types.join(", ")} accepted by #{@attachment_name}"
|
47
47
|
end
|
48
48
|
|
@@ -57,7 +57,8 @@ module Paperclip
|
|
57
57
|
file = StringIO.new(".")
|
58
58
|
file.content_type = type
|
59
59
|
(subject = @subject.new).attachment_for(@attachment_name).assign(file)
|
60
|
-
subject.valid?
|
60
|
+
subject.valid?
|
61
|
+
subject.errors[:"#{@attachment_name}_content_type"].blank?
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
@@ -72,4 +73,3 @@ module Paperclip
|
|
72
73
|
end
|
73
74
|
end
|
74
75
|
end
|
75
|
-
|
data/lib/paperclip/processor.rb
CHANGED
@@ -6,7 +6,7 @@ module Paperclip
|
|
6
6
|
#
|
7
7
|
# Processors are required to be defined inside the Paperclip module and
|
8
8
|
# are also required to be a subclass of Paperclip::Processor. There is
|
9
|
-
# only one method you *must* implement to properly be a subclass:
|
9
|
+
# only one method you *must* implement to properly be a subclass:
|
10
10
|
# #make, but #initialize may also be of use. Both methods accept 3
|
11
11
|
# arguments: the file that will be operated on (which is an instance of
|
12
12
|
# File), a hash of options that were defined in has_attached_file's
|
@@ -33,17 +33,26 @@ module Paperclip
|
|
33
33
|
new(file, options, attachment).make
|
34
34
|
end
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
# Due to how ImageMagick handles its image format conversion and how Tempfile
|
38
38
|
# handles its naming scheme, it is necessary to override how Tempfile makes
|
39
39
|
# its names so as to allow for file extensions. Idea taken from the comments
|
40
40
|
# on this blog post:
|
41
41
|
# http://marsorange.com/archives/of-mogrify-ruby-tempfile-dynamic-class-definitions
|
42
42
|
class Tempfile < ::Tempfile
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
# This is Ruby 1.8.7's implementation.
|
44
|
+
if RUBY_VERSION <= "1.8.6"
|
45
|
+
def make_tmpname(basename, n)
|
46
|
+
case basename
|
47
|
+
when Array
|
48
|
+
prefix, suffix = *basename
|
49
|
+
else
|
50
|
+
prefix, suffix = basename, ''
|
51
|
+
end
|
52
|
+
|
53
|
+
t = Time.now.strftime("%y%m%d")
|
54
|
+
path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
|
55
|
+
end
|
47
56
|
end
|
48
57
|
end
|
49
58
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'paperclip'
|
2
|
+
|
3
|
+
module Paperclip
|
4
|
+
if defined? Rails::Railtie
|
5
|
+
require 'rails'
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
initializer 'paperclip.insert_into_active_record' do
|
8
|
+
ActiveSupport.on_load :active_record do
|
9
|
+
Paperclip::Railtie.insert
|
10
|
+
end
|
11
|
+
end
|
12
|
+
rake_tasks do
|
13
|
+
load "tasks/paperclip.rake"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Railtie
|
19
|
+
def self.insert
|
20
|
+
ActiveRecord::Base.send(:include, Paperclip::Glue)
|
21
|
+
File.send(:include, Paperclip::Upfile)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|