transloadit 3.1.2 → 4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5b731d1993f71f5eed4e04c06a57bb5e74aa1df9b91bf0af31fee5957da71f1
4
- data.tar.gz: b191d41e32ec32d1c08824897c394a3ecd567a871338814315c9e66a6ad3157a
3
+ metadata.gz: 2c1220e478a424b85d5ac449fd7facf3790af3091a69435699a49480853b86fe
4
+ data.tar.gz: 544d23e833e4ee095744d91f948ca7bfc7616bb70e5d576042d44b3032510307
5
5
  SHA512:
6
- metadata.gz: bf2ba9d4356a6872af095bc41ce0162901de894aaa018a2a144d3109c4c718ee4328b748536515a80f18c85d1a9d3ca7f650ca7f9d8031c011fac3efde7def20
7
- data.tar.gz: 8c984d38f260faf2690b00527fb1afc897cd515140ab2e8639de1b3bc45581e566c7f6c8cc7dddb04210fe344b4a939373d0f351932343f249cbf57ed2479785
6
+ metadata.gz: 69178ed10b8fc21cd0c342e32aeecad79d886721e2c08b7df69e85a3d965faacef50054973a5b6dca7c72b885166a5f371416c641685df6679481d2e310dd0c8
7
+ data.tar.gz: 4a2393daa75a76414b208891d63a2d2d0a88e2c50e22da4b634a33fe7696cb084348fb26b1e823c5b45ca6feda168b12e8450a7dfa290a40d7e7bd29ea082d19
data/.gitattributes CHANGED
@@ -1,5 +1,44 @@
1
- *.jpg binary
2
- *.jpeg binary
3
- *.png binary
4
- *.mp3 binary
5
- *.flac binary
1
+ /.yarn/** linguist-vendored
2
+ /.yarn/releases/* binary
3
+ /.yarn/plugins/**/* binary
4
+ /.pnp.* binary linguist-generated
5
+ *.7z filter=lfs diff=lfs merge=lfs -text
6
+ *.aac filter=lfs diff=lfs merge=lfs -text
7
+ *.ai filter=lfs diff=lfs merge=lfs -text
8
+ *.avi filter=lfs diff=lfs merge=lfs -text
9
+ *.bmp filter=lfs diff=lfs merge=lfs -text
10
+ *.br filter=lfs diff=lfs merge=lfs -text
11
+ *.cr2 filter=lfs diff=lfs merge=lfs -text
12
+ *.dll filter=lfs diff=lfs merge=lfs -text
13
+ *.doc filter=lfs diff=lfs merge=lfs -text
14
+ *.docx filter=lfs diff=lfs merge=lfs -text
15
+ *.exe filter=lfs diff=lfs merge=lfs -text
16
+ *.flac filter=lfs diff=lfs merge=lfs -text
17
+ *.gif filter=lfs diff=lfs merge=lfs -text
18
+ *.gz filter=lfs diff=lfs merge=lfs -text
19
+ *.ico filter=lfs diff=lfs merge=lfs -text
20
+ *.jpeg filter=lfs diff=lfs merge=lfs -text
21
+ *.jpg filter=lfs diff=lfs merge=lfs -text
22
+ *.JPG filter=lfs diff=lfs merge=lfs -text
23
+ *.m4a filter=lfs diff=lfs merge=lfs -text
24
+ *.m4v filter=lfs diff=lfs merge=lfs -text
25
+ *.mkv filter=lfs diff=lfs merge=lfs -text
26
+ *.mov filter=lfs diff=lfs merge=lfs -text
27
+ *.mp3 filter=lfs diff=lfs merge=lfs -text
28
+ *.mp4 filter=lfs diff=lfs merge=lfs -text
29
+ *.ogg filter=lfs diff=lfs merge=lfs -text
30
+ *.pages filter=lfs diff=lfs merge=lfs -text
31
+ *.pdf filter=lfs diff=lfs merge=lfs -text
32
+ *.png filter=lfs diff=lfs merge=lfs -text
33
+ *.PNG filter=lfs diff=lfs merge=lfs -text
34
+ *.psd filter=lfs diff=lfs merge=lfs -text
35
+ *.pxm filter=lfs diff=lfs merge=lfs -text
36
+ *.tar filter=lfs diff=lfs merge=lfs -text
37
+ *.tgz filter=lfs diff=lfs merge=lfs -text
38
+ *.tif filter=lfs diff=lfs merge=lfs -text
39
+ *.tiff filter=lfs diff=lfs merge=lfs -text
40
+ *.webm filter=lfs diff=lfs merge=lfs -text
41
+ *.webp filter=lfs diff=lfs merge=lfs -text
42
+ *.woff2 filter=lfs diff=lfs merge=lfs -text
43
+ *.xlsx filter=lfs diff=lfs merge=lfs -text
44
+ *.zip filter=lfs diff=lfs merge=lfs -text
@@ -13,32 +13,33 @@ jobs:
13
13
  strategy:
