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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d366d63207d1f1f9344a18d85cb0600c1666fa9
4
- data.tar.gz: c78ce1f611b524aa62d971603f5de6c4e3974eff
3
+ metadata.gz: 6f101f3b58938e3d62d0af2ae6dcd3d7e49d3623
4
+ data.tar.gz: 8314af9c2984e0ddf629bb190a499f8bc7d84280
5
5
  SHA512:
6
- metadata.gz: 77b6562932f477793fec069cb1a71d8ec568b48729c0a8e3d60bd30655f737db9c479ce63723959fc556d88ba8409cbe3c35792d127c929dba5eeb7f816d2748
7
- data.tar.gz: a1dd95f697d0882d92f7b362d5ae1757902d1d59fd3a9787ea56d73ede417a21b7c6c48d550e21dd27d5343d69512a78d04de8834fda697a6f670fc4eb53cc91
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
- ### Webhooks
110
+ ### Direct uploads
111
111
 
112
- Transloadit performs its processing asynchronously, and you can provide a URL
113
- where you want Transloadit to POST results of processing once it's finished.
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
- ```rb
116
- class MyUploader < Shrine
117
- def transloadit_process(io, context)
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
- transloadit_assembly(files, notify_url: "http://myapp.com/webhooks/transloadit")
121
- end
122
- end
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
- Then in your `POST /webhooks/transloadit` route you can call the plugin to
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 backgrond job.
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
- ### Direct uploads
244
+ ### Metadata
216
245
 
217
- Transloadit supports direct uploads, allowing you to do additional processing
218
- on upload, along with a [jQuery plugin] for easy integration. Generally you
219
- only want to do some light processing on direct uploads, and without any
220
- exporting, so that you have better control over your Transloadit bandwidth.
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
- When direct upload finishes, Transloadit returns information about the uploaded
223
- file(s). You can just convert that data to a JSON string and pass it as attachment
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
- See the **[demo app]** for a complete implementation of direct uploads.
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] ||= UrlStorage.new
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
- # Transloadit in its result uses the name of the last step before the
267
- # export step.
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)
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = "shrine-transloadit"
3
- gem.version = "0.2.1"
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 "down", ">= 2.3.3"
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.2.1
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-08 00:00:00.000000000 Z
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: down
42
+ name: shrine-url
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 2.3.3
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: 2.3.3
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement