shrine 2.17.1 → 2.18.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.

data/doc/advantages.md CHANGED
@@ -84,9 +84,7 @@ Storage provides, you can ditch the Shrine's attachment implementation and use
84
84
  uploaders and uploaded files that are decoupled from attachment:
85
85
 
86
86
  ```rb
87
- uploader = ImageUploader.new(:store)
88
- uploaded_file = uploader.upload(image) # metadata extraction, upload location generation
89
-
87
+ uploaded_file = ImageUploader.upload(image, :store) # metadata extraction, upload location generation
90
88
  uploaded_file.id #=> "44ccafc10ce6a4ff22829e8f579ee6b9.jpg"
91
89
  uplaoded_file.metadata #=> { ... extracted metadata ... }
92
90
 
data/doc/attacher.md CHANGED
@@ -11,7 +11,7 @@ class Photo < Sequel::Model
11
11
  end
12
12
  ```
13
13
 
14
- However, you don't want to add additional methods on the model and prefer
14
+ However, if you don't want to add additional methods on the model and prefer
15
15
  explicitness, or you need more control, you can achieve the same behaviour
16
16
  using the `Shrine::Attacher` object, which is what the attachment interface
17
17
  uses under the hood.
@@ -22,7 +22,14 @@ attacher.assign(file) # equivalent to `photo.ima
22
22
  attacher.get # equivalent to `photo.image`
23
23
  ```
24
24
 
25
- ## Attributes
25
+ The attacher will use the `<attachment>_data` attribute for storing information
26
+ about the attachment.
27
+
28
+ ```rb
29
+ attacher.data_attribute #=> :image_data
30
+ ```
31
+
32
+ ## Initializing
26
33
 
27
34
  The attacher object exposes the objects it uses:
28
35
 
@@ -38,9 +45,15 @@ also tell it to use different temporary and permanent storage:
38
45
 
39
46
  ```rb
40
47
  ImageUploader::Attacher.new(photo, :image, cache: :other_cache, store: :other_store)
48
+
49
+ # OR
50
+
51
+ photo.image_attacher(cache: :other_cache, store: :other_store)
52
+ photo.image = file # uploads to :other_cache storage
53
+ photo.save # promotes to :other_store storage
41
54
  ```
42
55
 
43
- Note that you can pass the `:cache` and `:store` options via `Attachment.new` too:
56
+ You can pass the `:cache` and `:store` options via `Attachment.new` too:
44
57
 
45
58
  ```rb
46
59
  class Photo < Sequel::Model
@@ -48,12 +61,8 @@ class Photo < Sequel::Model
48
61
  end
49
62
  ```
50
63
 
51
- The attacher will use the `<attachment>_data` attribute for storing information
52
- about the attachment.
53
-
54
- ```rb
55
- attacher.data_attribute #=> :image_data
56
- ```
64
+ Note that it's not necessary to use the temporary storage, see the next section
65
+ for more details.
57
66
 
58
67
  ## Assignment
59
68
 
@@ -92,10 +101,12 @@ attacher.assign(cached_file.to_json)
92
101
 
93
102
  For security reasons `#assign` doesn't accept files uploaded to permanent
94
103
  storage, but you can use `#set` to attach any `Shrine::UploadedFile` object.
104
+ You can use this to skip temporary storage altogether and upload files directly
105
+ to permanent storage:
95
106
 
96
107
  ```rb
97
- uploaded_file #=> #<Shrine::UploadedFile>
98
- attacher.set(uploaded_file)
108
+ uploaded_file = attacher.store!(file) # upload a file directly to permanent storage
109
+ attacher.set(uploaded_file) # attach the uploaded file
99
110
  ```
100
111
 
101
112
  ## Retrieval
data/doc/carrierwave.md CHANGED
@@ -62,8 +62,7 @@ deleting files, they also represent the uploaded file. Shrine has a separate
62
62
  `Shrine::UploadedFile` class which represents the uploaded file.