14
14
  matrix:
15
15
  ruby:
16
- - 3.0
17
- - 3.1
18
- - 3.2
19
- - 3.3
20
- - jruby
21
- - truffleruby
16
+ - "3.1"
17
+ - "3.2"
18
+ - "3.3"
19
+ - "3.4"
20
+ - "4.0"
22
21
  fail-fast: false
23
22
  steps:
24
- - uses: actions/checkout@v3
23
+ - uses: actions/checkout@v4
24
+ with:
25
+ lfs: true
25
26
  - uses: actions/setup-node@v4
26
27
  with:
27
28
  node-version: 20
28
29
  - run: npm install -g tsx
29
- - uses: ruby/setup-ruby@v1
30
+ - uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f
30
31
  with:
31
32
  ruby-version: ${{ matrix.ruby }}
32
33
  bundler-cache: true
33
34
  - run: bundle exec standardrb
34
35
  - name: Run tests
35
36
  env:
36
- COVERAGE: ${{ matrix.ruby == '3.3' && '1' || '0' }}
37
- TEST_NODE_PARITY: ${{ matrix.ruby == '3.3' && '1' || '0' }}
37
+ COVERAGE: ${{ matrix.ruby == '4.0' && '1' || '0' }}
38
+ TEST_NODE_PARITY: ${{ matrix.ruby == '4.0' && '1' || '0' }}
38
39
  run: bundle exec rake test
39
40
  - name: Upload coverage to Codecov
40
- if: matrix.ruby == '3.3'
41
- uses: codecov/codecov-action@v5
41
+ if: matrix.ruby == '4.0'
42
+ uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f
42
43
  with:
43
44
  token: ${{ secrets.CODECOV_TOKEN }}
44
45
  files: ./coverage/coverage.json
@@ -53,10 +54,12 @@ jobs:
53
54
  TRANSLOADIT_KEY: ${{ secrets.TRANSLOADIT_KEY }}
54
55
  TRANSLOADIT_SECRET: ${{ secrets.TRANSLOADIT_SECRET }}
55
56
  steps:
56
- - uses: actions/checkout@v3
57
- - uses: ruby/setup-ruby@v1
57
+ - uses: actions/checkout@v4
58
+ with:
59
+ lfs: true
60
+ - uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f
58
61
  with:
59
- ruby-version: 3.3
62
+ ruby-version: "4.0"
60
63
  bundler-cache: true
61
64
  - name: Ensure e2e credentials are configured
62
65
  if: ${{ env.TRANSLOADIT_KEY == '' || env.TRANSLOADIT_SECRET == '' }}
data/.gitignore CHANGED
@@ -16,4 +16,4 @@ env.sh
16
16
  .env
17
17
  vendor/bundle/
18
18
  .docker-cache/
