shrine-transloadit 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|