63
63
 
64
64
  ```rb
65
- uploader = ImageUploader.new(:store)
66
- uploaded_file = uploader.upload(image)
65
+ uploaded_file = ImageUploader.upload(file, :store)
67
66
  uploaded_file #=> #<Shrine::UploadedFile>
68
67
  uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/store/kfds0lg9rer.jpg"
69
68
  uploaded_file.download #=> #<Tempfile>
@@ -381,12 +380,12 @@ end
381
380
 
382
381
  #### `#store!`, `#cache!`
383
382
 
384
- In Shrine you store and cache files by instantiating it with a corresponding
385
- storage, and calling `#upload`:
383
+ In Shrine you store and cache files by passing the corresponding storage to
384
+ `Shrine.upload`:
386
385
 
387
386
  ```rb
388
- ImageUploader.new(:cache).upload(file)
389
- ImageUploader.new(:store).upload(file)
387
+ ImageUploader.upload(file, :cache)
388
+ ImageUploader.upload(file, :store)
390
389
  ```
391
390
 
392
391
  Note that in Shrine you cannot pass in a path to the file, you always have to
@@ -398,8 +397,8 @@ pass an IO-like object, which is required to respond to: `#read(*args)`,
398
397
  In Shrine you simply call `#download` on the uploaded file:
399
398
 
400
399
  ```rb
401
- uploaded_file = ImageUploader.new(:store).upload(file)
402
- uploaded_file.download #=> #<Tempfile>
400
+ uploaded_file = ImageUploader.upload(file, :store)
401
+ uploaded_file.download #=> #<Tempfile:/path/to/file>
403
402
  ```
404
403
 
405
404
  #### `#url`
data/doc/design.md CHANGED
@@ -66,7 +66,7 @@ name:
66
66
  Shrine.storages[:file_system] = Shrine::Storage::FileSystem.new("uploads")
67
67
  ```
68
68
 
69
- Now we can instantiate an uploader with this identifier, and upload files:
69
+ Now we can instantiate an uploader with this identifier and upload files:
70
70
 
71
71
  ```rb
72
72
  uploader = Shrine.new(:file_system)
data/doc/direct_s3.md CHANGED
@@ -29,7 +29,7 @@ temporary storage uploading to the `cache/` prefix:
29
29
  ```rb
30
30
  # Gemfile
31
31
  gem "shrine", "~> 2.11"
32
- gem "aws-sdk-s3", "~> 1.2"
32
+ gem "aws-sdk-s3", "~> 1.14"
33
33
  ```
34
34
  ```rb
35
35
  require "shrine/storage/s3"
@@ -55,7 +55,7 @@ default. You can do that from the AWS S3 Console by going to your bucket,
55
55
  clicking on the "Permissions" tab and then on "CORS Configuration".
56
56
 
57
57
  If you're using [Uppy], this is the recommended CORS configuration for the
58
- [Aws S3 plugin] that should work for both POST and PUT uploads:
58
+ [AWS S3 plugin][uppy aws-s3] that should work for both POST and PUT uploads:
59
59
 
60
60
  ```xml
61
61
  <?xml version="1.0" encoding="UTF-8"?>
@@ -70,6 +70,7 @@ If you're using [Uppy], this is the recommended CORS configuration for the
70
70
  <AllowedHeader>x-amz-date</AllowedHeader>
71
71
  <AllowedHeader>x-amz-content-sha256</AllowedHeader>
72
72
  <AllowedHeader>content-type</AllowedHeader>
73
+ <AllowedHeader>content-disposition</AllowedHeader>
73
74
  </CORSRule>
74
75
  <CORSRule>
75
76
  <AllowedOrigin>*</AllowedOrigin>
@@ -114,13 +115,6 @@ Shrine.plugin :presign_endpoint, presign_options: -> (request) {
114
115
  }
115
116
  ```
116
117
  ```rb