19
- .cache/rubocop_cache/04e06e0faf5ad652d8bcbcfd85bac5f6c32e711e/3031a80880d8a984708138f0d003f77c4bad2648
19
+ .cache/
data/.standard.yml CHANGED
@@ -0,0 +1,2 @@
1
+ ignore:
2
+ - ".docker-cache/**/*"
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ### Unreleased
2
+
3
+ ### 4.1.0 / 2026-10-18
4
+
5
+ - Add `uploads: {name => io}` to Assembly options and `create!` for named multipart uploads, separate from signed JSON Instructions.
6
+ - Send custom `fields` only in signed JSON, preserving numbers, booleans, arrays, objects, and null without duplicate form fields.
7
+ - Keep IOs in legacy `fields` working as named uploads throughout v4, omit them from JSON, and warn to migrate to `uploads` before the next major release. Positional `file_N` uploads and `create!(fields: metadata)` overrides remain supported.
8
+ - Restore in-memory uploads to their original position on rate-limit retries and raise a clear `IOError` when a non-seekable upload cannot be retried.
9
+
10
+ ### 4.0.0 / 2026-09-03
11
+
12
+ - BREAKING: Replace the long-dormant `rest-client` transport with Faraday 2 and `faraday-multipart`.
13
+ - BREAKING: Raise the minimum supported Ruby version from 3.0 to 3.1.
14
+ - Keep HTTP client types out of SDK responses and exceptions while preserving response status, headers, body access, multipart ordering, and rate-limit retries.
15
+ - Add a [v3 to v4 migration guide](MIGRATING_TO_V4.md).
16
+ - Test all Ruby versions from the supported minimum through Ruby 4.0.
17
+
1
18
  ### 3.1.2 / 2025-10-29
2
19
 
3
20
  - Compress e2e fixture asset to keep gem size close to previous releases (kvz)
data/Dockerfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # syntax=docker/dockerfile:1
2
2
 
3
- FROM ruby:3.3 AS base
3
+ FROM ruby:4.0 AS base
4
4
 
5
5
  RUN apt-get update \
6
6
  && apt-get install -y --no-install-recommends \
data/Gemfile CHANGED
@@ -5,4 +5,8 @@ gem "minitest"
5
5
  gem "simplecov"
6
6
  gem "simplecov-cobertura"
7
7
 
8
+ # Ruby 3.1 resolves MultiJson 1.19, which cannot parse JSON 3 (ruby-sdk#89).
9
+ # Keep its test bundle compatible without restricting consumers on newer Rubies.
10
+ gem "json", "< 3.0" if RUBY_VERSION.start_with?("3.1.")
11
+
8
12
  gemspec
