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 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