117
- # config.ru (Rack)
118
- map "/s3/params" do
119
- run Shrine.presign_endpoint(:cache)
120
- end
121
-
122
- # OR
123
-
124
118
  # config/routes.rb (Rails)
125
119
  Rails.application.routes.draw do
126
120
  mount Shrine.presign_endpoint(:cache) => "/s3/params"
@@ -149,7 +143,7 @@ and request headers.
149
143
  }
150
144
  ```
151
145
 
152
- Uppy's [AWS S3][uppy aws s3] plugin would then make a request to this endpoint
146
+ Uppy's [AWS S3][uppy aws-s3] plugin would then make a request to this endpoint
153
147
  and use these parameters to upload the file directly to S3. Once the file has
154
148
  been uploaded, you can generate a JSON representation of the uploaded file on
155
149
  the client side, and write it to the hidden attachment field (or send it
@@ -178,11 +172,11 @@ as the [Roda][roda demo] / [Rails][rails demo] demo app for a complete example
178
172
  of multiple direct S3 uploads.
179
173
 
180
174
  Also, if you're dealing with larger files, you may want to make the uploads
181
- resumable by using the [Aws S3 Multipart][uppy aws s3 multipart] Uppy plugin
175
+ resumable by using the [AWS S3 Multipart][uppy aws-s3-multipart] Uppy plugin
182
176
  instead, with the [uppy-s3_multipart] gem on the backend. Your back-end
183
177
  implementation is similar, just using `Shrine.uppy_s3_multipart` in place of
184
- `Shrine.presign_endpoint`. Instructions can be found in uppy-s3_multipart
185
- README.
178
+ `Shrine.presign_endpoint`. Instructions can be found in the [gem
179
+ docs][uppy-s3_multipart shrine].
186
180
 
187
181
  ## Strategy B (static)
188
182
 
@@ -391,10 +385,11 @@ setup] guide.
391
385
  [roda demo]: https://github.com/shrinerb/shrine/tree/master/demo
392
386
  [rails demo]: https://github.com/erikdahlstrand/shrine-rails-example
393
387
  [Uppy]: https://uppy.io
394
- [uppy aws s3]: https://uppy.io/docs/aws-s3/
388
+ [uppy aws-s3]: https://uppy.io/docs/aws-s3/
395
389
  [uppy aws-s3 cors]: https://uppy.io/docs/aws-s3/#S3-Bucket-configuration
396
- [uppy aws s3 multipart]: https://uppy.io/docs/aws-s3/
390
+ [uppy aws-s3-multipart]: https://uppy.io/docs/aws-s3/
397
391
  [uppy-s3_multipart]: https://github.com/janko/uppy-s3_multipart
392
+ [uppy-s3_multipart shrine]: https://github.com/janko/uppy-s3_multipart#shrine
398
393
  [Amazon S3 Data Consistency Model]: http://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html#ConsistencyMode
399
394
  [CORS guide]: http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html
400
395
  [CORS API]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#put_bucket_cors-instance_method
data/doc/metadata.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # Extracting Metadata
2
2
 
3
3
  Before a file is uploaded, Shrine automatically extracts metadata from it, and
4
- stores them in the `Shrine::UploadedFile` object. By default it extracts
5
- `size`, `filename` and `mime_type`.
4
+ stores them in the `Shrine::UploadedFile` object.
6
5
 
7
6
  ```rb
8
7
  uploaded_file = uploader.upload(file)
@@ -14,6 +13,15 @@ uploaded_file.metadata #=>
14
13
  # }
15
14
  ```
16
15
 
16
+ The following metadata is extracted by default:
17
+
18
+ | Key | Default source |
19
+ | :----- | :------ |
20
+ | `filename` | extracted from `io.original_filename` or `io.path` |
21
+ | `mime_type` | extracted from `io.content_type` |
22
+ | `size` | extracted from `io.size` |
23
+
24
+
17
25
  Under the hood `Shrine#extract_metadata` is called, which you can also use