@@ -0,0 +1,136 @@
1
+ # Migrating from v3 to v4
2
+
3
+ Version 4 replaces the `rest-client` HTTP transport with Faraday 2. Most applications only use the
4
+ Transloadit API exposed by this gem and do not need code changes. The migration is a major release
5
+ because v3 also delegated undocumented methods to RestClient response objects and exposed
6
+ RestClient exception inheritance.
7
+
8
+ Version 4 requires Ruby 3.1 or newer. The final v3 release allowed Ruby 3.0, so applications still
9
+ running Ruby 3.0 must upgrade Ruby before updating this gem.
10
+
11
+ ## Update the dependency
12
+
13
+ Once v4 is released, update the application Gemfile and bundle:
14
+
15
+ ```ruby
16
+ gem "transloadit", "~> 4.0"
17
+ ```
18
+
19
+ ```shell
20
+ bundle update transloadit
21
+ ```
22
+
23
+ The SDK installs Faraday and its multipart middleware. Applications do not need to add Faraday
24
+ directly. Remove an explicit `rest-client` dependency only if the application does not use it for
25
+ anything else. Version 4 no longer installs or loads the `RestClient` constant on the application's
26
+ behalf.
27
+
28
+ ## Responses
29
+
30
+ Use the SDK-owned response API:
31
+
32
+ ```ruby
33
+ response = transloadit.assembly.get(assembly_id)
34
+
35
+ response["ok"]
36
+ response[:assembly_id]
37
+ response.body
38
+ response.headers
39
+ response.code
40
+ response.status
41
+ ```
42
+
43
+ `code` continues to return the integer HTTP status, and `status` is its new explicit alias. Header
44
+ names remain normalized to lowercase symbols with underscores, for example
45
+ `response.headers[:retry_after]`.
46
+
47
+ HTTP error statuses still return `Transloadit::Response` objects. They are not converted into
48
+ transport exceptions:
49
+
50
+ ```ruby
51
+ response = transloadit.assembly.get(assembly_id)
52
+
53
+ if response.code >= 400
54
+ warn response["error"]
55
+ end
56
+ ```
57
+
58
+ ### Remove raw RestClient response usage
59
+
60
+ In v3, `Transloadit::Response` delegated unknown methods to a `RestClient::Response`. Version 4 no
61
+ longer exposes the underlying HTTP client. Replace calls to RestClient-specific response methods
62
+ with the SDK-owned response API above.
63
+
64
+ For example:
65
+
66
+ ```ruby
67
+ # v3: relied on the delegated RestClient object
68
+ response.raw_headers
69
+
70
+ # v4
71
+ response.headers
72
+ ```
73
+
74
+ If an application relies on another delegated method that has no equivalent, open an issue with the
75
+ use case before upgrading.
76
+
77
+ ## Request failures
78
+
79
+ Network, TLS, and timeout failures now raise `Transloadit::Exception::RequestFailed` instead of a
80
+ RestClient exception:
81
+
82
+ ```ruby
83
+ # v3
84
+ begin
85
+ transloadit.assembly.get(assembly_id)
86
+ rescue RestClient::Exception => error
87
+ warn error.message
88
+ end
89
+
90
+ # v4
91
+ begin
92
+ transloadit.assembly.get(assembly_id)
93
+ rescue Transloadit::Exception::RequestFailed => error
94
+ warn error.message
95
+ end
96
+ ```
97
+
98
+ The original adapter exception is retained as `error.cause` for diagnostics. Avoid branching on its
99
+ class so application behavior does not become coupled to Faraday.
100
+
101
+ ## Rate-limit failures
102
+
103
+ Continue rescuing the SDK exception directly:
104
+
105
+ ```ruby
106
+ begin
107
+ assembly.create!(file)
108
+ rescue Transloadit::Exception::RateLimitReached => error
109
+ retry_after = error.response.wait_time
110
+ end
111
+ ```
112
+
113
+ `RateLimitReached` remains a `StandardError` and retains its Transloadit response through
114
+ `error.response`, but it no longer inherits from `RestClient::RequestEntityTooLarge`. Replace code
115
+ that rescues the RestClient superclass with the SDK exception.
116
+
117
+ ## Uploaded file handles
118
+
119
+ Version 4 does not close path-backed file objects supplied by the caller. Prefer a block so ownership
120
+ is explicit:
121
+
122
+ ```ruby
123
+ File.open("video.mp4", "rb") do |file|
124
+ assembly.create!(file)
125
+ end
126
+ ```
127
+
128
+ Multipart field order is unchanged: `params` and `signature` are sent before file bodies.
129
+
130
+ ## Upgrade checklist
131
+
132
+ - Update the gem and run the application's request and upload tests.
133
+ - Replace rescues of `RestClient::Exception` and `RestClient::RequestEntityTooLarge`.
134
+ - Replace calls to delegated RestClient response methods.
135
+ - Confirm the application closes files that it opens.
136
+ - Remove the direct `rest-client` dependency if nothing else uses it.
data/README.md CHANGED
@@ -19,6 +19,9 @@ _If you run Ruby on Rails and are looking to integrate with the browser for file
19
19
  gem install transloadit
20
20
  ```
21
21
 
22
+ Upgrading from v3? See the [v4 migration guide](MIGRATING_TO_V4.md) for response, exception, and
23
+ file-handle compatibility notes.
24
+
22
25
  ## Usage
23
26
 
24
27
  To get started, you need to require the 'transloadit' gem:
@@ -168,6 +171,38 @@ files = [open('puppies.jpg'), open('kittens.jpg'), open('ferrets.jpg')]
168
171
  response = assembly.create! *files
169
172
  ```
170
173
 
