shrine-transloadit 0.1.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +345 -0
- data/lib/shrine/plugins/transloadit.rb +267 -0
- data/shrine-transloadit.gemspec +26 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f9ad060395c4432e137702840eba7f23eef980b5
|
4
|
+
data.tar.gz: 983a7aaf98794573d4f946fe14d0014162b02bfd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 48003055afa7b725f0f42213daf2dedb6ecf91d03f8c5805921b0ff57953202aa0a8463300a40246cb1b0984eda816972cd8a0af2b99676188ac62bd18e010c0
|
7
|
+
data.tar.gz: a4aec56c9856366f4d8fb87c00ce6d6ff8f91750033ed6c0aba48b241533358c6bc5af7b4930f7bda463ebf06ff35322de5fc948d229597b8e855808439d4bdc
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Janko Marohnić
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,345 @@
|
|
1
|
+
# Shrine::Plugins::Transloadit
|
2
|
+
|
3
|
+
Provides [Transloadit] integration for [Shrine].
|
4
|
+
|
5
|
+
Transloadit offers advanced file processing for all sorts of media, including
|
6
|
+
images, videos, audio, and documents, along with importing from and exporting
|
7
|
+
to various file storage services.
|
8
|
+
|
9
|
+
## Setup
|
10
|
+
|
11
|
+
While Transloadit is able to export processed files to [many storage
|
12
|
+
services], this plugin currently supports only Amazon S3 (just because there
|
13
|
+
are no Shrine integrations written for other services on that list yet).
|
14
|
+
|
15
|
+
```rb
|
16
|
+
gem "shrine-transloadit"
|
17
|
+
gem "aws-sdk"
|
18
|
+
```
|
19
|
+
|
20
|
+
```rb
|
21
|
+
require "shrine"
|
22
|
+
require "shrine/storage/s3"
|
23
|
+
|
24
|
+
s3_options = {
|
25
|
+
access_key_id: "xyz",
|
26
|
+
secret_access_key: "abc",
|
27
|
+
region: "my-region",
|
28
|
+
bucket: "my-app",
|
29
|
+
}
|
30
|
+
|
31
|
+
Shrine.storages = {
|
32
|
+
cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
|
33
|
+
store: Shrine::Storage::S3.new(prefix: "store", **s3_options),
|
34
|
+
}
|
35
|
+
|
36
|
+
Shrine.plugin :transloadit,
|
37
|
+
auth_key: "your transloadit key",
|
38
|
+
auth_secret: "your transloadit secret"
|
39
|
+
```
|
40
|
+
```rb
|
41
|
+
post "/webhooks/transloadit" do
|
42
|
+
Shrine::Attacher.transloadit_save(params)
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
## How it works
|
47
|
+
|
48
|
+
Transloadit works in a way that you create an "assembly", which contains all
|
49
|
+
information about how the file(s) should be processed, from import to export.
|
50
|
+
Processing itself happens asynchronously, and you can give Transloadit a URL
|
51
|
+
which it will POST results to when processing finishes.
|
52
|
+
|
53
|
+
This plugin allows you to easily implement this webhook flow. You can intercept
|
54
|
+
promoting, and submit a Transloadit assembly using the cached file, along with
|
55
|
+
a URL to the route in your app where you'd like Transloadit to POST the results
|
56
|
+
of processing. Then you can call the plugin again in the route to save the
|
57
|
+
results to your attachment column.
|
58
|
+
|
59
|
+
## Usage
|
60
|
+
|
61
|
+
Transloadit assemblies are built inside `#transloadit_process` method in your
|
62
|
+
uploader, and you can use some convenient helper methods which the plugin
|
63
|
+
provides.
|
64
|
+
|
65
|
+
```rb
|
66
|
+
class MyUploader < Shrine
|
67
|
+
def transloadit_process(io, context)
|
68
|
+
resized = transloadit_file(io)
|
69
|
+
.add_step("resize", "/image/resize", width: 800)
|
70
|
+
|
71
|
+
transloadit_assembly(resized, context: context)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
These helper methods just provide a higher-level interface over the
|
77
|
+
[transloadit gem], which you might want look at to get a better understanding
|
78
|
+
of how building assemblies works.
|
79
|
+
|
80
|
+
In short, in Transloadit every action, be it import, processing, or export, is
|
81
|
+
a "step". Each step is defined by its [robot and arguments], and needs to have
|
82
|
+
a *unique name*. Transloadit allows you to define the entire processing flow
|
83
|
+
(which can result in multiple files) as a collection of steps, which is called
|
84
|
+
an "assembly". Once the assembly is built it can be submitted to Transloadit.
|
85
|
+
|
86
|
+
### Versions
|
87
|
+
|
88
|
+
With Transloadit you can create multiple files in a single assembly, and this
|
89
|
+
plugin allows you to leverage that in form of a hash of versions.
|
90
|
+
|
91
|
+
```rb
|
92
|
+
class MyUploader < Shrine
|
93
|
+
plugin :versions
|
94
|
+
|
95
|
+
def transloadit_process(io, context)
|
96
|
+
original = transloadit_file(io)
|
97
|
+
medium = original.add_step("resize_500", "/image/resize", width: 500)
|
98
|
+
small = original.add_step("resize_300", "/image/resize", width: 300)
|
99
|
+
|
100
|
+
files = {original: original, medium: medium, small: small}
|
101
|
+
|
102
|
+
transloadit_assembly(files, context: context)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
### Webhooks
|
108
|
+
|
109
|
+
Transloadit performs its processing asynchronously, and you can provide a URL
|
110
|
+
where you want Transloadit to POST results of processing once it's finished.
|
111
|
+
|
112
|
+
```rb
|
113
|
+
class MyUploader < Shrine
|
114
|
+
def transloadit_process(io, context)
|
115
|
+
# ...
|
116
|
+
|
117
|
+
transloadit_assembly(files, notify_url: "http://myapp.com/webhooks/transloadit")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
Then in your `POST /webhooks/transloadit` route you can call the plugin to
|
123
|
+
automatically save the results to the attachment column in Shrine's format.
|
124
|
+
|
125
|
+
```rb
|
126
|
+
post "/webhooks/transloadit" do
|
127
|
+
Shrine::Attacher.transloadit_save(params)
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
### Templates
|
132
|
+
|
133
|
+
Transloadit recommends using [templates], since they allow you to replay failed
|
134
|
+
assemblies, and also allow you not to expose credentials in your HTML.
|
135
|
+
|
136
|
+
Here is an example where the whole processing is defined inside a template,
|
137
|
+
and we just set the location of the imported file.
|
138
|
+
|
139
|
+
```rb
|
140
|
+
# Your saved template
|
141
|
+
{
|
142
|
+
steps: {
|
143
|
+
import: {
|
144
|
+
robot: "/http/import",
|
145
|
+
url: "..."
|
146
|
+
},
|
147
|
+
resize: {
|
148
|
+
robot: "/image/resize",
|
149
|
+
use: "import",
|
150
|
+
width: 800
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
```
|
155
|
+
```rb
|
156
|
+
class MyUploader < Shrine
|
157
|
+
def transloadit_process(io, context)
|
158
|
+
transloadit_assembly("my_template", steps: {import: {url: io.url}})
|
159
|
+
end
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
### Backgrounding
|
164
|
+
|
165
|
+
Even though submitting a Transloadit assembly doesn't require any uploading, it
|
166
|
+
still does two HTTP requests, so you might want to put it into a backgrond job.
|
167
|
+
This plugin naturally hooks onto Shrine's backgrounding plugin:
|
168
|
+
|
169
|
+
```rb
|
170
|
+
Shrine::Attacher.promote { |data| TransloaditJob.perform_async(data) }
|
171
|
+
```
|
172
|
+
```rb
|
173
|
+
class TransloaditJob
|
174
|
+
include Sidekiq::Worker
|
175
|
+
|
176
|
+
def perform(data)
|
177
|
+
Shrine::Attacher.transloadit_process(data)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
### Tracking progress
|
183
|
+
|
184
|
+
When an assembly is submitted, Transloadit returns a lot of useful information
|
185
|
+
about the status of that assembly, which the plugin saves to the cached
|
186
|
+
attachment's metadata.
|
187
|
+
|
188
|
+
```rb
|
189
|
+
response = photo.image.transloadit_response
|
190
|
+
response.body #=>
|
191
|
+
# {
|
192
|
+
# "ok" => "ASSEMBLY_EXECUTING",
|
193
|
+
# "message" => "The assembly is currently being executed.",
|
194
|
+
# "assembly_id" => "83d07d10414011e68cc8c5df79919836",
|
195
|
+
# "assembly_url" => "http://api2.janani.transloadit.com/assemblies/83d07d10414011e68cc8c5df79919836",
|
196
|
+
# "execution_start" => "2016/07/03 17:06:42 GMT",
|
197
|
+
# "execution_duration" => 2.113,
|
198
|
+
# "params" => "{\"steps\":{...}}",
|
199
|
+
# ...
|
200
|
+
# }
|
201
|
+
```
|
202
|
+
|
203
|
+
At an point during the execution of the assembly you can refresh this
|
204
|
+
information:
|
205
|
+
|
206
|
+
```rb
|
207
|
+
response.finished? #=> false
|
208
|
+
response.reload!
|
209
|
+
response.finished? #=> true
|
210
|
+
```
|
211
|
+
|
212
|
+
### Import & Export
|
213
|
+
|
214
|
+
Every `TransloaditFile` needs to have an import and an export step. This plugin
|
215
|
+
automatically generates those steps for you:
|
216
|
+
|
217
|
+
```rb
|
218
|
+
transloadit_file(io)
|
219
|
+
|
220
|
+
# is equivalent to
|
221
|
+
|
222
|
+
file = transloadit_file
|
223
|
+
file.add_step(transloadit_import_step("import", io))
|
224
|
+
```
|
225
|
+
|
226
|
+
```rb
|
227
|
+
transloadit_assembly({original: original, thumb: thumb})
|
228
|
+
|
229
|
+
# is equivalent to
|
230
|
+
|
231
|
+
transloadit_assembly({
|
232
|
+
original: original.add_step(transloadit_export_step("export_original")),
|
233
|
+
thumb: thumb.add_step(transloadit_export_step("export_thumb")),
|
234
|
+
})
|
235
|
+
```
|
236
|
+
|
237
|
+
If you want/need to generate these steps yourself, you can just use the
|
238
|
+
expanded forms.
|
239
|
+
|
240
|
+
### Transloadit gem
|
241
|
+
|
242
|
+
If you want to have complete control over how steps are generated, you can just
|
243
|
+
use the [transloadit gem] directly. This plugin doesn't care how you generate
|
244
|
+
your steps, it only requires you to return an instance of
|
245
|
+
`Transloadit::Assembly`.
|
246
|
+
|
247
|
+
```rb
|
248
|
+
class MyUploader < Shrine
|
249
|
+
def transloadit_process(io, context)
|
250
|
+
# build options
|
251
|
+
transloadit #=> #<Transloadit>
|
252
|
+
transloadit.assembly(options)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
```
|
256
|
+
|
257
|
+
The import/export helper methods simply generate a `Transloadit::Step` object,
|
258
|
+
and you can pass additional options:
|
259
|
+
|
260
|
+
```rb
|
261
|
+
class MyUploader < Shrinee
|
262
|
+
def transloadit_process(io, context)
|
263
|
+
transloadit_import_step("import", io) #=> #<Transloadit::Step>
|
264
|
+
transloadit_export_step("export", path: "mypath") #=> #<Transloadit::Step>
|
265
|
+
end
|
266
|
+
end
|
267
|
+
```
|
268
|
+
|
269
|
+
The `#add_step` method for `TransloaditFile` is just a convenient way to add
|
270
|
+
steps where `:use` is automatically set to previous step.
|
271
|
+
|
272
|
+
### Testing
|
273
|
+
|
274
|
+
In development or test environment you cannot use webhooks, because Transloadit
|
275
|
+
as an external service cannot access your localhost. In this case you can just
|
276
|
+
do polling:
|
277
|
+
|
278
|
+
```rb
|
279
|
+
class MyUploader < Shrine
|
280
|
+
def transloadit_process(io, context)
|
281
|
+
# ...
|
282
|
+
|
283
|
+
if ENV["RACK_ENV"] == "production"
|
284
|
+
notify_url = "https://myapp.com/webhooks/transloadit"
|
285
|
+
else
|
286
|
+
# In development we cannot receive webhooks, because Transloadit as an
|
287
|
+
# external service cannot reach our localhost.
|
288
|
+
end
|
289
|
+
|
290
|
+
transloadit_assembly(files, context: context, notify_url: notify_url)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
```
|
294
|
+
|
295
|
+
```rb
|
296
|
+
class TransloaditJob
|
297
|
+
include Sidekiq::Worker
|
298
|
+
|
299
|
+
def perform(data)
|
300
|
+
attacher = Shrine::Attacher.transloadit_process(data)
|
301
|
+
|
302
|
+
# Webhooks won't work in development, so we can just use polling.
|
303
|
+
unless ENV["RACK_ENV"] == "production"
|
304
|
+
response = attacher.get.transloadit_response
|
305
|
+
until response.finished?
|
306
|
+
sleep 1
|
307
|
+
response.reload!
|
308
|
+
end
|
309
|
+
attacher.transloadit_save(response.body)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
```
|
314
|
+
|
315
|
+
## Contributing
|
316
|
+
|
317
|
+
Before you can run tests, you need to first create an `.env` file in the
|
318
|
+
project root containing your Transloadit and Amazon S3 credentials:
|
319
|
+
|
320
|
+
```sh
|
321
|
+
# .env
|
322
|
+
TRANSLOADIT_AUTH_KEY="..."
|
323
|
+
TRANSLOADIT_AUTH_SECRET="..."
|
324
|
+
S3_BUCKET="..."
|
325
|
+
S3_REGION="..."
|
326
|
+
S3_ACCESS_KEY_ID="..."
|
327
|
+
S3_SECRET_ACCESS_KEY="..."
|
328
|
+
```
|
329
|
+
|
330
|
+
Afterwards you can run the tests:
|
331
|
+
|
332
|
+
```sh
|
333
|
+
$ bundle exec rake test
|
334
|
+
```
|
335
|
+
|
336
|
+
## License
|
337
|
+
|
338
|
+
[MIT](LICENSE.txt)
|
339
|
+
|
340
|
+
[Shrine]: https://github.com/janko-m/shrine
|
341
|
+
[Transloadit]: https://transloadit.com/
|
342
|
+
[many storage services]: https://transloadit.com/docs/conversion-robots/#file-export-robots
|
343
|
+
[transloadit gem]: https://github.com/transloadit/ruby-sdk
|
344
|
+
[robot and arguments]: https://transloadit.com/docs/conversion-robots/
|
345
|
+
[templates]: https://transloadit.com/docs/#templates
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require "transloadit"
|
2
|
+
require "uri"
|
3
|
+
require "json"
|
4
|
+
require "openssl"
|
5
|
+
|
6
|
+
class Shrine
|
7
|
+
module Plugins
|
8
|
+
module Transloadit
|
9
|
+
def self.configure(uploader, opts = {})
|
10
|
+
uploader.opts[:transloadit_auth_key] = opts.fetch(:auth_key, uploader.opts[:transloadit_auth_key])
|
11
|
+
uploader.opts[:transloadit_auth_secret] = opts.fetch(:auth_secret, uploader.opts[:transloadit_auth_secret])
|
12
|
+
|
13
|
+
raise Error, "The :auth_key is required for transloadit plugin" if uploader.opts[:transloadit_auth_key].nil?
|
14
|
+
raise Error, "The :auth_secret is required for transloadit plugin" if uploader.opts[:transloadit_auth_secret].nil?
|
15
|
+
|
16
|
+
uploader.opts[:backgrounding_promote] ||= proc { transloadit_process }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.load_dependencies(uploader, opts = {})
|
20
|
+
uploader.plugin :backgrounding
|
21
|
+
end
|
22
|
+
|
23
|
+
module AttacherClassMethods
|
24
|
+
def transloadit_process(data)
|
25
|
+
attacher = self.load(data)
|
26
|
+
cached_file = attacher.uploaded_file(data["attachment"])
|
27
|
+
attacher.transloadit_process(cached_file)
|
28
|
+
attacher
|
29
|
+
end
|
30
|
+
|
31
|
+
def transloadit_save(params)
|
32
|
+
params["transloadit"] = params["transloadit"].to_json if params["transloadit"].is_a?(Hash)
|
33
|
+
check_transloadit_signature!(params)
|
34
|
+
response = JSON.parse(params["transloadit"])
|
35
|
+
data = response["fields"]["attacher"]
|
36
|
+
attacher = self.load(data)
|
37
|
+
cached_file = attacher.uploaded_file(data["attachment"])
|
38
|
+
return if cached_file != attacher.get
|
39
|
+
attacher.transloadit_save(response)
|
40
|
+
attacher
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_transloadit_signature!(params)
|
44
|
+
sent_signature = params["signature"]
|
45
|
+
payload = params["transloadit"]
|
46
|
+
algorithm = OpenSSL::Digest.new('sha1')
|
47
|
+
secret = shrine_class.opts[:transloadit_auth_secret]
|
48
|
+
calculated_signature = OpenSSL::HMAC.hexdigest(algorithm, secret, payload)
|
49
|
+
raise Error, "Transloadit signature that was sent doesn't match the calculated signature" if calculated_signature != sent_signature
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module AttacherMethods
|
54
|
+
def transloadit_process(cached_file = get)
|
55
|
+
assembly = store.transloadit_process(cached_file, context)
|
56
|
+
assembly.options[:fields] ||= {}
|
57
|
+
assembly.options[:fields]["attacher"] = self.dump.merge("attachment" => cached_file.to_json)
|
58
|
+
response = assembly.submit!
|
59
|
+
raise Error, "#{response["error"]}: #{response["message"]}" if response["error"]
|
60
|
+
cached_file.metadata["transloadit_response"] = response.body.to_json
|
61
|
+
swap(cached_file)
|
62
|
+
end
|
63
|
+
|
64
|
+
def transloadit_save(response)
|
65
|
+
if versions = response["fields"]["versions"]
|
66
|
+
stored_file = versions.inject({}) do |hash, (name, key)|
|
67
|
+
result = response["results"].fetch(key)[0]
|
68
|
+
uploaded_file = store.transloadit_uploaded_file(result)
|
69
|
+
hash.update(name => uploaded_file)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
result = response["results"].values.last[0]
|
73
|
+
stored_file = store.transloadit_uploaded_file(result)
|
74
|
+
end
|
75
|
+
|
76
|
+
update(stored_file)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
module ClassMethods
|
81
|
+
def transloadit
|
82
|
+
::Transloadit.new(
|
83
|
+
key: opts[:transloadit_auth_key],
|
84
|
+
secret: opts[:transloadit_auth_secret],
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
module InstanceMethods
|
90
|
+
def transloadit_uploaded_file(result)
|
91
|
+
case url = result.fetch("url")
|
92
|
+
when /amazonaws\.com/
|
93
|
+
raise Error, "Cannot save a processed file which wasn't exported: #{url.inspect}" if url.include?("tmp.transloadit.com")
|
94
|
+
path = URI(url).path
|
95
|
+
id = path.match(/^\/#{storage.prefix}/).post_match
|
96
|
+
else
|
97
|
+
raise Error, "The transloadit Shrine plugin doesn't support storage identified by #{url.inspect}"
|
98
|
+
end
|
99
|
+
|
100
|
+
self.class::UploadedFile.new(
|
101
|
+
"id" => id,
|
102
|
+
"storage" => storage_key,
|
103
|
+
"metadata" => {
|
104
|
+
"filename" => result.fetch("name"),
|
105
|
+
"size" => result.fetch("size"),
|
106
|
+
"mime_type" => result.fetch("mime"),
|
107
|
+
"width" => (result["meta"] && result["meta"]["width"]),
|
108
|
+
"height" => (result["meta"] && result["meta"]["height"]),
|
109
|
+
"transloadit" => result["meta"],
|
110
|
+
}
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
def transloadit_import_step(name, io, **step_options)
|
115
|
+
uri = URI.parse(io.url)
|
116
|
+
|
117
|
+
if defined?(Storage::S3) && io.storage.is_a?(Storage::S3)
|
118
|
+
step = transloadit.step(name, "/s3/import",
|
119
|
+
key: io.storage.s3.client.config.access_key_id,
|
120
|
+
secret: io.storage.s3.client.config.secret_access_key,
|
121
|
+
bucket: io.storage.bucket.name,
|
122
|
+
bucket_region: io.storage.s3.client.config.region,
|
123
|
+
path: [*io.storage.prefix, io.id].join("/"),
|
124
|
+
)
|
125
|
+
elsif uri.scheme == "http" || uri.scheme == "https"
|
126
|
+
step = transloadit.step(name, "/http/import",
|
127
|
+
url: uri.to_s,
|
128
|
+
)
|
129
|
+
elsif uri.scheme == "ftp"
|
130
|
+
step = transloadit.step(name, "/ftp/import",
|
131
|
+
host: uri.host,
|
132
|
+
user: uri.user,
|
133
|
+
password: uri.password,
|
134
|
+
path: uri.path,
|
135
|
+
)
|
136
|
+
else
|
137
|
+
raise Error, "Cannot construct a transloadit import step from #{io.inspect}"
|
138
|
+
end
|
139
|
+
|
140
|
+
step.options.update(step_options)
|
141
|
+
|
142
|
+
step
|
143
|
+
end
|
144
|
+
|
145
|
+
def transloadit_export_step(name, **step_options)
|
146
|
+
if defined?(Storage::S3) && storage.is_a?(Storage::S3)
|
147
|
+
step = transloadit.step(name, "/s3/store",
|
148
|
+
key: storage.s3.client.config.access_key_id,
|
149
|
+
secret: storage.s3.client.config.secret_access_key,
|
150
|
+
bucket: storage.bucket.name,
|
151
|
+
bucket_region: storage.s3.client.config.region
|
152
|
+
)
|
153
|
+
else
|
154
|
+
raise Error, "Cannot construct a transloadit export step from #{storage.inspect}"
|
155
|
+
end
|
156
|
+
|
157
|
+
step.options.update(step_options)
|
158
|
+
|
159
|
+
step
|
160
|
+
end
|
161
|
+
|
162
|
+
def transloadit_file(io = nil)
|
163
|
+
file = TransloaditFile.new(transloadit: transloadit)
|
164
|
+
file = file.add_step(transloadit_import_step("import", io)) if io
|
165
|
+
file
|
166
|
+
end
|
167
|
+
|
168
|
+
def transloadit_assembly(value, context: {}, **options)
|
169
|
+
options[:steps] ||= []
|
170
|
+
options[:fields] ||= {}
|
171
|
+
|
172
|
+
if (versions = value).is_a?(Hash)
|
173
|
+
options[:fields]["versions"] = {}
|
174
|
+
raise Error, "The versions Shrine plugin isn't loaded" if !defined?(Shrine::Plugins::Versions)
|
175
|
+
versions.each do |name, transloadit_file|
|
176
|
+
raise Error, "The given TransloaditFile is missing an import step" if !transloadit_file.imported?
|
177
|
+
unless transloadit_file.exported?
|
178
|
+
path = generate_location(transloadit_file, context.merge(version: name)) + ".${file.ext}"
|
179
|
+
export_step = transloadit_export_step("export_#{name}", path: path)
|
180
|
+
transloadit_file = transloadit_file.add_step(export_step)
|
181
|
+
end
|
182
|
+
options[:steps] |= transloadit_file.steps
|
183
|
+
options[:fields]["versions"][name] = transloadit_file.name
|
184
|
+
end
|
185
|
+
elsif (transloadit_file = value).is_a?(TransloaditFile)
|
186
|
+
raise Error, "The given TransloaditFile is missing an import step" if !transloadit_file.imported?
|
187
|
+
unless transloadit_file.exported?
|
188
|
+
path = generate_location(transloadit_file, context) + ".${file.ext}"
|
189
|
+
export_step = transloadit_export_step("export", path: path)
|
190
|
+
transloadit_file = transloadit_file.add_step(export_step)
|
191
|
+
end
|
192
|
+
options[:steps] += transloadit_file.steps
|
193
|
+
elsif (template = value).is_a?(String)
|
194
|
+
options[:template_id] = template
|
195
|
+
else
|
196
|
+
raise Error, "First argument has to be a TransloaditFile, a hash of TransloaditFiles, or a template"
|
197
|
+
end
|
198
|
+
|
199
|
+
if options[:steps].uniq(&:name) != options[:steps]
|
200
|
+
raise Error, "There are different transloadit steps using the same name"
|
201
|
+
end
|
202
|
+
|
203
|
+
transloadit.assembly(options)
|
204
|
+
end
|
205
|
+
|
206
|
+
def transloadit
|
207
|
+
@transloadit ||= self.class.transloadit
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
module FileMethods
|
212
|
+
def transloadit_response
|
213
|
+
@transloadit_response ||= (
|
214
|
+
body = metadata.fetch("transloadit_response")
|
215
|
+
body.instance_eval { def body; self; end }
|
216
|
+
response = ::Transloadit::Response.new(body)
|
217
|
+
response.extend ::Transloadit::Response::Assembly
|
218
|
+
response
|
219
|
+
)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
class TransloaditFile
|
224
|
+
attr_reader :transloadit, :steps
|
225
|
+
|
226
|
+
def initialize(transloadit:, steps: [])
|
227
|
+
@transloadit = transloadit
|
228
|
+
@steps = steps
|
229
|
+
end
|
230
|
+
|
231
|
+
def add_step(*args)
|
232
|
+
if args[0].is_a?(::Transloadit::Step)
|
233
|
+
step = args[0]
|
234
|
+
else
|
235
|
+
step = transloadit.step(*args)
|
236
|
+
end
|
237
|
+
|
238
|
+
unless step.options[:use]
|
239
|
+
step.use @steps.last if @steps.any?
|
240
|
+
end
|
241
|
+
|
242
|
+
TransloaditFile.new(transloadit: transloadit, steps: @steps + [step])
|
243
|
+
end
|
244
|
+
|
245
|
+
# Transloadit in its result uses the name of the last step before the
|
246
|
+
# export step.
|
247
|
+
def name
|
248
|
+
if exported?
|
249
|
+
@steps[-2].name
|
250
|
+
else
|
251
|
+
@steps.last.name
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def imported?
|
256
|
+
@steps.any? && @steps.first.robot.end_with?("/import")
|
257
|
+
end
|
258
|
+
|
259
|
+
def exported?
|
260
|
+
@steps.any? && @steps.last.robot.end_with?("/store")
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
register_plugin(:transloadit, Transloadit)
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = "shrine-transloadit"
|
3
|
+
gem.version = "0.1.0"
|
4
|
+
|
5
|
+
gem.required_ruby_version = ">= 2.1"
|
6
|
+
|
7
|
+
gem.summary = "Provides Transloadit integration for Shrine."
|
8
|
+
gem.homepage = "https://github.com/janko-m/shrine-transloadit"
|
9
|
+
gem.authors = ["Janko Marohnić"]
|
10
|
+
gem.email = ["janko.marohnic@gmail.com"]
|
11
|
+
gem.license = "MIT"
|
12
|
+
|
13
|
+
gem.files = Dir["README.md", "LICENSE.txt", "lib/**/*.rb", "*.gemspec"]
|
14
|
+
gem.require_path = "lib"
|
15
|
+
|
16
|
+
gem.add_dependency "shrine", "~> 2.1"
|
17
|
+
gem.add_dependency "transloadit", "~> 1.2"
|
18
|
+
|
19
|
+
gem.add_development_dependency "rake"
|
20
|
+
gem.add_development_dependency "minitest"
|
21
|
+
gem.add_development_dependency "minitest-hooks"
|
22
|
+
gem.add_development_dependency "dotenv"
|
23
|
+
gem.add_development_dependency "aws-sdk"
|
24
|
+
gem.add_development_dependency "sequel"
|
25
|
+
gem.add_development_dependency "sqlite3"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shrine-transloadit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Janko Marohnić
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: shrine
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: transloadit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest-hooks
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: dotenv
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: aws-sdk
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: sequel
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: sqlite3
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description:
|
140
|
+
email:
|
141
|
+
- janko.marohnic@gmail.com
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- LICENSE.txt
|
147
|
+
- README.md
|
148
|
+
- lib/shrine/plugins/transloadit.rb
|
149
|
+
- shrine-transloadit.gemspec
|
150
|
+
homepage: https://github.com/janko-m/shrine-transloadit
|
151
|
+
licenses:
|
152
|
+
- MIT
|
153
|
+
metadata: {}
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options: []
|
156
|
+
require_paths:
|
157
|
+
- lib
|
158
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '2.1'
|
163
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
requirements: []
|
169
|
+
rubyforge_project:
|
170
|
+
rubygems_version: 2.5.1
|
171
|
+
signing_key:
|
172
|
+
specification_version: 4
|
173
|
+
summary: Provides Transloadit integration for Shrine.
|
174
|
+
test_files: []
|
175
|
+
has_rdoc:
|