18
26
  directly to extract metadata from any IO object.
19
27
 
@@ -107,7 +115,7 @@ require "mini_magick"
107
115
  class ImageUploader < Shrine
108
116
  plugin :add_metadata
109
117
 
110
- add_metadata :exif do |io|
118
+ add_metadata :exif do |io, context|
111
119
  Shrine.with_file(io) do |file|
112
120
  begin
113
121
  MiniMagick::Image.new(file.path).exif
data/doc/paperclip.md CHANGED
@@ -73,8 +73,7 @@ Among other things, this allows you to use uploader classes standalone, which
73
73
  gives you more power:
74
74
 
75
75
  ```rb
76
- uploader = ImageUploader.new(:store)
77
- uploaded_file = uploader.upload(File.open("nature.jpg"))
76
+ uploaded_file = ImageUploader.upload(File.open("nature.jpg"), :store)
78
77
  uploaded_file #=> #<Shrine::UploadedFile>
79
78
  uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/store/kfds0lg9rer.jpg"
80
79
  ```
@@ -5,7 +5,7 @@ URL options that will be applied by default for uploaded files of specified
5
5
  storages.
6
6
 
7
7
  ```rb
8
- plugin :default_url_options, store: { download: true }
8
+ plugin :default_url_options, store: { expires_in: 24*60*60 }
9
9
  ```
10
10
 
11
11
  You can also generate the default URL options dynamically by using a block,
@@ -163,7 +163,8 @@ end
163
163
  # app/controllers/derivations_controller.rb
164
164
  class DerivationsController < ApplicationController
165
165
  def image
166
- # we can perform authentication here
166
+ # ... we can perform authentication here ...
167
+
167
168
  set_rack_response ImageUploader.derivation_response(request.env)
168
169
  end
169
170
 
@@ -12,26 +12,18 @@ mounted on.
12
12
  plugin :download_endpoint, prefix: "attachments"
13
13
  ```
14
14
 
15
- The endpoint should then be mounted on the specified prefix:
15
+ The plugin adds a `Shrine.download_endpoint` method which returns a Rack
16
+ application that handles downloads using the `rack_response` plugin. You can
17
+ run this Rack app inside your app on the prefix that you specified:
16
18
 
17
19
  ```rb
18
- # config.ru (Rack)
19
- map "/attachments" do
20
- run Shrine.download_endpoint
21
- end
22
-
23
- # OR
24
-
25
20
  # config/routes.rb (Rails)
26
21
  Rails.application.routes.draw do
22
+ # ...
27
23
  mount Shrine.download_endpoint => "/attachments"
28
24
  end
29
25
  ```
30
26
 
31
- Any uploaded file can be downloaded through this endpoint. When a file is
32
- requested, its content will be efficiently streamed from the storage into the
33
- response body.
34
-
35
27
  Links to the download endpoint are generated by calling
36
28
  `UploadedFile#download_url` instead of the usual `UploadedFile#url`.
37
29
 
@@ -70,7 +62,7 @@ plugin :download_endpoint, download_options: {
70
62
  You can also specify a proc to generate download options dynamically:
71
63
 
72
64
  ```rb
73
- plugin :download_enpdoint, download_options: -> (uploaded_file, request) {
65
+ plugin :download_endpoint, download_options: -> (uploaded_file, request) {
74
66
  {
75
67
  sse_customer_algorithm: "AES256",
76
68
  sse_customer_key: "secret_key",
@@ -8,4 +8,16 @@ assign cached files with hashes/arrays.
8
8
  plugin :parsed_json
9
9
  ```
10
10
 
11
+ ```rb
12
+ photo.image = {
13
+ "id" => "sdf90s2443.jpg",
14
+ "storage" => "cache",
15
+ "metadata" => {
16
+ "filename" => "nature.jpg",
17
+ "size" => 29475,
18
+ "mime_type" => "image/jpeg",
19
+ }
20
+ }
21
+ ```
22
+
11
23
  [parsed_json]: /lib/shrine/plugins/parsed_json.rb
@@ -17,15 +17,9 @@ a presign for the specified storage. You can run this Rack application inside
17
17
  your app:
18
18
 
19
19
  ```rb
20
- # config.ru (Rack)
21
- map "/images/presign" do
22
- run ImageUploader.presign_endpoint(:cache)
23
- end
24
-
25
- # OR
26
-
27
20
  # config/routes.rb (Rails)
28
21
  Rails.application.routes.draw do
22
+ # ...
29
23
  mount ImageUploader.presign_endpoint(:cache) => "/images/presign"
30
24
  end
31
25
  ```
@@ -55,6 +49,39 @@ single upload directly to the storage service, in JSON format.
55
49
  }
