contentful-management 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/.rubocop_todo.yml +3 -3
- data/.travis.yml +4 -3
- data/CHANGELOG.md +5 -0
- data/Gemfile +0 -8
- data/Guardfile +3 -54
- data/README.md +45 -0
- data/Rakefile +0 -5
- data/contentful-management.gemspec +8 -5
- data/lib/contentful/management/client.rb +44 -14
- data/lib/contentful/management/client_upload_methods_factory.rb +19 -0
- data/lib/contentful/management/content_type.rb +19 -19
- data/lib/contentful/management/dynamic_entry.rb +1 -1
- data/lib/contentful/management/entry.rb +57 -50
- data/lib/contentful/management/http_client.rb +7 -1
- data/lib/contentful/management/request.rb +14 -9
- data/lib/contentful/management/resource.rb +7 -7
- data/lib/contentful/management/resource/array_like.rb +2 -2
- data/lib/contentful/management/resource/system_properties.rb +2 -1
- data/lib/contentful/management/resource_builder.rb +6 -3
- data/lib/contentful/management/resource_requester.rb +3 -3
- data/lib/contentful/management/support.rb +1 -1
- data/lib/contentful/management/upload.rb +42 -0
- data/lib/contentful/management/version.rb +1 -1
- data/spec/fixtures/pixel.jpg +0 -0
- data/spec/fixtures/vcr_cassettes/upload/associate_with_asset.yml +426 -0
- data/spec/fixtures/vcr_cassettes/upload/create_file.yml +85 -0
- data/spec/fixtures/vcr_cassettes/upload/create_path.yml +85 -0
- data/spec/fixtures/vcr_cassettes/upload/destroy.yml +133 -0
- data/spec/fixtures/vcr_cassettes/upload/find.yml +82 -0
- data/spec/fixtures/vcr_cassettes/upload/find_not_found.yml +66 -0
- data/spec/lib/contentful/management/client_spec.rb +25 -0
- data/spec/lib/contentful/management/upload_spec.rb +92 -0
- data/spec/spec_helper.rb +9 -1
- metadata +91 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c880b5d6e4f5f9b5cf11599a5177de92c8cfb673
|
4
|
+
data.tar.gz: 57ed6666b81a6767b4c7d5b1b315d2bf64f3e7e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e590866845d60f709bfd6b525f2a7a4ef779373e78e57881e375237f52ea54e3b66a52d221052cab7a572c3a609127cfbb8aa41a0cd3cddaca9a01e447dce278
|
7
|
+
data.tar.gz: f115b17339749173bdbd4c883b8d3cc0d99ceaa398f3b30639959a31ebcfc5751d01a9b61198deae79f5fd019d2c91b883836a2f06f95f8895ec1c4bc2c437af
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
# Offense count: 12
|
10
10
|
Metrics/AbcSize:
|
11
|
-
Max:
|
11
|
+
Max: 45
|
12
12
|
|
13
13
|
# Offense count: 4
|
14
14
|
Metrics/CyclomaticComplexity:
|
@@ -17,8 +17,8 @@ Metrics/CyclomaticComplexity:
|
|
17
17
|
# Offense count: 18
|
18
18
|
# Configuration parameters: CountComments.
|
19
19
|
Metrics/MethodLength:
|
20
|
-
Max:
|
20
|
+
Max: 25
|
21
21
|
|
22
22
|
# Offense count: 2
|
23
23
|
Metrics/PerceivedComplexity:
|
24
|
-
Max:
|
24
|
+
Max: 10
|
data/.travis.yml
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
3
|
- 2.2.3
|
4
|
+
- 2.3.1
|
5
|
+
- 2.4.0
|
4
6
|
- 2.1.1
|
5
7
|
- 2.1
|
6
8
|
- 2.0.0
|
7
|
-
-
|
8
|
-
- jruby-19mode
|
9
|
+
- jruby-head
|
9
10
|
|
10
|
-
before_install: gem install bundler
|
11
|
+
before_install: gem install bundler
|
11
12
|
script: bundle exec rake rspec_rubocop
|
12
13
|
notifications:
|
13
14
|
slack:
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,29 +1,3 @@
|
|
1
|
-
# A sample Guardfile
|
2
|
-
# More info at https://github.com/guard/guard#readme
|
3
|
-
|
4
|
-
## Uncomment and set this to only include directories you want to watch
|
5
|
-
# directories %w(app lib config test spec features) \
|
6
|
-
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
-
|
8
|
-
## Note: if you are using the `directories` clause above and you are not
|
9
|
-
## watching the project directory ('.'), then you will want to move
|
10
|
-
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
-
#
|
12
|
-
# $ mkdir config
|
13
|
-
# $ mv Guardfile config/
|
14
|
-
# $ ln -s config/Guardfile .
|
15
|
-
#
|
16
|
-
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
-
|
18
|
-
# Note: The cmd option is now required due to the increasing number of ways
|
19
|
-
# rspec may be run, below are examples of the most common uses.
|
20
|
-
# * bundler: 'bundle exec rspec'
|
21
|
-
# * bundler binstubs: 'bin/rspec'
|
22
|
-
# * spring: 'bin/rspec' (This will use spring if running and you have
|
23
|
-
# installed the spring binstubs per the docs)
|
24
|
-
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
|
-
# * 'just' rspec: 'rspec'
|
26
|
-
|
27
1
|
group :green_red_refactor, halt_on_fail: true do
|
28
2
|
guard :rspec, cmd: "bundle exec rspec --format documentation" do
|
29
3
|
require "guard/rspec/dsl"
|
@@ -40,36 +14,11 @@ group :green_red_refactor, halt_on_fail: true do
|
|
40
14
|
# Ruby files
|
41
15
|
ruby = dsl.ruby
|
42
16
|
dsl.watch_spec_files_for(ruby.lib_files)
|
43
|
-
|
44
|
-
# Rails files
|
45
|
-
rails = dsl.rails(view_extensions: %w(erb haml slim))
|
46
|
-
dsl.watch_spec_files_for(rails.app_files)
|
47
|
-
dsl.watch_spec_files_for(rails.views)
|
48
|
-
|
49
|
-
watch(rails.controllers) do |m|
|
50
|
-
[
|
51
|
-
rspec.spec.("routing/#{m[1]}_routing"),
|
52
|
-
rspec.spec.("controllers/#{m[1]}_controller"),
|
53
|
-
rspec.spec.("acceptance/#{m[1]}")
|
54
|
-
]
|
55
|
-
end
|
56
|
-
|
57
|
-
# Rails config changes
|
58
|
-
watch(rails.spec_helper) { rspec.spec_dir }
|
59
|
-
watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
60
|
-
watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
61
|
-
|
62
|
-
# Capybara features specs
|
63
|
-
watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
|
64
|
-
watch(rails.layouts) { |m| rspec.spec.("features/#{m[1]}") }
|
65
|
-
|
66
|
-
# Turnip features and steps
|
67
|
-
watch(%r{^spec/acceptance/(.+)\.feature$})
|
68
|
-
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
69
|
-
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
70
|
-
end
|
71
17
|
end
|
72
18
|
|
73
19
|
guard :rubocop, cmd: "rubocop" do
|
74
20
|
end
|
21
|
+
|
22
|
+
guard :yard, cmd: "yard doc" do
|
23
|
+
end
|
75
24
|
end
|
data/README.md
CHANGED
@@ -196,6 +196,51 @@ Checking if an asset is published:
|
|
196
196
|
my_image_asset.published?
|
197
197
|
```
|
198
198
|
|
199
|
+
### File Uploads
|
200
|
+
|
201
|
+
Creating an upload from a file path:
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
upload = client.uploads.create('space_id', '/path/to/file.md')
|
205
|
+
```
|
206
|
+
|
207
|
+
Alternatively, create it from an `::IO` object:
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
File.open('/path/to/file.md', 'rb') do |file|
|
211
|
+
upload = client.uploads.create('space_id', file)
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
Finding an upload:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
upload = client.uploads.find('space_id', 'upload_id')
|
219
|
+
```
|
220
|
+
|
221
|
+
Deleting an upload
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
upload.destroy
|
225
|
+
```
|
226
|
+
|
227
|
+
Associating an upload with an asset:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
# We find or create an upload:
|
231
|
+
upload = client.uploads.find('space_id', 'upload_id')
|
232
|
+
|
233
|
+
# We create a File object with the associated upload:
|
234
|
+
file = Contentful::Management::File.new
|
235
|
+
file.properties[:contentType] = 'text/plain'
|
236
|
+
file.properties[:fileName] = 'file.md'
|
237
|
+
file.properties[:uploadFrom] = upload.to_link_json # We create the Link from the upload.
|
238
|
+
|
239
|
+
# We create an asset with the associated file:
|
240
|
+
asset = client.assets.create('space_id', title: 'My Upload', file: file)
|
241
|
+
asset.process_file # We process the file, to generate an URL for our upload.
|
242
|
+
```
|
243
|
+
|
199
244
|
### Entries
|
200
245
|
|
201
246
|
Retrieving all entries from the space:
|
data/Rakefile
CHANGED
@@ -24,11 +24,6 @@ RSpec::Core::RakeTask.new
|
|
24
24
|
require 'rubocop/rake_task'
|
25
25
|
RuboCop::RakeTask.new
|
26
26
|
|
27
|
-
require 'reek/rake/task'
|
28
|
-
Reek::Rake::Task.new do |t|
|
29
|
-
t.fail_on_error = false
|
30
|
-
end
|
31
|
-
|
32
27
|
desc 'Run specs, rubocop and reek'
|
33
28
|
task ci: %w(spec reek rubocop)
|
34
29
|
|
@@ -17,19 +17,22 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ['lib']
|
19
19
|
|
20
|
-
spec.add_dependency 'http', '
|
20
|
+
spec.add_dependency 'http', '> 1.0', '< 3.0'
|
21
21
|
spec.add_dependency 'multi_json', '~> 1'
|
22
22
|
spec.add_dependency 'json', '~> 1.8'
|
23
23
|
|
24
24
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
25
25
|
spec.add_development_dependency 'rake', '< 11.0'
|
26
26
|
spec.add_development_dependency 'public_suffix', '< 1.5'
|
27
|
-
|
28
27
|
spec.add_development_dependency 'rspec', '~> 3'
|
29
28
|
spec.add_development_dependency 'rspec-its'
|
30
|
-
spec.add_development_dependency '
|
31
|
-
spec.add_development_dependency '
|
32
|
-
spec.add_development_dependency '
|
29
|
+
spec.add_development_dependency 'guard'
|
30
|
+
spec.add_development_dependency 'guard-rspec'
|
31
|
+
spec.add_development_dependency 'guard-rubocop'
|
32
|
+
spec.add_development_dependency 'guard-yard'
|
33
|
+
spec.add_development_dependency 'rubocop', '~> 0.41.0'
|
34
|
+
spec.add_development_dependency 'listen', '3.0.0'
|
33
35
|
spec.add_development_dependency 'vcr'
|
34
36
|
spec.add_development_dependency 'webmock', '~> 1', '>= 1.17.3'
|
37
|
+
spec.add_development_dependency 'simplecov'
|
35
38
|
end
|
@@ -14,6 +14,7 @@ require 'contentful/management/client_locale_methods_factory'
|
|
14
14
|
require 'contentful/management/client_role_methods_factory'
|
15
15
|
require 'contentful/management/client_editor_interface_methods_factory'
|
16
16
|
require 'contentful/management/client_webhook_methods_factory'
|
17
|
+
require 'contentful/management/client_upload_methods_factory'
|
17
18
|
require 'contentful/management/client_snapshot_methods_factory'
|
18
19
|
|
19
20
|
require_relative 'request'
|
@@ -34,6 +35,7 @@ module Contentful
|
|
34
35
|
# Default configuration for Contentful::Management::Client
|
35
36
|
DEFAULT_CONFIGURATION = {
|
36
37
|
api_url: 'api.contentful.com',
|
38
|
+
uploads_url: 'upload.contentful.com',
|
37
39
|
api_version: '1',
|
38
40
|
secure: true,
|
39
41
|
default_locale: 'en-US',
|
@@ -48,9 +50,10 @@ module Contentful
|
|
48
50
|
proxy_password: nil,
|
49
51
|
max_rate_limit_retries: 1,
|
50
52
|
max_rate_limit_wait: 60
|
51
|
-
}
|
53
|
+
}.freeze
|
54
|
+
|
52
55
|
# Rate Limit Reset Header Key
|
53
|
-
RATE_LIMIT_RESET_HEADER_KEY = 'x-contentful-ratelimit-reset'
|
56
|
+
RATE_LIMIT_RESET_HEADER_KEY = 'x-contentful-ratelimit-reset'.freeze
|
54
57
|
|
55
58
|
# @param [String] access_token
|
56
59
|
# @param [Hash] configuration
|
@@ -156,6 +159,15 @@ module Contentful
|
|
156
159
|
ClientWebhookMethodsFactory.new(self)
|
157
160
|
end
|
158
161
|
|
162
|
+
# Allows manipulation of uploads in context of the current client
|
163
|
+
# Allows creating new and finding uploads by id.
|
164
|
+
# @see _ README for details.
|
165
|
+
#
|
166
|
+
# @return [Contentful::Management::ClientUploadMethodsFactory]
|
167
|
+
def uploads
|
168
|
+
ClientUploadMethodsFactory.new(self)
|
169
|
+
end
|
170
|
+
|
159
171
|
# Allows manipulation of snapshots in context of the current client
|
160
172
|
# Allows listing all webhooks for client, creating new and finding one by id.
|
161
173
|
# @see _ README for details.
|
@@ -223,11 +235,11 @@ module Contentful
|
|
223
235
|
# @private
|
224
236
|
def execute_request(request)
|
225
237
|
retries_left = configuration[:max_rate_limit_retries]
|
226
|
-
|
227
|
-
url = request.absolute? ?
|
238
|
+
host = host_url(request)
|
239
|
+
url = request.absolute? ? request.url : host + request.url
|
228
240
|
|
229
241
|
begin
|
230
|
-
logger.info(request: { url: url, query: request.query, header: request_headers }) if logger
|
242
|
+
logger.info(request: { url: url, query: request.query, header: request_headers(request) }) if logger
|
231
243
|
raw_response = yield(url)
|
232
244
|
logger.debug(response: raw_response) if logger
|
233
245
|
clear_headers
|
@@ -237,10 +249,7 @@ module Contentful
|
|
237
249
|
reset_time = rate_limit_error.response.raw[RATE_LIMIT_RESET_HEADER_KEY].to_i
|
238
250
|
if should_retry(retries_left, reset_time, configuration[:max_rate_limit_wait])
|
239
251
|
retries_left -= 1
|
240
|
-
retry_message
|
241
|
-
retry_message += "Retrying - Retries left: #{retries_left}"
|
242
|
-
retry_message += "- Time until reset (seconds): #{reset_time}"
|
243
|
-
logger.info(retry_message) if logger
|
252
|
+
logger.info(retry_message(retries_left, reset_time)) if logger
|
244
253
|
sleep(reset_time * Random.new.rand(1.0..1.2))
|
245
254
|
retry
|
246
255
|
end
|
@@ -251,6 +260,20 @@ module Contentful
|
|
251
260
|
result
|
252
261
|
end
|
253
262
|
|
263
|
+
# @private
|
264
|
+
def host_url(request)
|
265
|
+
(%r{^/[\w|-]+/uploads(?:/[\w|-]*)?$} =~ request.url) ? uploads_url : base_url
|
266
|
+
end
|
267
|
+
|
268
|
+
# @private
|
269
|
+
def retry_message(retries_left, reset_time)
|
270
|
+
retry_message = 'Contentful Management API Rate Limit Hit! '
|
271
|
+
retry_message += "Retrying - Retries left: #{retries_left}"
|
272
|
+
retry_message += "- Time until reset (seconds): #{reset_time}"
|
273
|
+
|
274
|
+
retry_message
|
275
|
+
end
|
276
|
+
|
254
277
|
# @private
|
255
278
|
def should_retry(retries_left, reset_time, max_wait)
|
256
279
|
retries_left > 0 && max_wait > reset_time
|
@@ -266,28 +289,28 @@ module Contentful
|
|
266
289
|
# @private
|
267
290
|
def delete(request)
|
268
291
|
execute_request(request) do |url|
|
269
|
-
self.class.delete_http(url, {}, request_headers, proxy_parameters)
|
292
|
+
self.class.delete_http(url, {}, request_headers(request), proxy_parameters)
|
270
293
|
end
|
271
294
|
end
|
272
295
|
|
273
296
|
# @private
|
274
297
|
def get(request)
|
275
298
|
execute_request(request) do |url|
|
276
|
-
self.class.get_http(url, request.query, request_headers, proxy_parameters)
|
299
|
+
self.class.get_http(url, request.query, request_headers(request), proxy_parameters)
|
277
300
|
end
|
278
301
|
end
|
279
302
|
|
280
303
|
# @private
|
281
304
|
def post(request)
|
282
305
|
execute_request(request) do |url|
|
283
|
-
self.class.post_http(url, request.query, request_headers, proxy_parameters)
|
306
|
+
self.class.post_http(url, request.query, request_headers(request), proxy_parameters)
|
284
307
|
end
|
285
308
|
end
|
286
309
|
|
287
310
|
# @private
|
288
311
|
def put(request)
|
289
312
|
execute_request(request) do |url|
|
290
|
-
self.class.put_http(url, request.query, request_headers, proxy_parameters)
|
313
|
+
self.class.put_http(url, request.query, request_headers(request), proxy_parameters)
|
291
314
|
end
|
292
315
|
end
|
293
316
|
|
@@ -296,6 +319,11 @@ module Contentful
|
|
296
319
|
"#{protocol}://#{configuration[:api_url]}/spaces"
|
297
320
|
end
|
298
321
|
|
322
|
+
# @private
|
323
|
+
def uploads_url
|
324
|
+
"#{protocol}://#{configuration[:uploads_url]}/spaces"
|
325
|
+
end
|
326
|
+
|
299
327
|
# @private
|
300
328
|
def default_locale
|
301
329
|
configuration[:default_locale]
|
@@ -348,7 +376,7 @@ module Contentful
|
|
348
376
|
|
349
377
|
# @todo headers should be supplied differently, maybe through the request object.
|
350
378
|
# @private
|
351
|
-
def request_headers
|
379
|
+
def request_headers(request = nil)
|
352
380
|
headers = {}
|
353
381
|
headers.merge! user_agent
|
354
382
|
headers.merge! authentication_header
|
@@ -358,6 +386,8 @@ module Contentful
|
|
358
386
|
headers.merge! zero_length_header if zero_length
|
359
387
|
headers.merge! content_type_header(content_type_id) if content_type_id
|
360
388
|
headers.merge! accept_encoding_header('gzip') if gzip_encoded
|
389
|
+
|
390
|
+
headers.merge! request.headers unless request.nil?
|
361
391
|
headers
|
362
392
|
end
|
363
393
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'client_association_methods_factory'
|
2
|
+
|
3
|
+
module Contentful
|
4
|
+
module Management
|
5
|
+
# Wrapper for Upload API for usage from within Client
|
6
|
+
# @private
|
7
|
+
class ClientUploadMethodsFactory
|
8
|
+
include Contentful::Management::ClientAssociationMethodsFactory
|
9
|
+
|
10
|
+
def new(*)
|
11
|
+
fail 'Not supported'
|
12
|
+
end
|
13
|
+
|
14
|
+
def all(*)
|
15
|
+
fail 'Not supported'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -14,17 +14,17 @@ module Contentful
|
|
14
14
|
class ContentType
|
15
15
|
# Shortcuts for Contentful Field Types
|
16
16
|
FIELD_TYPES = [
|
17
|
-
SYMBOL = 'Symbol',
|
18
|
-
TEXT = 'Text',
|
19
|
-
INTEGER = 'Integer',
|
20
|
-
FLOAT = 'Number',
|
21
|
-
DATE = 'Date',
|
22
|
-
BOOLEAN = 'Boolean',
|
23
|
-
LINK = 'Link',
|
24
|
-
ARRAY = 'Array',
|
25
|
-
OBJECT = 'Object',
|
26
|
-
LOCATION = 'Location'
|
27
|
-
]
|
17
|
+
SYMBOL = 'Symbol'.freeze,
|
18
|
+
TEXT = 'Text'.freeze,
|
19
|
+
INTEGER = 'Integer'.freeze,
|
20
|
+
FLOAT = 'Number'.freeze,
|
21
|
+
DATE = 'Date'.freeze,
|
22
|
+
BOOLEAN = 'Boolean'.freeze,
|
23
|
+
LINK = 'Link'.freeze,
|
24
|
+
ARRAY = 'Array'.freeze,
|
25
|
+
OBJECT = 'Object'.freeze,
|
26
|
+
LOCATION = 'Location'.freeze
|
27
|
+
].freeze
|
28
28
|
|
29
29
|
include Contentful::Management::Resource
|
30
30
|
include Contentful::Management::Resource::SystemProperties
|
@@ -42,9 +42,9 @@ module Contentful
|
|
42
42
|
ClientContentTypeMethodsFactory
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
alias activate publish
|
46
|
+
alias deactivate unpublish
|
47
|
+
alias active? published?
|
48
48
|
|
49
49
|
# @private
|
50
50
|
def self.create_attributes(_client, attributes)
|
@@ -97,7 +97,7 @@ module Contentful
|
|
97
97
|
end
|
98
98
|
|
99
99
|
# @private
|
100
|
-
|
100
|
+
alias orig_fields fields
|
101
101
|
|
102
102
|
# Use this method only in the context of content type.
|
103
103
|
# Allows you to add and create a field with specified attributes or destroy by pass field id.
|
@@ -164,10 +164,10 @@ module Contentful
|
|
164
164
|
|
165
165
|
def query_attributes(attributes)
|
166
166
|
parameters = {}
|
167
|
-
parameters
|
168
|
-
parameters
|
169
|
-
parameters
|
170
|
-
parameters
|
167
|
+
parameters[:displayField] = display_field_value(attributes)
|
168
|
+
parameters[:name] = attributes[:name] || name
|
169
|
+
parameters[:description] = attributes[:description] || description
|
170
|
+
parameters[:fields] = self.class.fields_to_nested_properties_hash(attributes[:fields] || fields)
|
171
171
|
|
172
172
|
parameters.delete_if { |_, v| v.nil? }
|
173
173
|
end
|