paperclip-cloudfiles 2.3.2 → 2.3.8
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/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
|