56
50
  ```
57
51
 
52
+ ## Calling from a controller
53
+
54
+ If you want to run additional code around the presign (such as authentication),
55
+ mounting the presign endpoint in your router might be limiting. You can instead
56
+ create a custom controller action and handle presign requests there using
57
+ `Shrine.presign_response`:
58
+
59
+ ```rb
60
+ # config/routes.rb (Rails)
61
+ Rails.application.routes.draw do
62
+ # ...
63
+ post "/images/presign", to: "presigns#image"
64
+ end
65
+ ```
66
+ ```rb
67
+ # app/controllers/presigns_controller.rb (Rails)
68
+ class PresignsController < ApplicationController
69
+ def image
70
+ # ... we can perform authentication here ...
71
+
72
+ set_rack_response ImageUploader.presign_response(:cache, env)
73
+ end
74
+
75
+ private
76
+
77
+ def set_rack_response((status, headers, body))
78
+ self.status = status
79
+ self.headers.merge!(headers)
80
+ self.response_body = body
81
+ end
82
+ end
83
+ ```
84
+
58
85
  ## Location
59
86
 
60
87
  By default the generated location won't have any file extension, but you can
@@ -121,10 +148,12 @@ end
121
148
 
122
149
  ## Ad-hoc options
123
150
 
124
- You can override any of the options above when creating the endpoint:
151
+ You can override any of the options above when creating the endpoint/response:
125
152
 
126
153
  ```rb
127
154
  Shrine.presign_endpoint(:cache, presign_location: "${filename}")
155
+ # or
156
+ Shrine.presign_response(:cache, env, presign_location: "${filename}")
128
157
  ```
129
158
 
130
159
  [presign_endpoint]: /lib/shrine/plugins/presign_endpoint.rb
@@ -11,18 +11,12 @@ plugin :upload_endpoint
11
11
  The plugin adds a `Shrine.upload_endpoint` method which, given a storage
12
12
  identifier, returns a Rack application that accepts multipart POST requests,
13
13
  and uploads received files to the specified storage. You can run this Rack
14
- papplication inside your app:
14
+ application inside your app:
15
15
 
16
16
  ```rb
17
- # config.ru (Rack)
18
- map "/images/upload" do
19
- run ImageUploader.upload_endpoint(:cache)
20
- end
21
-
22
- # OR
23
-
24
17
  # config/routes.rb (Rails)
25
18
  Rails.application.routes.draw do
19
+ # ...
26
20
  mount ImageUploader.upload_endpoint(:cache) => "/images/upload"
27
21
  end
