shrine 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +143 -84
- data/doc/carrierwave.md +187 -47
- data/doc/direct_s3.md +57 -39
- data/doc/paperclip.md +183 -91
- data/doc/refile.md +148 -124
- data/doc/regenerating_versions.md +2 -3
- data/lib/shrine.rb +26 -28
- data/lib/shrine/plugins/activerecord.rb +22 -31
- data/lib/shrine/plugins/add_metadata.rb +1 -1
- data/lib/shrine/plugins/backgrounding.rb +19 -7
- data/lib/shrine/plugins/backup.rb +2 -2
- data/lib/shrine/plugins/cached_attachment_data.rb +1 -1
- data/lib/shrine/plugins/copy.rb +52 -0
- data/lib/shrine/plugins/data_uri.rb +1 -1
- data/lib/shrine/plugins/default_storage.rb +2 -2
- data/lib/shrine/plugins/default_url.rb +1 -1
- data/lib/shrine/plugins/default_url_options.rb +1 -1
- data/lib/shrine/plugins/delete_promoted.rb +1 -1
- data/lib/shrine/plugins/delete_raw.rb +1 -1
- data/lib/shrine/plugins/determine_mime_type.rb +3 -2
- data/lib/shrine/plugins/direct_upload.rb +36 -24
- data/lib/shrine/plugins/download_endpoint.rb +3 -3
- data/lib/shrine/plugins/dynamic_storage.rb +2 -2
- data/lib/shrine/plugins/hooks.rb +1 -1
- data/lib/shrine/plugins/included.rb +3 -4
- data/lib/shrine/plugins/keep_files.rb +1 -1
- data/lib/shrine/plugins/logging.rb +1 -1
- data/lib/shrine/plugins/module_include.rb +1 -1
- data/lib/shrine/plugins/moving.rb +10 -5
- data/lib/shrine/plugins/multi_delete.rb +2 -2
- data/lib/shrine/plugins/parallelize.rb +2 -2
- data/lib/shrine/plugins/parsed_json.rb +1 -1
- data/lib/shrine/plugins/pretty_location.rb +1 -1
- data/lib/shrine/plugins/processing.rb +11 -9
- data/lib/shrine/plugins/rack_file.rb +1 -1
- data/lib/shrine/plugins/recache.rb +14 -4
- data/lib/shrine/plugins/remote_url.rb +1 -1
- data/lib/shrine/plugins/remove_attachment.rb +3 -4
- data/lib/shrine/plugins/remove_invalid.rb +1 -1
- data/lib/shrine/plugins/restore_cached_data.rb +11 -4
- data/lib/shrine/plugins/sequel.rb +34 -45
- data/lib/shrine/plugins/store_dimensions.rb +1 -1
- data/lib/shrine/plugins/upload_options.rb +2 -2
- data/lib/shrine/plugins/validation_helpers.rb +7 -8
- data/lib/shrine/plugins/versions.rb +31 -30
- data/lib/shrine/storage/file_system.rb +16 -12
- data/lib/shrine/storage/s3.rb +36 -2
- data/lib/shrine/version.rb +1 -1
- data/shrine.gemspec +9 -8
- metadata +11 -9
data/doc/direct_s3.md
CHANGED
@@ -1,13 +1,30 @@
|
|
1
1
|
# Direct Uploads to S3
|
2
2
|
|
3
|
-
Shrine gives you the ability to upload files directly to S3, which
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
Shrine gives you the ability to upload files directly to Amazon S3, which is
|
4
|
+
beneficial for several use cases:
|
5
|
+
|
6
|
+
* accepting uploads is resource-intensive for the server, and delegating it to
|
7
|
+
an external service makes scaling easier
|
8
|
+
|
9
|
+
* if both temporary and permanent storage are S3, promoting an S3 file to
|
10
|
+
permanent storage will simply issue an S3 copy request, without any
|
11
|
+
downloading and reuploading
|
12
|
+
|
13
|
+
* with multiple servers it's generally not possible to cache files to the disk,
|
14
|
+
unless you're using a distibuted filesystem that's shared between servers
|
15
|
+
|
16
|
+
* Heroku restricts file uploads to disk, allowing you to save files only in
|
17
|
+
the temporary folder, which gets wiped out between deploys
|
18
|
+
|
19
|
+
* Heroku has a 30-second request limit, so if the client has a slow connection
|
20
|
+
and/or your files are larger, uploads to your app can easily hit that limit
|
21
|
+
|
22
|
+
You can start by setting both temporary and permanent storage to S3 with
|
23
|
+
different prefixes (or even buckets):
|
10
24
|
|
25
|
+
```rb
|
26
|
+
gem "aws-sdk", "~> 2.1"
|
27
|
+
```
|
11
28
|
```rb
|
12
29
|
require "shrine/storage/s3"
|
13
30
|
|
@@ -21,9 +38,10 @@ Shrine.storages = {
|
|
21
38
|
|
22
39
|
## Enabling CORS
|
23
40
|
|
24
|
-
|
25
|
-
that by clicking on "Properties >
|
26
|
-
|
41
|
+
In order to be able upload files directly to your S3 bucket, you need enable
|
42
|
+
CORS. You can do that in the AWS S3 Console by clicking on "Properties >
|
43
|
+
Permissions > Add CORS Configuration", and then just follow the Amazon
|
44
|
+
documentation on how to write a CORS file.
|
27
45
|
|
28
46
|
http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html
|
29
47
|
|
@@ -32,7 +50,8 @@ DNS propagation.
|
|
32
50
|
|
33
51
|
## File hash
|
34
52
|
|
35
|
-
|
53
|
+
After direct S3 uploads we'll need to manually construct Shrine's
|
54
|
+
representation of an uploaded file:
|
36
55
|
|
37
56
|
```rb
|
38
57
|
{
|
@@ -46,10 +65,9 @@ Shrine's JSON representation of an uploaded file looks like this:
|
|
46
65
|
}
|
47
66
|
```
|
48
67
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
JSON, and then you can assign it to the hidden attachment field in the form.
|
68
|
+
* `id` – location of the file on S3 (minus the `:prefix`)
|
69
|
+
* `storage` – direct uploads typically use the `:cache` storage
|
70
|
+
* `metadata` – hash of metadata extracted from the file
|
53
71
|
|
54
72
|
## Strategy A (dynamic)
|
55
73
|
|
@@ -57,17 +75,20 @@ JSON, and then you can assign it to the hidden attachment field in the form.
|
|
57
75
|
* Single or multiple file uploads
|
58
76
|
* Some JavaScript needed
|
59
77
|
|
60
|
-
When the user selects the file, we dynamically
|
78
|
+
When the user selects the file, we dynamically fetch the presign from the
|
61
79
|
server, and use this information to start uploading the file to S3. The
|
62
|
-
direct_upload plugin gives us this presign route, so we just need to mount it
|
80
|
+
`direct_upload` plugin gives us this presign route, so we just need to mount it
|
63
81
|
in our application:
|
64
82
|
|
83
|
+
```rb
|
84
|
+
gem "roda"
|
85
|
+
```
|
65
86
|
```rb
|
66
87
|
plugin :direct_upload
|
67
88
|
```
|
68
89
|
```rb
|
69
90
|
Rails.application.routes.draw do
|
70
|
-
mount ImageUploader::UploadEndpoint => "/
|
91
|
+
mount ImageUploader::UploadEndpoint => "/images"
|
71
92
|
end
|
72
93
|
```
|
73
94
|
|
@@ -91,27 +112,24 @@ necessary request parameters:
|
|
91
112
|
```
|
92
113
|
|
93
114
|
For uploading to S3 you'll probably want to use a JavaScript file upload
|
94
|
-
library like [jQuery-File-Upload] or [
|
95
|
-
create a JSON representation of the uploaded file, which you
|
96
|
-
the hidden attachment field:
|
97
|
-
|
98
|
-
```
|
99
|
-
|
100
|
-
id:
|
101
|
-
storage:
|
102
|
-
metadata: {
|
103
|
-
size:
|
104
|
-
filename:
|
105
|
-
mime_type:
|
115
|
+
library like [jQuery-File-Upload], [Dropzone] or [FineUploader]. After the
|
116
|
+
upload you should create a JSON representation of the uploaded file, which you
|
117
|
+
can write to the hidden attachment field:
|
118
|
+
|
119
|
+
```html
|
120
|
+
<input type='hidden' name='photo[image]' value='{
|
121
|
+
"id": "302858ldg9agjad7f3ls.jpg",
|
122
|
+
"storage": "cache",
|
123
|
+
"metadata": {
|
124
|
+
"size": 943483,
|
125
|
+
"filename": "nature.jpg",
|
126
|
+
"mime_type": "image/jpeg",
|
106
127
|
}
|
107
|
-
}
|
108
|
-
|
109
|
-
$('input[type=file]').prev().val(JSON.stringify(image))
|
128
|
+
}'>
|
110
129
|
```
|
111
130
|
|
112
|
-
|
113
|
-
|
114
|
-
working implementation of multiple direct S3 uploads.
|
131
|
+
See the [demo app] for an example JavaScript implementation of multiple direct
|
132
|
+
S3 uploads.
|
115
133
|
|
116
134
|
## Strategy B (static)
|
117
135
|
|
@@ -163,7 +181,7 @@ caching the file doesn't touch your application. When the cached file is stored,
|
|
163
181
|
Shrine's default behaviour is to simply copy over cached file's metadata.
|
164
182
|
|
165
183
|
If you want to extract metadata on the server before storing, you can just
|
166
|
-
load the restore_cached_data plugin.
|
184
|
+
load the `restore_cached_data` plugin.
|
167
185
|
|
168
186
|
```rb
|
169
187
|
plugin :restore_cached_data
|
@@ -201,12 +219,12 @@ backgrounding library to perform the job with a delay:
|
|
201
219
|
Shrine.plugin :backgrounding
|
202
220
|
|
203
221
|
Shrine::Attacher.promote do |data|
|
204
|
-
PromoteJob.perform_in(
|
222
|
+
PromoteJob.perform_in(3, data) # tells a Sidekiq worker to perform in 3 seconds
|
205
223
|
end
|
206
224
|
```
|
207
225
|
|
208
226
|
[`Aws::S3::PresignedPost`]: http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Bucket.html#presigned_post-instance_method
|
209
|
-
[
|
227
|
+
[demo app]: https://github.com/janko-m/shrine/tree/master/demo
|
210
228
|
[Dropzone]: https://github.com/enyo/dropzone
|
211
229
|
[jQuery-File-Upload]: https://github.com/blueimp/jQuery-File-Upload
|
212
230
|
[Amazon S3 Data Consistency Model]: http://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html#ConsistencyMode
|
data/doc/paperclip.md
CHANGED
@@ -1,158 +1,251 @@
|
|
1
1
|
# Shrine for Paperclip Users
|
2
2
|
|
3
|
-
This guide is aimed at helping Paperclip users transition to Shrine
|
4
|
-
|
5
|
-
complete reference of Paperclip's interface and what is the equivalent in
|
6
|
-
Shrine.
|
3
|
+
This guide is aimed at helping Paperclip users transition to Shrine, and it
|
4
|
+
consists of three parts:
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
your models, in Shrine you instead have "uploader" classes where you put all
|
12
|
-
your uploading logic.
|
6
|
+
1. Explanation of the key differences in design between Paperclip and Shrine
|
7
|
+
2. Instructions how to migrate and existing app that uses Paperclip to Shrine
|
8
|
+
3. Extensive reference of Paperclip's interface with Shrine equivalents
|
13
9
|
|
14
|
-
|
15
|
-
class ImageUploader < Shrine
|
16
|
-
plugin :validation_helpers
|
10
|
+
## Storages
|
17
11
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
12
|
+
While in Paperclip you configure storage in the model, a Shrine storage is just
|
13
|
+
a class which you configure individually:
|
21
14
|
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
```rb
|
16
|
+
class Photo < ActiveRecord::Base
|
17
|
+
has_attached_file :image,
|
18
|
+
storage: :s3,
|
19
|
+
s3_credentials: {
|
20
|
+
bucket: "my-bucket",
|
21
|
+
access_key_id: "abc",
|
22
|
+
secret_access_key: "xyz",
|
23
|
+
},
|
24
|
+
s3_host_alias: "http://abc123.cloudfront.net",
|
25
25
|
end
|
26
26
|
```
|
27
|
+
```rb
|
28
|
+
Shrine.storages[:store] = Shrine::Storage::S3.new(
|
29
|
+
bucket: "my-bucket",
|
30
|
+
access_key_id: "abc",
|
31
|
+
secret_access_key: "xyz",
|
32
|
+
host: "http://abc123.cloudfront.net",
|
33
|
+
)
|
34
|
+
```
|
27
35
|
|
28
|
-
|
29
|
-
|
30
|
-
|
36
|
+
Paperclip doesn't have a concept of "temporary" storage, so it cannot retain
|
37
|
+
uploaded files in case of validation errors, and [direct S3 uploads] cannot be
|
38
|
+
implemented in a safe way. Shrine conceptually separates a "temporary" and
|
39
|
+
"permanent" storage:
|
31
40
|
|
32
41
|
```rb
|
33
|
-
require "shrine/storage/file_system"
|
34
|
-
|
35
42
|
Shrine.storages = {
|
36
43
|
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
|
37
|
-
store: Shrine::Storage::
|
44
|
+
store: Shrine::Storage::S3.new(bucket: "my-bucket", **s3_options),
|
38
45
|
}
|
39
46
|
```
|
47
|
+
|
48
|
+
## Uploaders
|
49
|
+
|
50
|
+
While in Paperclip you define all your uploading logic inside your models,
|
51
|
+
Shrine takes a more object-oriented approach and lets you define uploading logic
|
52
|
+
inside "uploader" classes:
|
53
|
+
|
54
|
+
```rb
|
55
|
+
class Photo < ActiveRecord::Base
|
56
|
+
has_attached_file :image
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
```rb
|
61
|
+
class ImageUploader < Shrine
|
62
|
+
# ...
|
63
|
+
end
|
64
|
+
|
65
|
+
class Photo < ActiveRecord::Base
|
66
|
+
include ImageUploader[:image]
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Among other things, this allows you to use uploader classes standalone, which
|
71
|
+
gives you more power:
|
72
|
+
|
40
73
|
```rb
|
41
|
-
uploader =
|
74
|
+
uploader = ImageUploader.new(:store)
|
42
75
|
uploaded_file = uploader.upload(File.open("nature.jpg"))
|
43
|
-
uploaded_file
|
44
|
-
uploaded_file.
|
76
|
+
uploaded_file #=> #<Shrine::UploadedFile>
|
77
|
+
uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/store/kfds0lg9rer.jpg"
|
45
78
|
```
|
46
79
|
|
47
80
|
### Processing
|
48
81
|
|
49
|
-
|
50
|
-
instance-level
|
51
|
-
|
82
|
+
In contrast to Paperclip's static options, in Shrine you define and perform
|
83
|
+
processing on instance-level. The result of processing can be a single file
|
84
|
+
or a hash of versions:
|
52
85
|
|
53
86
|
```rb
|
54
|
-
|
87
|
+
class Photo < ActiveRecord::Base
|
88
|
+
has_attached_file :image,
|
89
|
+
styles: {
|
90
|
+
large: "800x800>",
|
91
|
+
medium: "500x500>",
|
92
|
+
small: "300x300>",
|
93
|
+
}
|
94
|
+
end
|
95
|
+
```
|
55
96
|
|
97
|
+
```rb
|
56
98
|
class ImageUploader < Shrine
|
57
99
|
include ImageProcessing::MiniMagick
|
58
100
|
plugin :processing
|
59
101
|
plugin :versions
|
60
102
|
|
61
103
|
process(:store) do |io, context|
|
62
|
-
|
63
|
-
|
104
|
+
size_800 = resize_to_limit(io.download, 800, 800)
|
105
|
+
size_500 = resize_to_limit(size_800, 500, 500)
|
106
|
+
size_300 = resize_to_limit(size_500, 300, 300)
|
107
|
+
|
108
|
+
{large: size_800, medium: size_500, small: size_300}
|
64
109
|
end
|
65
110
|
end
|
66
111
|
```
|
67
112
|
|
68
|
-
|
113
|
+
This allows you to fully optimize processing, because you can easily specify
|
114
|
+
which files are processed from which, and even add parallelization.
|
69
115
|
|
70
|
-
|
71
|
-
very individual and depends on what versions you want regenerated, what ORM are
|
72
|
-
you using, how many records there are in your database etc. The [Regenerating
|
73
|
-
versions] guide provides some useful tips on this task.
|
116
|
+
#### Reprocessing versions
|
74
117
|
|
75
|
-
|
118
|
+
Shrine doesn't have a built-in way of regenerating versions, because that has
|
119
|
+
to be written and optimized differently depending on whether you're adding or
|
120
|
+
removing a version, what ORM are you using, how many records there are in the
|
121
|
+
database etc. The [Reprocessing versions] guide provides some useful tips on
|
122
|
+
this task.
|
76
123
|
|
77
|
-
|
78
|
-
|
124
|
+
### Validations
|
125
|
+
|
126
|
+
Validations are also defined inside the uploader on the instance-level, which
|
127
|
+
allows you to do conditional validations:
|
79
128
|
|
80
129
|
```rb
|
81
|
-
|
130
|
+
class Photo < ActiveRecord::Base
|
131
|
+
has_attached_file :image
|
132
|
+
validates_attachment :image,
|
133
|
+
content_type: {content_type: %w[image/jpeg image/png image/gif]},
|
134
|
+
size: {in: 0..10.megabytes}
|
135
|
+
end
|
82
136
|
```
|
83
137
|
|
84
|
-
|
138
|
+
```rb
|
139
|
+
class ImageUploader < Shrine
|
140
|
+
plugin :validation_helpers
|
141
|
+
|
142
|
+
Attacher.validate do
|
143
|
+
validate_mime_type_inclusion %w[image/jpeg image/gif image/png]
|
144
|
+
validate_max_size 10*1024*1024 unless record.admin?
|
145
|
+
end
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
#### MIME type spoofing
|
150
|
+
|
151
|
+
Paperclip detects MIME type spoofing, in the way that it extracts the MIME type
|
152
|
+
from file contents using the `file` command and MimeMagic, compares it to the
|
153
|
+
value that the `mime-types` gem determined from file extension, and raises a
|
154
|
+
validation error if these two values mismatch.
|
85
155
|
|
86
|
-
|
87
|
-
|
88
|
-
|
156
|
+
However, this turned out to be very problematic, leading to a lot of valid
|
157
|
+
files being classified as "spoofed", because of the differences of MIME
|
158
|
+
type databases between the `mime-types` gem, `file` command, and MimeMagic.
|
159
|
+
|
160
|
+
Shrine takes a different approach here. By default it will extract MIME
|
161
|
+
type from file extension, but it has a plugin for determining MIME type from
|
162
|
+
file contents, which by default uses the `file` command:
|
89
163
|
|
90
164
|
```rb
|
91
|
-
Shrine.plugin :
|
92
|
-
Shrine.plugin :activerecord # If you're using ActiveRecord
|
165
|
+
Shrine.plugin :determine_mime_type
|
93
166
|
```
|
94
167
|
|
95
|
-
|
96
|
-
|
168
|
+
However, it doesn't try to compare this value with the one from file extension,
|
169
|
+
it just means that now this value will be used for your MIME type validations.
|
170
|
+
With this approach you can still prevent malicious files from being attached,
|
171
|
+
but without the possibility of false negatives.
|
172
|
+
|
173
|
+
### Logging
|
174
|
+
|
175
|
+
In Paperclip you enable logging by setting `Paperclip.options[:log] = true`,
|
176
|
+
however, this only logs ImageMagick commands. Shrine has full logging support,
|
177
|
+
which measures processing, uploading and deleting individually, along with
|
178
|
+
context for debugging:
|
97
179
|
|
98
180
|
```rb
|
99
|
-
|
100
|
-
|
101
|
-
|
181
|
+
Shrine.plugin :logging
|
182
|
+
```
|
183
|
+
```
|
184
|
+
2015-10-09T20:06:06.676Z #25602: STORE[cache] ImageUploader[:avatar] User[29543] 1 file (0.1s)
|
185
|
+
2015-10-09T20:06:06.854Z #25602: PROCESS[store]: ImageUploader[:avatar] User[29543] 1-3 files (0.22s)
|
186
|
+
2015-10-09T20:06:07.133Z #25602: DELETE[destroyed]: ImageUploader[:avatar] User[29543] 3 files (0.07s)
|
102
187
|
```
|
103
188
|
|
104
|
-
|
105
|
-
Shrine you only need to have an `<attachment>_data` text column, and all
|
106
|
-
information will be stored there (in the above case `avatar_data`).
|
189
|
+
## Attachments
|
107
190
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
backgrounding you can show the users the cached version before the file is
|
112
|
-
finished storing.
|
191
|
+
While Paperclip is designed to only integrate with ActiveRecord, Shrine is
|
192
|
+
designed to be completely generic and integrate with any ORM. It ships with
|
193
|
+
plugins for ActiveRecord and Sequel:
|
113
194
|
|
114
|
-
|
195
|
+
```rb
|
196
|
+
Shrine.plugin :activerecord # if you're using ActiveRecord
|
197
|
+
Shrine.plugin :sequel # if you're using Sequel
|
198
|
+
```
|
115
199
|
|
116
|
-
|
117
|
-
|
200
|
+
Instead of giving you class methods for defining attachments, in Shrine you
|
201
|
+
generate attachment modules which you simply include in your models, which
|
202
|
+
gives your models similar set of methods that Paperclip gives:
|
118
203
|
|
119
204
|
```rb
|
120
|
-
class
|
121
|
-
|
122
|
-
|
123
|
-
Attacher.validate do
|
124
|
-
validate_max_size 5*1024*1024
|
125
|
-
validate_mime_type_inclusion [/^image/]
|
126
|
-
end
|
205
|
+
class Photo < Sequel::Model
|
206
|
+
include ImageUploader[:image]
|
127
207
|
end
|
128
208
|
```
|
129
209
|
|
130
|
-
|
210
|
+
### Attachment column
|
211
|
+
|
212
|
+
Unlike in Paperclip which requires you to have 4 `<attachment>_*` columns, in
|
213
|
+
Shrine you only need to have a single `<attachment>_data` text column (in the
|
214
|
+
above case `image_data`), and all information will be stored there.
|
131
215
|
|
132
216
|
```rb
|
133
|
-
|
134
|
-
|
217
|
+
photo.image_data #=>
|
218
|
+
# {
|
219
|
+
# "storage" => "store",
|
220
|
+
# "id" => "photo/1/image/0d9o8dk42.png",
|
221
|
+
# "metadata" => {
|
222
|
+
# "filename" => "nature.png",
|
223
|
+
# "size" => 49349138,
|
224
|
+
# "mime_type" => "image/png"
|
225
|
+
# }
|
226
|
+
# }
|
135
227
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
228
|
+
photo.image.original_filename #=> "nature.png"
|
229
|
+
photo.image.size #=> 49349138
|
230
|
+
photo.image.mime_type #=> "image/png"
|
140
231
|
```
|
141
232
|
|
142
|
-
|
143
|
-
|
144
|
-
By default Shrine will extract the MIME type from the `Content-Type` header of
|
145
|
-
the uploaded file, which is solely determined from the file extension, so it's
|
146
|
-
prone to spoofing. Shrine provides the `determine_mime_type` plugin which
|
147
|
-
determines the MIME type from the file *contents* instead:
|
233
|
+
Unlike Paperclip, Shrine will store this information for each processed
|
234
|
+
version, making them first-class citizens:
|
148
235
|
|
149
236
|
```rb
|
150
|
-
|
237
|
+
photo.image[:original] #=> #<Shrine::UploadedFile>
|
238
|
+
photo.image[:original].width #=> 800
|
239
|
+
|
240
|
+
photo.image[:thumb] #=> #<Shrine::UploadedFile>
|
241
|
+
photo.image[:thumb].width #=> 300
|
151
242
|
```
|
152
243
|
|
153
|
-
|
154
|
-
|
155
|
-
|
244
|
+
Also, since Paperclip stores only the filename, it has to recalculate the full
|
245
|
+
location each time it wants to generate the URL. That makes it really difficult
|
246
|
+
to move files to a new location, because changing how the location is generated
|
247
|
+
will now cause incorrect URLs to be generated for all existing files. Shrine
|
248
|
+
calculates the whole location only once and saves it to the column.
|
156
249
|
|
157
250
|
### Hooks/Callbacks
|
158
251
|
|
@@ -312,8 +405,6 @@ In Shrine attachments will automatically use `:cache` and `:store` storages
|
|
312
405
|
which you have to register:
|
313
406
|
|
314
407
|
```rb
|
315
|
-
require "shrine/storage/file_system"
|
316
|
-
|
317
408
|
Shrine.storages = {
|
318
409
|
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
|
319
410
|
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
|
@@ -411,4 +502,5 @@ Shrine doesn't have an equivalent to this, but the [Regenerating versions]
|
|
411
502
|
guide provides some useful tips on how to do this.
|
412
503
|
|
413
504
|
[file]: http://linux.die.net/man/1/file
|
414
|
-
[
|
505
|
+
[Reprocessing versions]: http://shrinerb.com/rdoc/files/doc/regenerating_versions_md.html
|
506
|
+
[direct S3 uploads]: http://shrinerb.com/rdoc/files/doc/direct_s3_md.html
|