kt-paperclip 4.4.0 → 5.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +17 -0
- data/.hound.yml +5 -16
- data/.travis.yml +15 -12
- data/Appraisals +4 -8
- data/CONTRIBUTING.md +16 -5
- data/Gemfile +3 -8
- data/LICENSE +1 -1
- data/NEWS +105 -31
- data/README.md +239 -153
- data/Rakefile +1 -1
- data/UPGRADING +12 -9
- data/features/basic_integration.feature +3 -2
- data/features/migration.feature +0 -24
- data/features/step_definitions/attachment_steps.rb +6 -6
- data/features/step_definitions/rails_steps.rb +29 -28
- data/features/step_definitions/s3_steps.rb +2 -2
- data/features/support/env.rb +1 -0
- data/features/support/paths.rb +1 -1
- data/features/support/rails.rb +0 -24
- data/gemfiles/4.2.gemfile +3 -5
- data/gemfiles/{3.2.gemfile → 5.0.gemfile} +4 -6
- data/lib/generators/paperclip/paperclip_generator.rb +9 -1
- data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +1 -1
- data/lib/paperclip/attachment.rb +25 -14
- data/lib/paperclip/attachment_registry.rb +2 -1
- data/lib/paperclip/callbacks.rb +8 -6
- data/lib/paperclip/content_type_detector.rb +3 -2
- data/lib/paperclip/errors.rb +3 -1
- data/lib/paperclip/file_command_content_type_detector.rb +1 -1
- data/lib/paperclip/geometry_detector_factory.rb +2 -2
- data/lib/paperclip/glue.rb +1 -1
- data/lib/paperclip/has_attached_file.rb +7 -1
- data/lib/paperclip/helpers.rb +15 -11
- data/lib/paperclip/interpolations.rb +1 -1
- data/lib/paperclip/io_adapters/abstract_adapter.rb +29 -3
- data/lib/paperclip/io_adapters/attachment_adapter.rb +10 -5
- data/lib/paperclip/io_adapters/data_uri_adapter.rb +8 -8
- data/lib/paperclip/io_adapters/empty_string_adapter.rb +5 -4
- data/lib/paperclip/io_adapters/file_adapter.rb +12 -6
- data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +7 -7
- data/lib/paperclip/io_adapters/identity_adapter.rb +12 -6
- data/lib/paperclip/io_adapters/nil_adapter.rb +8 -5
- data/lib/paperclip/io_adapters/registry.rb +6 -2
- data/lib/paperclip/io_adapters/stringio_adapter.rb +9 -6
- data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +10 -6
- data/lib/paperclip/io_adapters/uri_adapter.rb +17 -14
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +4 -4
- data/lib/paperclip/media_type_spoof_detector.rb +3 -2
- data/lib/paperclip/processor.rb +5 -4
- data/lib/paperclip/schema.rb +4 -10
- data/lib/paperclip/storage/filesystem.rb +13 -2
- data/lib/paperclip/storage/fog.rb +19 -13
- data/lib/paperclip/storage/s3.rb +87 -58
- data/lib/paperclip/thumbnail.rb +18 -8
- data/lib/paperclip/url_generator.rb +18 -14
- data/lib/paperclip/validators/attachment_size_validator.rb +1 -7
- data/lib/paperclip/validators.rb +1 -1
- data/lib/paperclip/version.rb +3 -1
- data/lib/paperclip.rb +13 -12
- data/lib/tasks/paperclip.rake +18 -4
- data/paperclip.gemspec +13 -11
- data/spec/paperclip/attachment_processing_spec.rb +2 -4
- data/spec/paperclip/attachment_registry_spec.rb +28 -0
- data/spec/paperclip/attachment_spec.rb +72 -18
- data/spec/paperclip/content_type_detector_spec.rb +1 -1
- data/spec/paperclip/file_command_content_type_detector_spec.rb +15 -1
- data/spec/paperclip/has_attached_file_spec.rb +24 -8
- data/spec/paperclip/integration_spec.rb +4 -3
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +76 -22
- data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +6 -3
- data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +7 -1
- data/spec/paperclip/io_adapters/file_adapter_spec.rb +2 -2
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +18 -1
- data/spec/paperclip/io_adapters/identity_adapter_spec.rb +1 -1
- data/spec/paperclip/io_adapters/registry_spec.rb +2 -2
- data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +1 -1
- data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +5 -5
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +48 -3
- data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +10 -0
- data/spec/paperclip/media_type_spoof_detector_spec.rb +15 -0
- data/spec/paperclip/paperclip_spec.rb +15 -45
- data/spec/paperclip/processor_spec.rb +4 -4
- data/spec/paperclip/storage/fog_spec.rb +26 -0
- data/spec/paperclip/storage/s3_live_spec.rb +20 -14
- data/spec/paperclip/storage/s3_spec.rb +357 -190
- data/spec/paperclip/tempfile_spec.rb +35 -0
- data/spec/paperclip/thumbnail_spec.rb +38 -35
- data/spec/paperclip/url_generator_spec.rb +53 -42
- data/spec/paperclip/validators/attachment_size_validator_spec.rb +26 -20
- data/spec/paperclip/validators_spec.rb +5 -5
- data/spec/spec_helper.rb +6 -2
- data/spec/support/assertions.rb +12 -1
- data/spec/support/conditional_filter_helper.rb +5 -0
- data/spec/support/mock_attachment.rb +2 -0
- data/spec/support/mock_url_generator_builder.rb +2 -2
- data/spec/support/model_reconstruction.rb +10 -2
- data/spec/support/reporting.rb +11 -0
- metadata +69 -75
- data/cucumber/paperclip_steps.rb +0 -6
- data/gemfiles/4.1.gemfile +0 -19
- data/lib/paperclip/deprecations.rb +0 -42
- data/lib/paperclip/locales/de.yml +0 -18
- data/lib/paperclip/locales/es.yml +0 -18
- data/lib/paperclip/locales/ja.yml +0 -18
- data/lib/paperclip/locales/pt-BR.yml +0 -18
- data/lib/paperclip/locales/zh-CN.yml +0 -18
- data/lib/paperclip/locales/zh-HK.yml +0 -18
- data/lib/paperclip/locales/zh-TW.yml +0 -18
- data/spec/paperclip/deprecations_spec.rb +0 -65
- data/spec/support/deprecations.rb +0 -9
- data/spec/support/rails_helpers.rb +0 -7
data/README.md
CHANGED
@@ -1,12 +1,26 @@
|
|
1
1
|
Paperclip
|
2
2
|
=========
|
3
3
|
|
4
|
-
|
4
|
+
## Documentation valid for `master` branch
|
5
|
+
|
6
|
+
Please check the documentation for the paperclip version you are using:
|
7
|
+
https://github.com/thoughtbot/paperclip/releases
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
[![Build Status](https://secure.travis-ci.org/thoughtbot/paperclip.svg?branch=master)](http://travis-ci.org/thoughtbot/paperclip)
|
12
|
+
[![Dependency Status](https://gemnasium.com/thoughtbot/paperclip.svg?travis)](https://gemnasium.com/thoughtbot/paperclip)
|
13
|
+
[![Code Climate](https://codeclimate.com/github/thoughtbot/paperclip.svg)](https://codeclimate.com/github/thoughtbot/paperclip)
|
14
|
+
[![Inline docs](http://inch-ci.org/github/thoughtbot/paperclip.svg)](http://inch-ci.org/github/thoughtbot/paperclip)
|
15
|
+
[![Security](https://hakiri.io/github/thoughtbot/paperclip/master.svg)](https://hakiri.io/github/thoughtbot/paperclip/master)
|
16
|
+
|
17
|
+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
18
|
+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
5
19
|
|
6
20
|
- [Requirements](#requirements)
|
7
|
-
- [Ruby
|
21
|
+
- [Ruby and Rails](#ruby-and-rails)
|
8
22
|
- [Image Processor](#image-processor)
|
9
|
-
- [file](#file)
|
23
|
+
- [`file`](#file)
|
10
24
|
- [Installation](#installation)
|
11
25
|
- [Quick Start](#quick-start)
|
12
26
|
- [Models](#models)
|
@@ -14,35 +28,41 @@ Paperclip
|
|
14
28
|
- [Edit and New Views](#edit-and-new-views)
|
15
29
|
- [Edit and New Views with Simple Form](#edit-and-new-views-with-simple-form)
|
16
30
|
- [Controller](#controller)
|
17
|
-
- [
|
31
|
+
- [View Helpers](#view-helpers)
|
32
|
+
- [Checking a File Exists](#checking-a-file-exists)
|
18
33
|
- [Deleting an Attachment](#deleting-an-attachment)
|
19
34
|
- [Usage](#usage)
|
20
35
|
- [Validations](#validations)
|
36
|
+
- [Internationalization (I18n)](#internationalization-i18n)
|
21
37
|
- [Security Validations](#security-validations)
|
22
38
|
- [Defaults](#defaults)
|
23
39
|
- [Migrations](#migrations-1)
|
24
|
-
- [Table
|
40
|
+
- [Add Attachment Column To A Table](#add-attachment-column-to-a-table)
|
25
41
|
- [Schema Definition](#schema-definition)
|
26
42
|
- [Vintage Syntax](#vintage-syntax)
|
27
43
|
- [Storage](#storage)
|
28
44
|
- [Understanding Storage](#understanding-storage)
|
45
|
+
- [IO Adapters](#io-adapters)
|
29
46
|
- [Post Processing](#post-processing)
|
47
|
+
- [Custom Attachment Processors](#custom-attachment-processors)
|
30
48
|
- [Events](#events)
|
31
49
|
- [URI Obfuscation](#uri-obfuscation)
|
32
|
-
|
33
|
-
|
34
|
-
- [Custom Attachment Processors](#custom-attachment-processors)
|
50
|
+
- [Checksum / Fingerprint](#checksum--fingerprint)
|
51
|
+
- [File Preservation for Soft-Delete](#file-preservation-for-soft-delete)
|
35
52
|
- [Dynamic Configuration](#dynamic-configuration)
|
36
|
-
- [Dynamic Styles](#dynamic-styles)
|
37
|
-
- [Dynamic Processors](#dynamic-processors)
|
53
|
+
- [Dynamic Styles:](#dynamic-styles)
|
54
|
+
- [Dynamic Processors:](#dynamic-processors)
|
38
55
|
- [Logging](#logging)
|
39
56
|
- [Deployment](#deployment)
|
57
|
+
- [Attachment Styles](#attachment-styles)
|
40
58
|
- [Testing](#testing)
|
41
59
|
- [Contributing](#contributing)
|
42
60
|
- [License](#license)
|
43
61
|
- [About thoughtbot](#about-thoughtbot)
|
44
62
|
|
45
|
-
|
63
|
+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
64
|
+
|
65
|
+
Paperclip is intended as an easy file attachment library for ActiveRecord. The
|
46
66
|
intent behind it was to keep setup as easy as possible and to treat files as
|
47
67
|
much like other attributes as possible. This means they aren't saved to their
|
48
68
|
final locations on disk, nor are they deleted if set to nil, until
|
@@ -54,10 +74,10 @@ packages). Attached files are saved to the filesystem and referenced in the
|
|
54
74
|
browser by an easily understandable specification, which has sensible and
|
55
75
|
useful defaults.
|
56
76
|
|
57
|
-
See the documentation for `has_attached_file` in [`Paperclip::ClassMethods`](http://rubydoc.info/gems/paperclip/Paperclip/ClassMethods) for
|
77
|
+
See the documentation for `has_attached_file` in [`Paperclip::ClassMethods`](http://www.rubydoc.info/gems/paperclip/Paperclip/ClassMethods) for
|
58
78
|
more detailed options.
|
59
79
|
|
60
|
-
The complete [RDoc](http://
|
80
|
+
The complete [RDoc](http://www.rubydoc.info/gems/paperclip) is online.
|
61
81
|
|
62
82
|
---
|
63
83
|
|
@@ -66,9 +86,8 @@ Requirements
|
|
66
86
|
|
67
87
|
### Ruby and Rails
|
68
88
|
|
69
|
-
Paperclip now requires Ruby version **>= 2.
|
70
|
-
|
71
|
-
If you're still on Ruby 1.8.7 or Ruby on Rails 2.3.x, you can still use Paperclip 2.7.x with your project. Also, everything in this README might not apply to your version of Paperclip, and you should read [the README for version 2.7](http://rubydoc.info/gems/paperclip/2.7.0) instead.
|
89
|
+
Paperclip now requires Ruby version **>= 2.1** and Rails version **>= 4.2**
|
90
|
+
(only if you're going to use Paperclip with Ruby on Rails).
|
72
91
|
|
73
92
|
### Image Processor
|
74
93
|
|
@@ -86,7 +105,7 @@ In development mode, you might add this line to `config/environments/development
|
|
86
105
|
Paperclip.options[:command_path] = "/usr/local/bin/"
|
87
106
|
```
|
88
107
|
|
89
|
-
If you're on Mac OS X, you'll want to run the following with Homebrew:
|
108
|
+
If you're on Mac OS X, you'll want to run the following with [Homebrew](http://www.brew.sh):
|
90
109
|
|
91
110
|
brew install imagemagick
|
92
111
|
|
@@ -95,17 +114,14 @@ to install GhostScript. On Mac OS X, you can also install that using Homebrew:
|
|
95
114
|
|
96
115
|
brew install gs
|
97
116
|
|
98
|
-
If you
|
99
|
-
|
100
|
-
sudo apt-get install imagemagick -y
|
101
|
-
|
102
|
-
If you're on Ubuntu (or any Debian base Linux distribution), you'll want to run the following with apt-get:
|
117
|
+
If you are on Ubuntu (or any Debian base Linux distribution), you'll want to run
|
118
|
+
the following with apt-get:
|
103
119
|
|
104
120
|
sudo apt-get install imagemagick -y
|
105
121
|
|
106
122
|
### `file`
|
107
123
|
|
108
|
-
The Unix [`file` command](
|
124
|
+
The Unix [`file` command](https://en.wikipedia.org/wiki/File_(command)) is required for content-type checking.
|
109
125
|
This utility isn't available in Windows, but comes bundled with Ruby [Devkit](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit),
|
110
126
|
so Windows users must make sure that the devkit is installed and added to the system `PATH`.
|
111
127
|
|
@@ -151,18 +167,18 @@ Paperclip is distributed as a gem, which is how it should be used in your app.
|
|
151
167
|
Include the gem in your Gemfile:
|
152
168
|
|
153
169
|
```ruby
|
154
|
-
gem "paperclip", "~>
|
170
|
+
gem "paperclip", "~> 5.2.1"
|
155
171
|
```
|
156
172
|
|
157
173
|
Or, if you want to get the latest, you can get master from the main paperclip repository:
|
158
174
|
|
159
175
|
```ruby
|
160
|
-
gem "paperclip", :
|
176
|
+
gem "paperclip", git: "git://github.com/thoughtbot/paperclip.git"
|
161
177
|
```
|
162
178
|
|
163
179
|
If you're trying to use features that don't seem to be in the latest released gem, but are
|
164
180
|
mentioned in this README, then you probably need to specify the master branch if you want to
|
165
|
-
use them. This README is probably ahead of the latest released version
|
181
|
+
use them. This README is probably ahead of the latest released version if you're reading it
|
166
182
|
on GitHub.
|
167
183
|
|
168
184
|
For Non-Rails usage:
|
@@ -181,27 +197,17 @@ Quick Start
|
|
181
197
|
|
182
198
|
### Models
|
183
199
|
|
184
|
-
**Rails 3**
|
185
|
-
|
186
200
|
```ruby
|
187
201
|
class User < ActiveRecord::Base
|
188
|
-
|
189
|
-
|
190
|
-
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
|
191
|
-
end
|
192
|
-
```
|
193
|
-
|
194
|
-
**Rails 4**
|
195
|
-
|
196
|
-
```ruby
|
197
|
-
class User < ActiveRecord::Base
|
198
|
-
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
|
199
|
-
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
|
202
|
+
has_attached_file :avatar, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
|
203
|
+
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
|
200
204
|
end
|
201
205
|
```
|
202
206
|
|
203
207
|
### Migrations
|
204
208
|
|
209
|
+
|
210
|
+
Assuming you have a `users` table, add an `avatar` column to the `users` table:
|
205
211
|
```ruby
|
206
212
|
class AddAvatarColumnsToUsers < ActiveRecord::Migration
|
207
213
|
def up
|
@@ -217,35 +223,28 @@ end
|
|
217
223
|
(Or you can use the Rails migration generator: `rails generate paperclip user avatar`)
|
218
224
|
|
219
225
|
### Edit and New Views
|
220
|
-
|
226
|
+
Make sure you have corresponding methods in your controller:
|
221
227
|
```erb
|
222
|
-
<%= form_for @user, :
|
228
|
+
<%= form_for @user, url: users_path, html: { multipart: true } do |form| %>
|
223
229
|
<%= form.file_field :avatar %>
|
230
|
+
<%= form.submit %>
|
224
231
|
<% end %>
|
225
232
|
```
|
226
233
|
|
227
|
-
### Edit and New Views with Simple Form
|
234
|
+
### Edit and New Views with [Simple Form](https://github.com/plataformatec/simple_form)
|
235
|
+
|
228
236
|
```erb
|
229
237
|
<%= simple_form_for @user, url: users_path do |form| %>
|
230
238
|
<%= form.input :avatar, as: :file %>
|
239
|
+
<%= form.submit %>
|
231
240
|
<% end %>
|
232
241
|
```
|
233
242
|
|
234
243
|
### Controller
|
235
244
|
|
236
|
-
**Rails 3**
|
237
|
-
|
238
245
|
```ruby
|
239
246
|
def create
|
240
|
-
@user = User.create(
|
241
|
-
end
|
242
|
-
```
|
243
|
-
|
244
|
-
**Rails 4**
|
245
|
-
|
246
|
-
```ruby
|
247
|
-
def create
|
248
|
-
@user = User.create( user_params )
|
247
|
+
@user = User.create(user_params)
|
249
248
|
end
|
250
249
|
|
251
250
|
private
|
@@ -258,14 +257,24 @@ def user_params
|
|
258
257
|
end
|
259
258
|
```
|
260
259
|
|
261
|
-
###
|
262
|
-
|
260
|
+
### View Helpers
|
261
|
+
Add these to the view where you want your images displayed:
|
263
262
|
```erb
|
264
263
|
<%= image_tag @user.avatar.url %>
|
265
264
|
<%= image_tag @user.avatar.url(:medium) %>
|
266
265
|
<%= image_tag @user.avatar.url(:thumb) %>
|
267
266
|
```
|
268
267
|
|
268
|
+
### Checking a File Exists
|
269
|
+
|
270
|
+
There are two methods for checking if a file exists:
|
271
|
+
|
272
|
+
- `file?` and `present?` checks if the `_file_name` field is populated
|
273
|
+
- `exists?` checks if the file exists (will perform a TCP connection if stored in the cloud)
|
274
|
+
|
275
|
+
Keep this in mind if you are checking if files are present in a loop. The first
|
276
|
+
version is significantly more performant, but has different semantics.
|
277
|
+
|
269
278
|
### Deleting an Attachment
|
270
279
|
|
271
280
|
Set the attribute to `nil` and save.
|
@@ -296,7 +305,7 @@ You'll need to add `<attachment>_content_type` in case you want to use content t
|
|
296
305
|
validation.
|
297
306
|
|
298
307
|
More information about the options passed to `has_attached_file` is available in the
|
299
|
-
documentation of [`Paperclip::ClassMethods`](http://rubydoc.info/gems/paperclip/Paperclip/ClassMethods).
|
308
|
+
documentation of [`Paperclip::ClassMethods`](http://www.rubydoc.info/gems/paperclip/Paperclip/ClassMethods).
|
300
309
|
|
301
310
|
Validations
|
302
311
|
-----------
|
@@ -310,9 +319,9 @@ For validations, Paperclip introduces several validators to validate your attach
|
|
310
319
|
Example Usage:
|
311
320
|
|
312
321
|
```ruby
|
313
|
-
validates :avatar, :
|
314
|
-
validates_with AttachmentPresenceValidator, :
|
315
|
-
validates_with AttachmentSizeValidator, :
|
322
|
+
validates :avatar, attachment_presence: true
|
323
|
+
validates_with AttachmentPresenceValidator, attributes: :avatar
|
324
|
+
validates_with AttachmentSizeValidator, attributes: :avatar, less_than: 1.megabytes
|
316
325
|
|
317
326
|
```
|
318
327
|
|
@@ -331,9 +340,9 @@ validates_attachment_presence :avatar
|
|
331
340
|
Lastly, you can also define multiple validations on a single attachment using `validates_attachment`:
|
332
341
|
|
333
342
|
```ruby
|
334
|
-
validates_attachment :avatar, :
|
335
|
-
:
|
336
|
-
:
|
343
|
+
validates_attachment :avatar, presence: true,
|
344
|
+
content_type: { content_type: "image/jpeg" },
|
345
|
+
size: { in: 0..10.kilobytes }
|
337
346
|
```
|
338
347
|
|
339
348
|
_NOTE: Post-processing will not even **start** if the attachment is not valid
|
@@ -342,7 +351,7 @@ called with valid attachments._
|
|
342
351
|
|
343
352
|
```ruby
|
344
353
|
class Message < ActiveRecord::Base
|
345
|
-
has_attached_file :asset, styles: {thumb: "100x100#"}
|
354
|
+
has_attached_file :asset, styles: { thumb: "100x100#" }
|
346
355
|
|
347
356
|
before_post_process :skip_for_audio
|
348
357
|
|
@@ -358,7 +367,7 @@ afterwards, then assign manually:
|
|
358
367
|
|
359
368
|
```ruby
|
360
369
|
class Book < ActiveRecord::Base
|
361
|
-
has_attached_file :document, styles: {thumbnail: "60x60#"}
|
370
|
+
has_attached_file :document, styles: { thumbnail: "60x60#" }
|
362
371
|
validates_attachment :document, content_type: { content_type: "application/pdf" }
|
363
372
|
validates_something_else # Other validations that conflict with Paperclip's
|
364
373
|
end
|
@@ -391,7 +400,7 @@ image-y ones:
|
|
391
400
|
|
392
401
|
```ruby
|
393
402
|
validates_attachment :avatar,
|
394
|
-
:
|
403
|
+
content_type: { content_type: ["image/jpeg", "image/gif", "image/png"] }
|
395
404
|
```
|
396
405
|
|
397
406
|
`Paperclip::ContentTypeDetector` will attempt to match a file's extension to an
|
@@ -399,6 +408,12 @@ inferred content_type, regardless of the actual contents of the file.
|
|
399
408
|
|
400
409
|
---
|
401
410
|
|
411
|
+
Internationalization (I18n)
|
412
|
+
---------------------------
|
413
|
+
|
414
|
+
For using or adding locale files in different languages, check the project
|
415
|
+
https://github.com/thoughtbot/paperclip-i18n.
|
416
|
+
|
402
417
|
Security Validations
|
403
418
|
====================
|
404
419
|
|
@@ -415,9 +430,9 @@ do this.
|
|
415
430
|
class ActiveRecord::Base
|
416
431
|
has_attached_file :avatar
|
417
432
|
# Validate content type
|
418
|
-
validates_attachment_content_type :avatar, :
|
433
|
+
validates_attachment_content_type :avatar, content_type: /\Aimage/
|
419
434
|
# Validate filename
|
420
|
-
validates_attachment_file_name :avatar, :
|
435
|
+
validates_attachment_file_name :avatar, matches: [/png\z/, /jpe?g\z/]
|
421
436
|
# Explicitly do not validate
|
422
437
|
do_not_validate_attachment_file_type :avatar
|
423
438
|
end
|
@@ -437,14 +452,13 @@ will not cause errors to be raised.
|
|
437
452
|
|
438
453
|
This can sometimes cause false validation errors in applications that use custom
|
439
454
|
file extensions. In these cases you may wish to add your custom extension to the
|
440
|
-
list of
|
441
|
-
gem:
|
455
|
+
list of content type mappings by creating `config/initializers/paperclip.rb`:
|
442
456
|
|
443
457
|
```ruby
|
444
458
|
# Allow ".foo" as an extension for files with the MIME type "text/plain".
|
445
|
-
|
446
|
-
|
447
|
-
|
459
|
+
Paperclip.options[:content_type_mappings] = {
|
460
|
+
foo: %w(text/plain)
|
461
|
+
}
|
448
462
|
```
|
449
463
|
|
450
464
|
---
|
@@ -460,7 +474,7 @@ module YourApp
|
|
460
474
|
class Application < Rails::Application
|
461
475
|
# Other code...
|
462
476
|
|
463
|
-
config.paperclip_defaults = {:
|
477
|
+
config.paperclip_defaults = { storage: :fog, fog_credentials: { provider: "Local", local_root: "#{Rails.root}/public"}, fog_directory: "", fog_host: "localhost"}
|
464
478
|
end
|
465
479
|
end
|
466
480
|
```
|
@@ -471,7 +485,7 @@ An example Rails initializer would look something like this:
|
|
471
485
|
|
472
486
|
```ruby
|
473
487
|
Paperclip::Attachment.default_options[:storage] = :fog
|
474
|
-
Paperclip::Attachment.default_options[:fog_credentials] = {:
|
488
|
+
Paperclip::Attachment.default_options[:fog_credentials] = { provider: "Local", local_root: "#{Rails.root}/public"}
|
475
489
|
Paperclip::Attachment.default_options[:fog_directory] = ""
|
476
490
|
Paperclip::Attachment.default_options[:fog_host] = "http://localhost:3000"
|
477
491
|
```
|
@@ -493,6 +507,7 @@ class CreateUsersWithAttachments < ActiveRecord::Migration
|
|
493
507
|
create_table :users do |t|
|
494
508
|
t.attachment :avatar
|
495
509
|
end
|
510
|
+
end
|
496
511
|
|
497
512
|
# This is assuming you are only using the users table for Paperclip attachment. Drop with care!
|
498
513
|
def down
|
@@ -539,7 +554,7 @@ class AddAttachmentColumnsToUsers < ActiveRecord::Migration
|
|
539
554
|
end
|
540
555
|
```
|
541
556
|
|
542
|
-
### Vintage
|
557
|
+
### Vintage Syntax
|
543
558
|
|
544
559
|
Vintage syntax (such as `t.has_attached_file` and `drop_attached_file`) is still supported in
|
545
560
|
Paperclip 3.x, but you're advised to update those migration files to use this new syntax.
|
@@ -558,6 +573,7 @@ Paperclip ships with 3 storage adapters:
|
|
558
573
|
If you would like to use Paperclip with another storage, you can install these
|
559
574
|
gems along side with Paperclip:
|
560
575
|
|
576
|
+
* [paperclip-azure](https://github.com/supportify/paperclip-azure)
|
561
577
|
* [paperclip-azure-storage](https://github.com/gmontard/paperclip-azure-storage)
|
562
578
|
* [paperclip-dropbox](https://github.com/janko-m/paperclip-dropbox)
|
563
579
|
|
@@ -566,9 +582,9 @@ gems along side with Paperclip:
|
|
566
582
|
The files that are assigned as attachments are, by default, placed in the
|
567
583
|
directory specified by the `:path` option to `has_attached_file`. By default, this
|
568
584
|
location is `:rails_root/public/system/:class/:attachment/:id_partition/:style/:filename`.
|
569
|
-
This location was chosen because on standard Capistrano deployments, the
|
570
|
-
`public/system` directory
|
571
|
-
|
585
|
+
This location was chosen because, on standard Capistrano deployments, the
|
586
|
+
`public/system` directory can be symlinked to the app's shared directory, meaning it
|
587
|
+
survives between deployments. For example, using that `:path`, you may have a
|
572
588
|
file at
|
573
589
|
|
574
590
|
/data/myapp/releases/20081229172410/public/system/users/avatar/000/000/013/small/my_pic.png
|
@@ -580,12 +596,12 @@ You may also choose to store your files using Amazon's S3 service. To do so, inc
|
|
580
596
|
the `aws-sdk` gem in your Gemfile:
|
581
597
|
|
582
598
|
```ruby
|
583
|
-
gem 'aws-sdk', '~>
|
599
|
+
gem 'aws-sdk', '~> 2.3.0'
|
584
600
|
```
|
585
601
|
|
586
602
|
And then you can specify using S3 from `has_attached_file`.
|
587
603
|
You can find more information about configuring and using S3 storage in
|
588
|
-
[the `Paperclip::Storage::S3` documentation](http://rubydoc.info/gems/paperclip/Paperclip/Storage/S3).
|
604
|
+
[the `Paperclip::Storage::S3` documentation](http://www.rubydoc.info/gems/paperclip/Paperclip/Storage/S3).
|
589
605
|
|
590
606
|
Files on the local filesystem (and in the Rails app's public directory) will be
|
591
607
|
available to the internet at large. If you require access control, it's
|
@@ -596,64 +612,121 @@ variables.
|
|
596
612
|
|
597
613
|
---
|
598
614
|
|
615
|
+
IO Adapters
|
616
|
+
-----------
|
617
|
+
|
618
|
+
When a file is uploaded or attached, it can be in one of a few different input
|
619
|
+
forms, from Rails' UploadedFile object to a StringIO to a Tempfile or even a
|
620
|
+
simple String that is a URL that points to an image.
|
621
|
+
|
622
|
+
Paperclip will accept, by default, many of these sources. It also is capable of
|
623
|
+
handling even more with a little configuration. The IO Adapters that handle
|
624
|
+
images from non-local sources are not enabled by default. They can be enabled by
|
625
|
+
adding a line similar to the following into `config/initializers/paperclip.rb`:
|
626
|
+
|
627
|
+
```ruby
|
628
|
+
Paperclip::DataUriAdapter.register
|
629
|
+
```
|
630
|
+
|
631
|
+
It's best to only enable a remote-loading adapter if you need it. Otherwise
|
632
|
+
there's a chance that someone can gain insight into your internal network
|
633
|
+
structure using it as a vector.
|
634
|
+
|
635
|
+
The following adapters are *not* loaded by default:
|
636
|
+
|
637
|
+
* `Paperclip::UriAdapter` - which accepts a `URI` instance.
|
638
|
+
* `Paperclip::HttpUrlProxyAdapter` - which accepts a `http` string.
|
639
|
+
* `Paperclip::DataUriAdapter` - which accepts a Base64-encoded `data:` string.
|
640
|
+
|
641
|
+
---
|
642
|
+
|
599
643
|
Post Processing
|
600
644
|
---------------
|
601
645
|
|
602
646
|
Paperclip supports an extensible selection of post-processors. When you define
|
603
647
|
a set of styles for an attachment, by default it is expected that those
|
604
|
-
"styles" are actually "thumbnails."
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
automatically loaded by Paperclip, allowing you to easily define custom
|
609
|
-
processors. You can specify a processor with the `:processors` option to
|
610
|
-
`has_attached_file`:
|
648
|
+
"styles" are actually "thumbnails." These are processed by
|
649
|
+
`Paperclip::Thumbnail`. For backward compatibility reasons you can pass either
|
650
|
+
a single geometry string, or an array containing a geometry and a format that
|
651
|
+
the file will be converted to, like so:
|
611
652
|
|
612
653
|
```ruby
|
613
|
-
has_attached_file :
|
614
|
-
:processors => [:ocr]
|
654
|
+
has_attached_file :avatar, styles: { thumb: ["32x32#", :png] }
|
615
655
|
```
|
616
656
|
|
617
|
-
This
|
618
|
-
|
619
|
-
|
657
|
+
This will convert the "thumb" style to a 32x32 square in PNG format, regardless
|
658
|
+
of what was uploaded. If the format is not specified, it is kept the same (e.g.
|
659
|
+
JPGs will remain JPGs). `Paperclip::Thumbnail` uses ImageMagick to process
|
660
|
+
images; [ImageMagick's geometry documentation](http://www.imagemagick.org/script/command-line-processing.php#geometry)
|
661
|
+
has more information on the accepted style formats.
|
620
662
|
|
621
|
-
|
622
|
-
reasons, you can pass a single geometry string or an array containing a
|
623
|
-
geometry and a format that the file will be converted to, like so:
|
663
|
+
For more fine-grained control of the conversion process, `source_file_options` and `convert_options` can be used to pass flags and settings directly to ImageMagick's powerful Convert tool, [documented here](https://www.imagemagick.org/script/convert.php). For example:
|
624
664
|
|
625
665
|
```ruby
|
626
|
-
has_attached_file :
|
666
|
+
has_attached_file :image, styles: { regular: ['800x800>', :png]},
|
667
|
+
source_file_options: { regular: "-density 96 -depth 8 -quality 85" },
|
668
|
+
convert_options: { regular: "-posterize 3"}
|
627
669
|
```
|
628
670
|
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
671
|
+
ImageMagick supports a number of environment variables for controlling its resource limits. For example, you can enforce memory or execution time limits by setting the following variables in your application's process environment:
|
672
|
+
|
673
|
+
* `MAGICK_MEMORY_LIMIT=128MiB`
|
674
|
+
* `MAGICK_MAP_LIMIT=64MiB`
|
675
|
+
* `MAGICK_TIME_LIMIT=30`
|
676
|
+
|
677
|
+
For a full list of variables and description, see [ImageMagick's resources documentation](http://www.imagemagick.org/script/resources.php).
|
678
|
+
|
679
|
+
---
|
680
|
+
|
681
|
+
Custom Attachment Processors
|
682
|
+
-------
|
683
|
+
|
684
|
+
You can write your own custom attachment processors to carry out tasks like
|
685
|
+
adding watermarks, compressing images, or encrypting files. Custom processors
|
686
|
+
must be defined within the `Paperclip` module, inherit from
|
687
|
+
`Paperclip::Processor` (see [`lib/paperclip/processor.rb`](https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/processor.rb)),
|
688
|
+
and implement a `make` method that returns a `File`. All files in your Rails
|
689
|
+
app's `lib/paperclip` and `lib/paperclip_processors` directories will be
|
690
|
+
automatically loaded by Paperclip. Processors are specified using the
|
691
|
+
`:processors` option to `has_attached_file`:
|
692
|
+
|
693
|
+
```ruby
|
694
|
+
has_attached_file :scan, styles: { text: { quality: :better } },
|
695
|
+
processors: [:ocr]
|
696
|
+
```
|
697
|
+
|
698
|
+
This would load the hypothetical class `Paperclip::Ocr`, and pass it the
|
699
|
+
options hash `{ quality: :better }`, along with the uploaded file.
|
633
700
|
|
634
701
|
Multiple processors can be specified, and they will be invoked in the order
|
635
|
-
they are defined in the `:processors` array. Each successive processor
|
636
|
-
|
637
|
-
|
638
|
-
|
702
|
+
they are defined in the `:processors` array. Each successive processor is given
|
703
|
+
the result from the previous processor. All processors receive the same
|
704
|
+
parameters, which are defined in the `:styles` hash. For example, assuming we
|
705
|
+
had this definition:
|
639
706
|
|
640
707
|
```ruby
|
641
|
-
has_attached_file :scan, :
|
642
|
-
:
|
708
|
+
has_attached_file :scan, styles: { text: { quality: :better } },
|
709
|
+
processors: [:rotator, :ocr]
|
643
710
|
```
|
644
711
|
|
645
|
-
|
646
|
-
options `{ :
|
647
|
-
|
712
|
+
Both the `:rotator` processor and the `:ocr` processor would receive the
|
713
|
+
options `{ quality: :better }`. If a processor receives an option it doesn't
|
714
|
+
recognise, it's expected to ignore it.
|
648
715
|
|
649
716
|
_NOTE: Because processors operate by turning the original attachment into the
|
650
717
|
styles, no processors will be run if there are no styles defined._
|
651
718
|
|
652
719
|
If you're interested in caching your thumbnail's width, height and size in the
|
653
|
-
database, take a look at the [paperclip-meta](https://github.com/teeparham/paperclip-meta)
|
720
|
+
database, take a look at the [paperclip-meta](https://github.com/teeparham/paperclip-meta)
|
721
|
+
gem.
|
654
722
|
|
655
723
|
Also, if you're interested in generating the thumbnail on-the-fly, you might want
|
656
|
-
to look into the [attachment_on_the_fly](https://github.com/drpentode/Attachment-on-the-Fly)
|
724
|
+
to look into the [attachment_on_the_fly](https://github.com/drpentode/Attachment-on-the-Fly)
|
725
|
+
gem.
|
726
|
+
|
727
|
+
Paperclip's thumbnail generator (see [`lib/paperclip/thumbnail.rb`](lib/paperclip/thumbnail.rb))
|
728
|
+
is implemented as a processor, and may be a good reference for writing your own
|
729
|
+
processors.
|
657
730
|
|
658
731
|
---
|
659
732
|
|
@@ -677,7 +750,7 @@ called with valid attachments._
|
|
677
750
|
|
678
751
|
```ruby
|
679
752
|
class Message < ActiveRecord::Base
|
680
|
-
has_attached_file :asset, styles: {thumb: "100x100#"}
|
753
|
+
has_attached_file :asset, styles: { thumb: "100x100#" }
|
681
754
|
|
682
755
|
before_post_process :skip_for_audio
|
683
756
|
|
@@ -699,8 +772,8 @@ Example Usage:
|
|
699
772
|
|
700
773
|
```ruby
|
701
774
|
has_attached_file :avatar, {
|
702
|
-
:
|
703
|
-
:
|
775
|
+
url: "/system/:hash.:extension",
|
776
|
+
hash_secret: "longSecretString"
|
704
777
|
}
|
705
778
|
```
|
706
779
|
|
@@ -712,10 +785,10 @@ is specified in `:hash_data`. The default value for `:hash_data` is `":class/:at
|
|
712
785
|
|
713
786
|
For more on this feature, read [the author's own explanation](https://github.com/thoughtbot/paperclip/pull/416)
|
714
787
|
|
715
|
-
|
788
|
+
Checksum / Fingerprint
|
716
789
|
-------
|
717
790
|
|
718
|
-
|
791
|
+
A checksum of the original file assigned will be placed in the model if it
|
719
792
|
has an attribute named fingerprint. Following the user model migration example
|
720
793
|
above, the migration would look like the following:
|
721
794
|
|
@@ -731,6 +804,17 @@ class AddAvatarFingerprintColumnToUser < ActiveRecord::Migration
|
|
731
804
|
end
|
732
805
|
```
|
733
806
|
|
807
|
+
The algorithm can be specified using a configuration option; it defaults to MD5
|
808
|
+
for backwards compatibility with Paperclip 5 and earlier.
|
809
|
+
|
810
|
+
```ruby
|
811
|
+
has_attached_file :some_attachment, adapter_options: { hash_digest: Digest::SHA256 }
|
812
|
+
```
|
813
|
+
|
814
|
+
Run `CLASS=User ATTACHMENT=avatar rake paperclip:refresh:fingerprints` after
|
815
|
+
changing the digest on existing attachments to update the fingerprints in the
|
816
|
+
database.
|
817
|
+
|
734
818
|
File Preservation for Soft-Delete
|
735
819
|
-------
|
736
820
|
|
@@ -738,7 +822,7 @@ An option is available to preserve attachments in order to play nicely with soft
|
|
738
822
|
|
739
823
|
```ruby
|
740
824
|
has_attached_file :some_attachment, {
|
741
|
-
:
|
825
|
+
preserve_files: true,
|
742
826
|
}
|
743
827
|
```
|
744
828
|
|
@@ -746,25 +830,6 @@ This will prevent ```some_attachment``` from being wiped out when the model gets
|
|
746
830
|
|
747
831
|
---
|
748
832
|
|
749
|
-
Custom Attachment Processors
|
750
|
-
-------
|
751
|
-
|
752
|
-
Custom attachment processors can be implemented and their only requirement is
|
753
|
-
to inherit from `Paperclip::Processor` (see `lib/paperclip/processor.rb`).
|
754
|
-
For example, when `:styles` are specified for an image attachment, the
|
755
|
-
thumbnail processor (see `lib/paperclip/thumbnail.rb`) is loaded without having
|
756
|
-
to specify it as a `:processor` parameter to `has_attached_file`. When any
|
757
|
-
other processor is defined, it must be called out in the `:processors`
|
758
|
-
parameter if it is to be applied to the attachment. The thumbnail processor
|
759
|
-
uses the ImageMagick `convert` command to do the work of resizing image
|
760
|
-
thumbnails. It would be easy to create a custom processor that watermarks
|
761
|
-
an image using ImageMagick's `composite` command. Following the
|
762
|
-
implementation pattern of the thumbnail processor would be a way to implement a
|
763
|
-
watermark processor. All kinds of attachment processors can be created;
|
764
|
-
a few utility examples would be compression and encryption processors.
|
765
|
-
|
766
|
-
---
|
767
|
-
|
768
833
|
Dynamic Configuration
|
769
834
|
---------------------
|
770
835
|
|
@@ -786,7 +851,7 @@ look as follows where a boss will receive a `300x300` thumbnail otherwise a
|
|
786
851
|
|
787
852
|
```ruby
|
788
853
|
class User < ActiveRecord::Base
|
789
|
-
has_attached_file :avatar, :
|
854
|
+
has_attached_file :avatar, styles: lambda { |attachment| { thumb: (attachment.instance.boss? ? "300x300>" : "100x100>") } }
|
790
855
|
end
|
791
856
|
```
|
792
857
|
|
@@ -803,7 +868,7 @@ processors, where a defined `watermark` processor is invoked after the
|
|
803
868
|
|
804
869
|
```ruby
|
805
870
|
class User < ActiveRecord::Base
|
806
|
-
has_attached_file :avatar, :
|
871
|
+
has_attached_file :avatar, processors: lambda { |instance| instance.processors }
|
807
872
|
attr_accessor :processors
|
808
873
|
end
|
809
874
|
```
|
@@ -813,7 +878,7 @@ end
|
|
813
878
|
Logging
|
814
879
|
----------
|
815
880
|
|
816
|
-
By default Paperclip outputs logging according to your logger level. If you want to disable logging (e.g. during testing) add this
|
881
|
+
By default, Paperclip outputs logging according to your logger level. If you want to disable logging (e.g. during testing) add this into your environment's configuration:
|
817
882
|
```ruby
|
818
883
|
Your::Application.configure do
|
819
884
|
...
|
@@ -822,13 +887,23 @@ Your::Application.configure do
|
|
822
887
|
end
|
823
888
|
```
|
824
889
|
|
825
|
-
More information in the [rdocs](http://
|
890
|
+
More information in the [rdocs](http://www.rubydoc.info/github/thoughtbot/paperclip/Paperclip.options)
|
826
891
|
|
827
892
|
---
|
828
893
|
|
829
894
|
Deployment
|
830
895
|
----------
|
831
896
|
|
897
|
+
To make Capistrano symlink the `public/system` directory so that attachments
|
898
|
+
survive new deployments, set the `linked_dirs` option in your `config/deploy.rb`
|
899
|
+
file:
|
900
|
+
|
901
|
+
```ruby
|
902
|
+
set :linked_dirs, fetch(:linked_dirs, []).push('public/system')
|
903
|
+
```
|
904
|
+
|
905
|
+
### Attachment Styles
|
906
|
+
|
832
907
|
Paperclip is aware of new attachment styles you have added in previous deploys. The only thing you should do after each deployment is to call
|
833
908
|
`rake paperclip:refresh:missing_styles`. It will store current attachment styles in `RAILS_ROOT/public/system/paperclip_attachments.yml`
|
834
909
|
by default. You can change it by:
|
@@ -840,9 +915,9 @@ Paperclip.registered_attachments_styles_path = '/tmp/config/paperclip_attachment
|
|
840
915
|
Here is an example for Capistrano:
|
841
916
|
|
842
917
|
```ruby
|
843
|
-
namespace :
|
918
|
+
namespace :paperclip do
|
844
919
|
desc "build missing paperclip styles"
|
845
|
-
task :
|
920
|
+
task :build_missing_styles do
|
846
921
|
on roles(:app) do
|
847
922
|
within release_path do
|
848
923
|
with rails_env: fetch(:rails_env) do
|
@@ -853,7 +928,7 @@ namespace :deploy do
|
|
853
928
|
end
|
854
929
|
end
|
855
930
|
|
856
|
-
after("deploy:compile_assets", "
|
931
|
+
after("deploy:compile_assets", "paperclip:build_missing_styles")
|
857
932
|
```
|
858
933
|
|
859
934
|
Now you don't have to remember to refresh thumbnails in production every time you add a new style.
|
@@ -864,12 +939,12 @@ Paperclip about existing styles. Simply create a `paperclip_attachments.yml` fil
|
|
864
939
|
|
865
940
|
```ruby
|
866
941
|
class User < ActiveRecord::Base
|
867
|
-
has_attached_file :avatar, :
|
942
|
+
has_attached_file :avatar, styles: { thumb: 'x100', croppable: '600x600>', big: '1000x1000>' }
|
868
943
|
end
|
869
944
|
|
870
945
|
class Book < ActiveRecord::Base
|
871
|
-
has_attached_file :cover, :
|
872
|
-
has_attached_file :sample, :
|
946
|
+
has_attached_file :cover, styles: { small: 'x100', large: '1000x1000>' }
|
947
|
+
has_attached_file :sample, styles: { thumb: 'x100' }
|
873
948
|
end
|
874
949
|
```
|
875
950
|
|
@@ -896,7 +971,7 @@ Testing
|
|
896
971
|
-------
|
897
972
|
|
898
973
|
Paperclip provides rspec-compatible matchers for testing attachments. See the
|
899
|
-
documentation on [Paperclip::Shoulda::Matchers](http://rubydoc.info/gems/paperclip/Paperclip/Shoulda/Matchers)
|
974
|
+
documentation on [Paperclip::Shoulda::Matchers](http://www.rubydoc.info/gems/paperclip/Paperclip/Shoulda/Matchers)
|
900
975
|
for more information.
|
901
976
|
|
902
977
|
**Parallel Tests**
|
@@ -920,7 +995,7 @@ similar mechanism for whichever parallel testing library you use.
|
|
920
995
|
|
921
996
|
**Integration Tests**
|
922
997
|
|
923
|
-
Using integration tests with
|
998
|
+
Using integration tests with FactoryBot may save multiple copies of
|
924
999
|
your test files within the app. To avoid this, specify a custom path in
|
925
1000
|
the `config/environments/test.rb` like so:
|
926
1001
|
|
@@ -936,6 +1011,17 @@ config.after(:suite) do
|
|
936
1011
|
FileUtils.rm_rf(Dir["#{Rails.root}/spec/test_files/"])
|
937
1012
|
end
|
938
1013
|
```
|
1014
|
+
|
1015
|
+
**Example of test configuration with Factory Bot**
|
1016
|
+
|
1017
|
+
|
1018
|
+
```ruby
|
1019
|
+
FactoryBot.define do
|
1020
|
+
factory :user do
|
1021
|
+
avatar { File.new("#{Rails.root}/spec/support/fixtures/image.jpg") }
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
```
|
939
1025
|
---
|
940
1026
|
|
941
1027
|
Contributing
|
@@ -951,20 +1037,20 @@ guidelines:
|
|
951
1037
|
about writing tests for paperclip, please open a
|
952
1038
|
[GitHub issue](https://github.com/thoughtbot/paperclip/issues/new).
|
953
1039
|
|
954
|
-
Please see `CONTRIBUTING.md` for more details on contributing and running test.
|
1040
|
+
Please see [`CONTRIBUTING.md`](./CONTRIBUTING.md) for more details on contributing and running test.
|
955
1041
|
|
956
|
-
Thank you to all [the contributors](https://github.com/thoughtbot/paperclip/contributors)!
|
1042
|
+
Thank you to all [the contributors](https://github.com/thoughtbot/paperclip/graphs/contributors)!
|
957
1043
|
|
958
1044
|
License
|
959
1045
|
-------
|
960
1046
|
|
961
|
-
Paperclip is Copyright © 2008-
|
1047
|
+
Paperclip is Copyright © 2008-2017 thoughtbot, inc. It is free software, and may be
|
962
1048
|
redistributed under the terms specified in the MIT-LICENSE file.
|
963
1049
|
|
964
1050
|
About thoughtbot
|
965
1051
|
----------------
|
966
1052
|
|
967
|
-
![thoughtbot](
|
1053
|
+
![thoughtbot](http://presskit.thoughtbot.com/images/thoughtbot-logo-for-readmes.svg)
|
968
1054
|
|
969
1055
|
Paperclip is maintained and funded by thoughtbot.
|
970
1056
|
The names and logos for thoughtbot are trademarks of thoughtbot, inc.
|