28
22
  ```
@@ -52,6 +46,39 @@ This JSON string can now be assigned to an attachment attribute instead of a
52
46
  raw file. In a form it can be written to a hidden attachment field, and then it
53
47
  can be assigned as the attachment.
54
48
 
49
+ ## Calling from a controller
50
+
51
+ If you want to run additional code around the upload (such as authentication),
52
+ mounting the upload endpoint in your router might be limiting. You can instead
53
+ create a custom controller action and handle upload requests there using
54
+ `Shrine.upload_response`:
55
+
56
+ ```rb
57
+ # config/routes.rb (Rails)
58
+ Rails.application.routes.draw do
59
+ # ...
60
+ post "/images/upload", to: "uploads#image"
61
+ end
62
+ ```
63
+ ```rb
64
+ # app/controllers/uploads_controller.rb (Rails)
65
+ class UploadsController < ApplicationController
66
+ def image
67
+ # ... we can perform authentication here ...
68
+
69
+ set_rack_response ImageUploader.upload_response(:cache, env)
70
+ end
71
+
72
+ private
73
+
74
+ def set_rack_response((status, headers, body))
75
+ self.status = status
76
+ self.headers.merge!(headers)
77
+ self.response_body = body
78
+ end
79
+ end
80
+ ```
81
+
55
82
  ## Limiting filesize
56
83
 
57
84
  It's good practice to limit the accepted filesize of uploaded files. You can do
@@ -64,15 +91,6 @@ plugin :upload_endpoint, max_size: 20*1024*1024 # 20 MB
64
91
  If the uploaded file is larger than the specified value, a `413 Payload Too
65
92
  Large` response will be returned.
66
93
 
67
- ## Checksum
68
-
69
- If you want the upload endpoint to verify the integrity of the uploaded file,
70
- you can include the `Content-MD5` header in the request filled with the
71
- base64-encoded MD5 hash of the file that was calculated prior to the upload,
72
- and the endpoint will automatically use it to verify the uploaded data.
73
-
74
- If the checksums don't match, a `460 Checksum Mismatch` response is returned.
75
-
76
94
  ## Context
77
95
 
78
96
  The upload context will *not* contain `:record` and `:name` values, as the
@@ -96,10 +114,34 @@ You can also customize the upload itself via the `:upload` option:
96
114
 
97
115
  ```rb
98
116
  plugin :upload_endpoint, upload: -> (io, context, request) do
99
- Shrine.new(:cache).upload(io, context)
117
+ Shrine.upload(io, :cache, context)
100
118
  end
101
119
  ```
102
120
 
121
+ ## URL
122
+
123
+ You can have the endpoint include the uploaded file URL in the response body
124
+ by specifying the `:url` option:
125
+
126
+ ```rb
127
+ plugin :upload_endpoint, url: true
128
+ # or
129
+ plugin :upload_endpoint, url: { public: true }
130
+ # or
131
+ plugin :upload_endpoint, url: -> (uploaded_file, request) {
132
+ uploaded_file.url(**options)
133
+ }
134
+ ```
135
+
136
+ In this case the response body will be:
137
+
138
+ ```rb
139
+ {
140
+ "data": { "id": "...", "storage": "...", "metadata": {...} },
141
+ "url": "https://example.com/path/to/file"
142
+ }
143
+ ```
144
+
103
145
  ## Response
104
146
 
105
147
  The response returned by the endpoint can be customized via the
@@ -114,11 +156,22 @@ end
114
156
 
115
157
  ## Ad-hoc options
116
158
 
117
- You can override any of the options above when creating the endpoint:
159
+ You can override any of the options above when creating the endpoint/response:
118
160
 
119
161
  ```rb
120
162
  Shrine.upload_endpoint(:cache, max_size: 20*1024*1024)
163
+ # or
164
+ Shrine.upload_response(:cache, env, max_size: 20*1024*1024)
121
165
  ```
122
166
 
167
+ ## Checksum
168
+
169
+ If you want the upload endpoint to verify the integrity of the uploaded file,
170
+ you can include the `Content-MD5` header in the request filled with the
171
+ base64-encoded MD5 hash of the file that was calculated prior to the upload,
172
+ and the endpoint will automatically use it to verify the uploaded data.
173
+
174
+ If the checksums don't match, a `460 Checksum Mismatch` response is returned.
175
+
123
176
  [upload_endpoint]: /lib/shrine/plugins/upload_endpoint.rb
124
177
  [Uppy]: https://uppy.io