174
+ For names that match a Step's `use.fields` selector, pass `uploads: {name => io}`
175
+ to `assembly` or `create!`. Uploads are multipart parts and are excluded from
176
+ the signed JSON Instructions. Open files in binary mode and close them after
177
+ the request; the SDK leaves caller-owned files open.
178
+
179
+ ```ruby
180
+ File.open('puppies.jpg', 'rb') do |photo|
181
+ response = transloadit.assembly(
182
+ steps: {
183
+ resize: {
184
+ robot: '/image/resize',
185
+ use: {fields: ['photo']},
186
+ width: 320,
187
+ height: 240
188
+ }
189
+ },
190
+ uploads: {photo: photo},
191
+ fields: {count: 2, enabled: false, details: {caption: 'Puppies'}}
192
+ ).create!
193
+ end
194
+ ```
195
+
196
+ Alternatively, use `assembly.create!(uploads: {photo: photo}, fields: metadata)`.
197
+ Per-call `uploads` and `fields` each replace the corresponding Assembly option;
198
+ `uploads: {}` clears only the `uploads` option. Legacy IOs configured in `fields`
199
+ remain uploads for compatibility. Positional files still use `file_0`,
200
+ `file_1`, and so on, and take precedence over named uploads with those names.
201
+ Upload values must respond to `read`; the names `params` and `signature` are reserved.
202
+ For rate-limit retries, in-memory uploads must also support `pos` and `seek`;
203
+ the SDK restores their original position. A non-seekable upload raises `IOError`
204
+ if a retry is needed, instead of sending consumed bytes again.
205
+
171
206
  ### 3. Parallel Assembly
172
207
 
173
208
  Transloadit allows you to perform several processing steps in parallel. You
@@ -223,9 +258,10 @@ has some nice examples for that.
223
258
 
224
259
  ### 5. Using fields
225
260
 
226
- Transloadit allows you to submit form field values that you'll get back in the
227
- notification. This is quite handy if you want to add additional custom metadata
228
- to the upload itself. You can use fields like the following:
261
+ Transloadit allows you to submit custom metadata that you'll get back in the
262
+ notification. `fields` is included once in the signed JSON Instructions and is
263
+ never duplicated as form fields. Numbers, booleans, arrays, objects, and null
264
+ retain their JSON types. You can use fields like the following:
229
265
 
230
266
  ```ruby
231
267
  require 'transloadit'
@@ -243,6 +279,17 @@ transloadit.assembly(
243
279
  ).create! open('/PATH/TO/FILE.mpg')
244
280
  ```
245
281
 
282
+ `create!(fields: metadata)` also sets custom fields and replaces any metadata
283
+ configured on the Assembly. Uploads and metadata may use the same name.
284
+
285
+ For compatibility, IO values passed directly in a `fields` hash still upload
286
+ under their field names throughout v4, but emit a deprecation warning. They are
287
+ excluded from JSON; other values in that hash remain custom metadata. Migrate
288
+ from `assembly(fields: {photo: file, count: 2})` to
289
+ `assembly(uploads: {photo: file}, fields: {count: 2})` before the next major release.
290
+ Explicit `uploads` take precedence over legacy IOs with the same name. Existing
291
+ `assembly(fields: {photo: file}).create!(fields: metadata)` calls continue working.
292
+
246
293
  ### 6. Notify URL
247
294
 
248
295
  If you want to be notified when the processing is finished you can provide
