shrine-transloadit 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +74 -30
- data/lib/shrine/plugins/transloadit.rb +76 -41
- data/shrine-transloadit.gemspec +2 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f101f3b58938e3d62d0af2ae6dcd3d7e49d3623
|
4
|
+
data.tar.gz: 8314af9c2984e0ddf629bb190a499f8bc7d84280
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f9ab0417eb5087a1d5c3652f8002781b45e0123053ad47b38856bd05755bb55e32585caf22985bb2b9796fcb1165d3fd00af7c36fcdc65b815a614bcc1a39e9
|
7
|
+
data.tar.gz: 000c35dbf448f1308e4f99f1de673ef71bb9ef6de22bd9b710314277608c10de156c5a74cc94f4bbc5164ea88af84b69dd8afd139d1906484ae95a24629d4b73
|
data/README.md
CHANGED
@@ -107,29 +107,34 @@ class MyUploader < Shrine
|
|
107
107
|
end
|
108
108
|
```
|
109
109
|
|
110
|
-
###
|
110
|
+
### Direct uploads
|
111
111
|
|
112
|
-
Transloadit
|
113
|
-
|
112
|
+
Transloadit supports direct uploads, allowing you to do additional processing
|
113
|
+
on upload, along with a [jQuery plugin] for easy integration. Generally you
|
114
|
+
only want to do some light processing on direct uploads, and without any
|
115
|
+
exporting, so that you have better control over your Transloadit bandwidth.
|
114
116
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
When direct upload finishes, Transloadit returns information about the uploaded
|
118
|
+
file(s). The default `:cache` storage allows you to store Transloadit's
|
119
|
+
temporary file URL as an attachment. This means that you can use the following
|
120
|
+
JavaScript to convert Transloadit's data hash into Shrine's format:
|
119
121
|
|
120
|
-
|
121
|
-
|
122
|
-
|
122
|
+
```js
|
123
|
+
{
|
124
|
+
id: data['url'], // we save the URL
|
125
|
+
storage: 'cache',
|
126
|
+
metadata: {
|
127
|
+
size: data['size'],
|
128
|
+
filename: data['name'],
|
129
|
+
mime_type: data['mime'],
|
130
|
+
width: data['meta'] && data['meta']['width'],
|
131
|
+
height: data['meta'] && data['meta']['height'],
|
132
|
+
transloadit: data['meta'],
|
133
|
+
}
|
134
|
+
}
|
123
135
|
```
|
124
136
|
|
125
|
-
|
126
|
-
automatically save the results to the attachment column in Shrine's format.
|
127
|
-
|
128
|
-
```rb
|
129
|
-
post "/webhooks/transloadit" do
|
130
|
-
Shrine::Attacher.transloadit_save(params)
|
131
|
-
end
|
132
|
-
```
|
137
|
+
See the **[demo app]** for a complete implementation of direct uploads.
|
133
138
|
|
134
139
|
### Templates
|
135
140
|
|
@@ -163,10 +168,34 @@ class MyUploader < Shrine
|
|
163
168
|
end
|
164
169
|
```
|
165
170
|
|
171
|
+
### Webhooks
|
172
|
+
|
173
|
+
Transloadit performs its processing asynchronously, and you can provide a URL
|
174
|
+
where you want Transloadit to POST results of processing once it's finished.
|
175
|
+
|
176
|
+
```rb
|
177
|
+
class MyUploader < Shrine
|
178
|
+
def transloadit_process(io, context)
|
179
|
+
# ...
|
180
|
+
|
181
|
+
transloadit_assembly(files, notify_url: "http://myapp.com/webhooks/transloadit")
|
182
|
+
end
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
Then in your `POST /webhooks/transloadit` route you can call the plugin to
|
187
|
+
automatically save the results to the attachment column in Shrine's format.
|
188
|
+
|
189
|
+
```rb
|
190
|
+
post "/webhooks/transloadit" do
|
191
|
+
Shrine::Attacher.transloadit_save(params)
|
192
|
+
end
|
193
|
+
```
|
194
|
+
|
166
195
|
### Backgrounding
|
167
196
|
|
168
197
|
Even though submitting a Transloadit assembly doesn't require any uploading, it
|
169
|
-
still does two HTTP requests, so you might want to put it into a
|
198
|
+
still does two HTTP requests, so you might want to put it into a background job.
|
170
199
|
This plugin naturally hooks onto Shrine's backgrounding plugin:
|
171
200
|
|
172
201
|
```rb
|
@@ -212,20 +241,35 @@ response.reload!
|
|
212
241
|
response.finished? #=> true
|
213
242
|
```
|
214
243
|
|
215
|
-
###
|
244
|
+
### Metadata
|
216
245
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
246
|
+
For each processed file Transloadit also extracts a great deal of useful
|
247
|
+
metadata. When the Transloadit processing is finished and the results are saved
|
248
|
+
as a Shrine attachment, this metadata will be automatically used to populate
|
249
|
+
the attachment's metadata.
|
221
250
|
|
222
|
-
|
223
|
-
|
224
|
-
value (e.g. by assigning it to the hidden attachment field), and the plugin
|
225
|
-
will automatically recognize it and convert it to Shrine's attachment
|
226
|
-
representation.
|
251
|
+
Additionally the Transloadit's metadata hash will be saved in an additional
|
252
|
+
metadata key, so that you can access any other values:
|
227
253
|
|
228
|
-
|
254
|
+
```rb
|
255
|
+
photo = Photo.create(image: image_file)
|
256
|
+
photo.image.metadata["transloadit"] #=>
|
257
|
+
# {
|
258
|
+
# "date_recorded" => "2013/09/04 08:03:39",
|
259
|
+
# "date_file_created" => "2013/09/04 12:03:39 GMT",
|
260
|
+
# "date_file_modified" => "2016/07/11 02:27:11 GMT",
|
261
|
+
# "aspect_ratio" => "1.504",
|
262
|
+
# "city" => "Decatur",
|
263
|
+
# "state" => "Georgia",
|
264
|
+
# "country" => "United States",
|
265
|
+
# "latitude" => 33.77519301,
|
266
|
+
# "longitude" => -84.295608,
|
267
|
+
# "orientation" => "Horizontal (normal)",
|
268
|
+
# "colorspace" => "RGB",
|
269
|
+
# "average_color" => "#8b8688",
|
270
|
+
# ...
|
271
|
+
# }
|
272
|
+
```
|
229
273
|
|
230
274
|
### Import & Export
|
231
275
|
|
@@ -1,14 +1,19 @@
|
|
1
1
|
require "transloadit"
|
2
|
-
require "down"
|
3
2
|
|
4
3
|
require "uri"
|
5
4
|
require "json"
|
6
5
|
require "openssl"
|
7
|
-
require "net/http"
|
8
6
|
|
9
7
|
class Shrine
|
10
8
|
module Plugins
|
11
9
|
module Transloadit
|
10
|
+
# Accepts Transloadit credentials via `:auth_key` and `:auth_secret`.
|
11
|
+
#
|
12
|
+
# If :cache storage wasn't assigned, it will be assigned to a URL storage
|
13
|
+
# for direct uploads.
|
14
|
+
#
|
15
|
+
# If promoting was not yet overriden, it is set to automatically trigger
|
16
|
+
# Transloadit processing defined in `Shrine#transloadit_process`.
|
12
17
|
def self.configure(uploader, opts = {})
|
13
18
|
uploader.opts[:transloadit_auth_key] = opts.fetch(:auth_key, uploader.opts[:transloadit_auth_key])
|
14
19
|
uploader.opts[:transloadit_auth_secret] = opts.fetch(:auth_secret, uploader.opts[:transloadit_auth_secret])
|
@@ -16,15 +21,22 @@ class Shrine
|
|
16
21
|
raise Error, "The :auth_key is required for transloadit plugin" if uploader.opts[:transloadit_auth_key].nil?
|
17
22
|
raise Error, "The :auth_secret is required for transloadit plugin" if uploader.opts[:transloadit_auth_secret].nil?
|
18
23
|
|
19
|
-
uploader.storages[:cache] ||=
|
24
|
+
uploader.storages[:cache] ||= (
|
25
|
+
require "shrine/storage/url"
|
26
|
+
Shrine::Storage::Url.new
|
27
|
+
)
|
28
|
+
|
20
29
|
uploader.opts[:backgrounding_promote] ||= proc { transloadit_process }
|
21
30
|
end
|
22
31
|
|
32
|
+
# It loads the backgrounding plugin, so that it can override promoting.
|
23
33
|
def self.load_dependencies(uploader, opts = {})
|
24
34
|
uploader.plugin :backgrounding
|
25
35
|
end
|
26
36
|
|
27
37
|
module AttacherClassMethods
|
38
|
+
# Loads the attacher from the data, and triggers Transloadit
|
39
|
+
# processing. Intended to be used in a background job.
|
28
40
|
def transloadit_process(data)
|
29
41
|
attacher = self.load(data)
|
30
42
|
cached_file = attacher.uploaded_file(data["attachment"])
|
@@ -32,6 +44,9 @@ class Shrine
|
|
32
44
|
attacher
|
33
45
|
end
|
34
46
|
|
47
|
+
# This should be called when receiving a webhook, where arguments are
|
48
|
+
# params that Transloadit POSTed to the endpoint. It checks the
|
49
|
+
# signature, loads the attacher, saves processing results to the record.
|
35
50
|
def transloadit_save(params)
|
36
51
|
params["transloadit"] = params["transloadit"].to_json if params["transloadit"].is_a?(Hash)
|
37
52
|
check_transloadit_signature!(params)
|
@@ -43,6 +58,9 @@ class Shrine
|
|
43
58
|
attacher
|
44
59
|
end
|
45
60
|
|
61
|
+
# Checks if the webhook has indeed been triggered by Transloadit, by
|
62
|
+
# checking if sent signature matches the calculated signature, and
|
63
|
+
# raising a `Shrine::Error` if signatures don't match.
|
46
64
|
def check_transloadit_signature!(params)
|
47
65
|
sent_signature = params["signature"]
|
48
66
|
payload = params["transloadit"]
|
@@ -54,6 +72,16 @@ class Shrine
|
|
54
72
|
end
|
55
73
|
|
56
74
|
module AttacherMethods
|
75
|
+
# Triggers Transloadit processing defined by the user in
|
76
|
+
# `Shrine#transloadit_process`. It dumps the attacher in the payload of
|
77
|
+
# the request, so that it's included in the webhook and that we know
|
78
|
+
# which webhook belongs to which record/attachment.
|
79
|
+
#
|
80
|
+
# After the Transloadit assembly was submitted, the response is saved
|
81
|
+
# into cached file's metadata, which can then be reloaded at will for
|
82
|
+
# checking progress of the assembly.
|
83
|
+
#
|
84
|
+
# It raises a `Shrine::Error` if Transloadit returned an error.
|
57
85
|
def transloadit_process(cached_file = get)
|
58
86
|
assembly = store.transloadit_process(cached_file, context)
|
59
87
|
assembly.options[:fields] ||= {}
|
@@ -64,6 +92,13 @@ class Shrine
|
|
64
92
|
swap(cached_file) or _set(cached_file)
|
65
93
|
end
|
66
94
|
|
95
|
+
# It receives the result of Transloadit processing, and converts it into
|
96
|
+
# Shrine's representation(s) of an uploaded file (either as a single
|
97
|
+
# file or a hash of versions).
|
98
|
+
#
|
99
|
+
# If attachment has changed in the meanwhile, meaning the result of
|
100
|
+
# this processing is no longer valid, it deletes the processed files
|
101
|
+
# from the main storage.
|
67
102
|
def transloadit_save(response, valid: true)
|
68
103
|
if versions = response["fields"]["versions"]
|
69
104
|
stored_file = versions.inject({}) do |hash, (name, key)|
|
@@ -82,22 +117,11 @@ class Shrine
|
|
82
117
|
_delete(stored_file, phase: :abort)
|
83
118
|
end
|
84
119
|
end
|
85
|
-
|
86
|
-
def uploaded_file(value)
|
87
|
-
if value.is_a?(String) || value.is_a?(Hash)
|
88
|
-
value = JSON.parse(value) if value.is_a?(String)
|
89
|
-
if value["url"].is_a?(String) # from direct upload
|
90
|
-
cache.transloadit_uploaded_file(value)
|
91
|
-
else
|
92
|
-
super
|
93
|
-
end
|
94
|
-
else
|
95
|
-
super
|
96
|
-
end
|
97
|
-
end
|
98
120
|
end
|
99
121
|
|
100
122
|
module ClassMethods
|
123
|
+
# Creates a new Transloadit client, so that the expiration timestamp is
|
124
|
+
# refreshed on new processing requests.
|
101
125
|
def transloadit
|
102
126
|
::Transloadit.new(
|
103
127
|
key: opts[:transloadit_auth_key],
|
@@ -107,11 +131,18 @@ class Shrine
|
|
107
131
|
end
|
108
132
|
|
109
133
|
module InstanceMethods
|
134
|
+
# Converts Transloadit's representation of an uploaded file into
|
135
|
+
# Shrine's representation. It currently only accepts files exported to
|
136
|
+
# S3. All Transloadit's metadata is saved into a "transloadit"
|
137
|
+
# attribute.
|
138
|
+
#
|
139
|
+
# When doing direct uploads to Transloadit you will only get a
|
140
|
+
# temporary URL, which will be saved in the "id" attribute and it's
|
141
|
+
# expected that the URL storage is used.
|
110
142
|
def transloadit_uploaded_file(result)
|
111
143
|
case url = result.fetch("url")
|
112
|
-
when /tmp\.transloadit\.com/
|
113
|
-
id = url
|
114
144
|
when /amazonaws\.com/
|
145
|
+
raise Error, "Cannot save a processed file which wasn't exported: #{url.inspect}" if url.include?("tmp.transloadit.com")
|
115
146
|
path = URI(url).path
|
116
147
|
id = path.match(/^\/#{storage.prefix}/).post_match
|
117
148
|
else
|
@@ -132,6 +163,9 @@ class Shrine
|
|
132
163
|
)
|
133
164
|
end
|
134
165
|
|
166
|
+
# Generates a Transloadit import step from the Shrine::UploadedFile.
|
167
|
+
# If it's from the S3 storage, an S3 import step will be generated.
|
168
|
+
# Otherwise either a generic HTTP(S) or an FTP import will be generated.
|
135
169
|
def transloadit_import_step(name, io, **step_options)
|
136
170
|
uri = URI.parse(io.url)
|
137
171
|
|
@@ -163,6 +197,8 @@ class Shrine
|
|
163
197
|
step
|
164
198
|
end
|
165
199
|
|
200
|
+
# Generates an export step from the current (permanent) storage.
|
201
|
+
# At the moment only Amazon S3 is supported.
|
166
202
|
def transloadit_export_step(name, **step_options)
|
167
203
|
if defined?(Storage::S3) && storage.is_a?(Storage::S3)
|
168
204
|
step = transloadit.step(name, "/s3/store",
|
@@ -180,12 +216,20 @@ class Shrine
|
|
180
216
|
step
|
181
217
|
end
|
182
218
|
|
219
|
+
# Creates a new TransloaditFile for building steps, with an optional
|
220
|
+
# import step applied.
|
183
221
|
def transloadit_file(io = nil)
|
184
222
|
file = TransloaditFile.new(transloadit: transloadit)
|
185
223
|
file = file.add_step(transloadit_import_step("import", io)) if io
|
186
224
|
file
|
187
225
|
end
|
188
226
|
|
227
|
+
# Accepts a TransloaditFile, a hash of TransloaditFiles or a template,
|
228
|
+
# and converts it into a Transloadit::Assembly.
|
229
|
+
#
|
230
|
+
# If a hash of versions are given, the version information is saved in
|
231
|
+
# assembly's payload, so that later in the webhook it can be used to
|
232
|
+
# construct a hash of versions.
|
189
233
|
def transloadit_assembly(value, context: {}, **options)
|
190
234
|
options[:steps] ||= []
|
191
235
|
options[:fields] ||= {}
|
@@ -224,12 +268,16 @@ class Shrine
|
|
224
268
|
transloadit.assembly(options)
|
225
269
|
end
|
226
270
|
|
271
|
+
# An cached instance of a Tranloadit client.
|
227
272
|
def transloadit
|
228
273
|
@transloadit ||= self.class.transloadit
|
229
274
|
end
|
230
275
|
end
|
231
276
|
|
232
277
|
module FileMethods
|
278
|
+
# Converts the "transloadit_response" that was saved in cached file's
|
279
|
+
# metadata into a reloadable object which is normally returned by the
|
280
|
+
# Transloadit gem.
|
233
281
|
def transloadit_response
|
234
282
|
@transloadit_response ||= (
|
235
283
|
body = metadata["transloadit_response"] or return
|
@@ -249,6 +297,11 @@ class Shrine
|
|
249
297
|
@steps = steps
|
250
298
|
end
|
251
299
|
|
300
|
+
# Adds a step to the pipeline, automatically setting :use to the
|
301
|
+
# previous step, so that later multiple pipelines can be seamlessly
|
302
|
+
# joined into a single assembly.
|
303
|
+
#
|
304
|
+
# It returns a new TransloaditFile with the step added.
|
252
305
|
def add_step(*args)
|
253
306
|
if args[0].is_a?(::Transloadit::Step)
|
254
307
|
step = args[0]
|
@@ -263,8 +316,9 @@ class Shrine
|
|
263
316
|
TransloaditFile.new(transloadit: transloadit, steps: @steps + [step])
|
264
317
|
end
|
265
318
|
|
266
|
-
#
|
267
|
-
#
|
319
|
+
# The key that Transloadit will use in its processing results for the
|
320
|
+
# corresponding processed file. This equals to the name of the last
|
321
|
+
# step, unless the last step is an export step.
|
268
322
|
def name
|
269
323
|
if exported?
|
270
324
|
@steps[-2].name
|
@@ -273,35 +327,16 @@ class Shrine
|
|
273
327
|
end
|
274
328
|
end
|
275
329
|
|
330
|
+
# Returns true if this TransloaditFile includes an import step.
|
276
331
|
def imported?
|
277
332
|
@steps.any? && @steps.first.robot.end_with?("/import")
|
278
333
|
end
|
279
334
|
|
335
|
+
# Returns true if this TransloaditFile includes an export step.
|
280
336
|
def exported?
|
281
337
|
@steps.any? && @steps.last.robot.end_with?("/store")
|
282
338
|
end
|
283
339
|
end
|
284
|
-
|
285
|
-
class UrlStorage
|
286
|
-
def download(id)
|
287
|
-
Down.download(id)
|
288
|
-
end
|
289
|
-
|
290
|
-
def open(id)
|
291
|
-
Down.open(id)
|
292
|
-
end
|
293
|
-
|
294
|
-
def exists?(id)
|
295
|
-
response = nil
|
296
|
-
uri = URI(id)
|
297
|
-
Net::HTTP.start(uri.host, uri.port) { |http| response = http.head(id) }
|
298
|
-
response.code.to_i == 200
|
299
|
-
end
|
300
|
-
|
301
|
-
def url(id, **options)
|
302
|
-
id
|
303
|
-
end
|
304
|
-
end
|
305
340
|
end
|
306
341
|
|
307
342
|
register_plugin(:transloadit, Transloadit)
|
data/shrine-transloadit.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |gem|
|
2
2
|
gem.name = "shrine-transloadit"
|
3
|
-
gem.version = "0.
|
3
|
+
gem.version = "0.3.0"
|
4
4
|
|
5
5
|
gem.required_ruby_version = ">= 2.1"
|
6
6
|
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
|
15
15
|
|
16
16
|
gem.add_dependency "shrine", "~> 2.1"
|
17
17
|
gem.add_dependency "transloadit", "~> 1.2"
|
18
|
-
gem.add_dependency "
|
18
|
+
gem.add_dependency "shrine-url"
|
19
19
|
|
20
20
|
gem.add_development_dependency "rake"
|
21
21
|
gem.add_development_dependency "minitest"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shrine-transloadit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janko Marohnić
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
11
|
+
date: 2016-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: shrine
|
@@ -39,19 +39,19 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: shrine-url
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: '0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|