@@ -461,6 +508,11 @@ for the latest [git main](https://rubydoc.info/github/transloadit/ruby-sdk/main/
461
508
  Please see [ci.yml](https://github.com/transloadit/ruby-sdk/tree/main/.github/workflows/ci.yml) for a list of supported ruby versions. It may also work on older Rubies, but support for those is not guaranteed. If it doesn't work on one of the officially supported Rubies, please file a
462
509
  [bug report](https://github.com/transloadit/ruby-sdk/issues). Compatibility patches for other Rubies are welcome.
463
510
 
511
+ On Ruby 3.1, add `gem "json", "< 3.0"` to your application's Gemfile while
512
+ [the older MultiJson/JSON compatibility issue](https://github.com/transloadit/ruby-sdk/issues/89)
513
+ is open. Ruby 3.2 and newer can use JSON 3 with
514
+ [MultiJson 1.21.2 or newer](https://github.com/sferik/multi_json/releases/tag/v1.21.2).
515
+
464
516
  ### Ruby 2.x
465
517
 
466
518
  If you still need support for Ruby 2.x, 2.0.1 is the last version that supports it.
@@ -44,25 +44,45 @@ class Transloadit::Assembly < Transloadit::ApiModel
44
44
  # @option params [String] :template_id the ID of a
45
45
  # {template}[https://transloadit.com/templates] to use instead of
46
46
  # specifying params here directly
47
+ # @option params [Hash] :fields custom metadata, retained as typed JSON
48
+ # @option params [Hash<String, IO>] :uploads named multipart uploads;
49
+ # overrides the Assembly's uploads when supplied (use {} to clear them)
47
50
  #
48
51
  def create!(*ios, **params)
49
52
  params[:steps] = _wrap_steps_in_hash(params[:steps]) unless params[:steps].nil?
50
53
 
54
+ legacy_uploads, = _split_fields(options[:fields])
55
+ if params.key?(:fields)
56
+ uploads, params[:fields] = _split_fields(params[:fields])
57
+ legacy_uploads.merge!(uploads)
58
+ end
59
+ unless legacy_uploads.empty?
60
+ warn "Passing upload IOs in fields is deprecated; use uploads: {name => io} instead."
61
+ end
62
+
51
63
  extra_params = {}
52
- extra_params.merge!(options[:fields]) if options[:fields]
64
+ [legacy_uploads, params.delete(:uploads) { options[:uploads] }].compact.each do |uploads|
65
+ uploads.each do |name, io|
66
+ name = name.to_s
67
+ raise ArgumentError, "Upload name #{name.inspect} is reserved" if %w[params signature].include?(name)
68
+ raise ArgumentError, "Upload #{name.inspect} must be an IO responding to read" unless io.respond_to?(:read)
69
+
70
+ extra_params[name] = io
71
+ end
72
+ end
73
+ # Keep positional file_N uploads and their precedence for existing callers.
74
+ ios.each_with_index { |io, i| extra_params["file_#{i}"] = io }
75
+ upload_positions = _upload_positions(extra_params.values)
53
76
 
54
77
  trials = options[:tries] || DEFAULT_TRIES
55
78
  (1..trials).each do |trial|
56
- # update the payload with file entries
57
- ios.each_with_index { |f, i| extra_params.update "file_#{i}": f }
58
-
59
79
  response = _do_request(
60
80
  "/assemblies", params, "post", extra_params
61
81
  ).extend!(Transloadit::Response::Assembly)
62
82
 
63
83
  return response unless response.rate_limit?
64
84
 
65
- _handle_rate_limit!(response, ios, trial < trials)
85
+ _handle_rate_limit!(response, upload_positions, trial < trials)
66
86
  end
67
87
  end
68
88
 
@@ -123,14 +143,37 @@ class Transloadit::Assembly < Transloadit::ApiModel
123
143
  # @return [Hash] a Transloadit-compatible Hash of the Assembly's contents
124
144
  #
125
145
  def to_hash
126
- options.merge(
146
+ result = options.merge(
127
147
  auth: transloadit.to_hash,
128
148
  steps: steps
129
- ).delete_if { |k, v| v.nil? }
149
+ )
150
+ result.delete(:uploads)
151
+ _, result[:fields] = _split_fields(result[:fields]) if result[:fields]
152
+ result.delete_if { |_, value| value.nil? }
130
153
  end
131
154
 
132
155
  private
133
156
 
157
+ # Legacy fields may contain both named IOs and metadata. Only metadata belongs
158
+ # in the signed JSON; the IOs remain supported as uploads throughout v4.
159
+ def _split_fields(fields)
160
+ return [{}, nil] if fields.nil?
161
+
162
+ fields.partition { |_, value| value.respond_to?(:read) }.map(&:to_h)
163
+ end
164
+
165
+ def _upload_positions(uploads)
166
+ uploads.filter_map do |io|
167
+ # The transport reopens path-backed uploads for each attempt.
168
+ next if io.respond_to?(:path) && io.path
169
+
170
+ position = io.pos if io.respond_to?(:pos) && io.respond_to?(:seek)
171
+ [io, position]
172
+ rescue IOError, SystemCallError
173
+ [io, nil]
174
+ end
175
+ end
176
+
134
177
  #
135
178
  # Returns a Transloadit-compatible Hash wrapping the +steps+ passed to it.
136
179
  # Accepts any supported format the +steps+ could come in.
@@ -143,27 +186,43 @@ class Transloadit::Assembly < Transloadit::ApiModel
143
186
  when nil then steps
144
187
  when Hash then steps
145
188
  when Transloadit::Step then steps.to_hash
146
- else
147
- if steps.uniq(&:name) != steps
189
+ when Array
190
+ names = steps.inject([]) do |array, step|
191
+ case step
192
+ when Transloadit::Step then array << step.name
193
+ when Hash then array + step.keys.map(&:to_s)
194
+ else
195
+ raise ArgumentError, "Unsupported array element"
196
+ end
197
+ end
198
+
199
+ if names.uniq != names
148
200
  raise ArgumentError, "There are different Assembly steps using the same name"
149
201
  end
150
202
  steps.inject({}) { |h, s| h.update s }
203
+ else
204
+ raise ArgumentError, "Unsupported steps class"
151
205
  end
152
206
  end
153
207
 
154
208
  #
155
- # Stays idle for certain time and then reopens assembly files for reprocessing.
209
+ # Restores in-memory upload positions and waits before retrying.
156
210
  # Should be called when assembly rate limit is reached.
157
211
  #
158
212
  # @param [Response] response assembly response that comes with a rate limit
159
- # @param [Array<IO>] ios the files sent for the assembly to process.
213
+ # @param [Array<Array>] upload_positions IOs and their positions before upload
160
214
  #
161
- def _handle_rate_limit!(response, ios, is_retrying)
215
+ def _handle_rate_limit!(response, upload_positions, is_retrying)
162
216
  if is_retrying
217
+ upload_positions.each do |io, position|
218
+ raise IOError if position.nil?
219
+
220
+ io.seek(position)
221
+ rescue IOError, SystemCallError
222
+ raise IOError, "Cannot retry a non-seekable upload; use a file or an IO supporting pos and seek"
223
+ end
163
224
  warn "Rate limit reached. Waiting for #{response.wait_time} seconds before retrying."
164
225
  sleep response.wait_time
165
- # RestClient closes file streams at the end of a request.
166
- ios.collect! { |file| File.open file.path }
167
226
  else
168
227
  raise Transloadit::Exception::RateLimitReached.new(response)
169
228
  end
@@ -1,16 +1,24 @@
1
1
  require "transloadit"
2
2
 
3
- require "rest-client"
4
-
5
3
  module Transloadit::Exception
4
+ #
5
+ # Exception raised when an HTTP request cannot be completed.
6
+ # The original transport error is available through +cause+.
7
+ #
8
+ class RequestFailed < StandardError
9
+ end
10
+
6
11
  #
7
12
  # Exception raised when Rate limit error response is returned from the API.
8
13
  # See {Rate Limiting}[https://transloadit.com/docs/api-docs/#rate-limiting]
9
14
  #
10
- class RateLimitReached < RestClient::RequestEntityTooLarge
11
- def default_message
12
- retry_msg = " Retry in #{@response.wait_time} seconds" if @response
13
- "Transloadit Rate Limit Reached.#{retry_msg}"
15
+ class RateLimitReached < StandardError
16
+ # @return [Transloadit::Response] the API response that reported the rate limit
17
+ attr_reader :response
18
+
19
+ def initialize(response)
20
+ @response = response
21
+ super("Transloadit Rate Limit Reached. Retry in #{response.wait_time} seconds")
14
22
  end
15
23
  end
16
24