gds-api-adapters 63.6.0 → 67.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/Rakefile +3 -4
  4. data/lib/gds_api.rb +2 -14
  5. data/lib/gds_api/asset_manager.rb +1 -1
  6. data/lib/gds_api/base.rb +8 -3
  7. data/lib/gds_api/email_alert_api.rb +12 -13
  8. data/lib/gds_api/imminence.rb +3 -3
  9. data/lib/gds_api/json_client.rb +6 -6
  10. data/lib/gds_api/list_response.rb +6 -6
  11. data/lib/gds_api/performance_platform/data_out.rb +21 -21
  12. data/lib/gds_api/publishing_api.rb +2 -2
  13. data/lib/gds_api/publishing_api/special_route_publisher.rb +1 -1
  14. data/lib/gds_api/response.rb +80 -6
  15. data/lib/gds_api/test_helpers/asset_manager.rb +0 -17
  16. data/lib/gds_api/test_helpers/calendars.rb +0 -9
  17. data/lib/gds_api/test_helpers/content_store.rb +0 -9
  18. data/lib/gds_api/test_helpers/email_alert_api.rb +28 -39
  19. data/lib/gds_api/test_helpers/imminence.rb +11 -14
  20. data/lib/gds_api/test_helpers/licence_application.rb +8 -16
  21. data/lib/gds_api/test_helpers/link_checker_api.rb +0 -8
  22. data/lib/gds_api/test_helpers/local_links_manager.rb +0 -13
  23. data/lib/gds_api/test_helpers/mapit.rb +15 -26
  24. data/lib/gds_api/test_helpers/organisations.rb +13 -18
  25. data/lib/gds_api/test_helpers/performance_platform/data_out.rb +34 -34
  26. data/lib/gds_api/test_helpers/publishing_api.rb +32 -51
  27. data/lib/gds_api/test_helpers/support.rb +0 -5
  28. data/lib/gds_api/test_helpers/support_api.rb +16 -20
  29. data/lib/gds_api/test_helpers/worldwide.rb +51 -31
  30. data/lib/gds_api/version.rb +1 -1
  31. metadata +14 -31
  32. data/lib/gds_api/publishing_api_v2.rb +0 -14
  33. data/lib/gds_api/test_helpers/alias_deprecated.rb +0 -13
  34. data/lib/gds_api/test_helpers/publishing_api_v2.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c63bd76ee4646694f763c71980fcce76ae359bfd12ad2de8437679eb0f389422
4
- data.tar.gz: 5729acfdc3d52d7fd1c7e053a7740bc09424006088e6ce0631fed2144479e267
3
+ metadata.gz: b9e99044909467134a19c5e0f273908fd94afbe72a40f50a5d81dfd4bf467d28
4
+ data.tar.gz: 0535e351b0d6899aa363f61bcd8c0ab9b3137389b4279bf9ff195250773c087d
5
5
  SHA512:
6
- metadata.gz: 7676cf189a66b0b699c08ae50605a4f481da8b775201876e641167b49d7a6fc33a2139c97f4b7d0801af335e3c32929afc7bb8c6cc562cd2999c889f7603f5c3
7
- data.tar.gz: 2c9f91be586e98a61e91dccf0c8bb66a9db31d1bbac59eccabf13e7fc8689f169126907d15a50ad483cb5605934240438ce5e2fbd97243511718be2278e7c359
6
+ metadata.gz: 796c442ffe778c4377f476db1fdae0d52592c776687aa4d52bc4e3e7f3f3a33fe7cd9e48e40af52e532943fe5b0549cf335ae516d7d4dd149e767f26b7142b7c
7
+ data.tar.gz: 3b37865fca30c9e7ff47bd9e8f64fe4579065b48a325eb59cad29a4722170873049c16373f4708c9601e015269875de3ec88612d2a84d64943337e6494aea9fa
data/README.md CHANGED
@@ -5,7 +5,6 @@ A set of API adapters to work with the GDS APIs.
5
5
  Example usage:
6
6
 
7
7
  ```ruby
8
- require 'gds_api/publishing_api'
9
8
  GdsApi.publishing_api.get_content("f3bbdec2-0e62-4520-a7fd-6ffd5d36e03a")
10
9
  ```
11
10
 
data/Rakefile CHANGED
@@ -1,5 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
1
  require "rdoc/task"
4
2
  require "rake/testtask"
5
3
 
@@ -13,7 +11,8 @@ Rake::TestTask.new("test") do |t|
13
11
  t.test_files = FileList["test/**/*_test.rb"]
14
12
  t.warning = false
15
13
  end
16
- task default: :test
14
+
15
+ task default: %i[lint test]
17
16
 
18
17
  require "pact_broker/client/tasks"
19
18
 
@@ -31,5 +30,5 @@ end
31
30
 
32
31
  desc "Run the linter against changed files"
33
32
  task :lint do
34
- sh "bundle exec rubocop --format clang lib test"
33
+ sh "bundle exec rubocop --format clang"
35
34
  end
@@ -1,4 +1,6 @@
1
+ require "addressable"
1
2
  require "plek"
3
+ require "time"
2
4
  require "gds_api/asset_manager"
3
5
  require "gds_api/calendars"
4
6
  require "gds_api/content_store"
@@ -11,7 +13,6 @@ require "gds_api/mapit"
11
13
  require "gds_api/maslow"
12
14
  require "gds_api/organisations"
13
15
  require "gds_api/publishing_api"
14
- require "gds_api/publishing_api_v2"
15
16
  require "gds_api/router"
16
17
  require "gds_api/search"
17
18
  require "gds_api/support"
@@ -139,19 +140,6 @@ module GdsApi
139
140
  )
140
141
  end
141
142
 
142
- # Creates a GdsApi::PublishingApiV2 adapter
143
- #
144
- # This will set a bearer token if a PUBLISHING_API_BEARER_TOKEN environment
145
- # variable is set
146
- #
147
- # @return [GdsApi::PublishingApiV2]
148
- def self.publishing_api_v2(options = {})
149
- GdsApi::PublishingApiV2.new(
150
- Plek.find("publishing-api"),
151
- { bearer_token: ENV["PUBLISHING_API_BEARER_TOKEN"] }.merge(options),
152
- )
153
- end
154
-
155
143
  # Creates a GdsApi::Router adapter for communicating with Router API
156
144
  #
157
145
  # This will set a bearer token if a ROUTER_API_BEARER_TOKEN environment
@@ -136,7 +136,7 @@ class GdsApi::AssetManager < GdsApi::Base
136
136
  #
137
137
  # @raise [HTTPErrorResponse] if the request returns an error
138
138
  def whitehall_asset(legacy_url_path)
139
- get_json("#{base_url}/whitehall_assets/#{Addressable::URI.encode(legacy_url_path)}")
139
+ get_json("#{base_url}/whitehall_assets/#{uri_encode(legacy_url_path)}")
140
140
  end
141
141
 
142
142
  # Updates an asset given a hash with one +file+ attribute
@@ -24,7 +24,8 @@ class GdsApi::Base
24
24
  :put_json,
25
25
  :patch_json,
26
26
  :delete_json,
27
- :get_raw, :get_raw!,
27
+ :get_raw,
28
+ :get_raw!,
28
29
  :put_multipart,
29
30
  :post_multipart
30
31
 
@@ -69,9 +70,9 @@ private
69
70
  param_pairs = params.sort.map { |key, value|
70
71
  case value
71
72
  when Array
72
- value.map { |v|
73
+ value.map do |v|
73
74
  "#{CGI.escape(key.to_s + '[]')}=#{CGI.escape(v.to_s)}"
74
- }
75
+ end
75
76
  else
76
77
  "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
77
78
  end
@@ -79,4 +80,8 @@ private
79
80
 
80
81
  "?#{param_pairs.join('&')}"
81
82
  end
83
+
84
+ def uri_encode(param)
85
+ Addressable::URI.encode(param.to_s)
86
+ end
82
87
  end
@@ -101,7 +101,7 @@ class GdsApi::EmailAlertApi < GdsApi::Base
101
101
  #
102
102
  # @return [nil]
103
103
  def unsubscribe(uuid)
104
- post_json("#{endpoint}/unsubscribe/#{uuid}")
104
+ post_json("#{endpoint}/unsubscribe/#{uri_encode(uuid)}")
105
105
  end
106
106
 
107
107
  # Unsubscribe subscriber from everything
@@ -110,18 +110,19 @@ class GdsApi::EmailAlertApi < GdsApi::Base
110
110
  #
111
111
  # @return [nil]
112
112
  def unsubscribe_subscriber(id)
113
- delete_json("#{endpoint}/subscribers/#{id}")
113
+ delete_json("#{endpoint}/subscribers/#{uri_encode(id)}")
114
114
  end
115
115
 
116
116
  # Subscribe
117
117
  #
118
118
  # @return [Hash] subscription_id
119
- def subscribe(subscriber_list_id:, address:, frequency: "immediately")
119
+ def subscribe(subscriber_list_id:, address:, frequency: "immediately", skip_confirmation_email: false)
120
120
  post_json(
121
121
  "#{endpoint}/subscriptions",
122
122
  subscriber_list_id: subscriber_list_id,
123
123
  address: address,
124
124
  frequency: frequency,
125
+ skip_confirmation_email: skip_confirmation_email,
125
126
  )
126
127
  end
127
128
 
@@ -141,7 +142,7 @@ class GdsApi::EmailAlertApi < GdsApi::Base
141
142
  # subscriber_count
142
143
  # }
143
144
  def get_subscriber_list(slug:)
144
- get_json("#{endpoint}/subscriber-lists/#{slug}")
145
+ get_json("#{endpoint}/subscriber-lists/#{uri_encode(slug)}")
145
146
  end
146
147
 
147
148
  # Get a Subscription
@@ -158,7 +159,7 @@ class GdsApi::EmailAlertApi < GdsApi::Base
158
159
  # source
159
160
  # }
160
161
  def get_subscription(id)
161
- get_json("#{endpoint}/subscriptions/#{id}")
162
+ get_json("#{endpoint}/subscriptions/#{uri_encode(id)}")
162
163
  end
163
164
 
164
165
  # Get the latest Subscription that has the same subscriber_list
@@ -177,7 +178,7 @@ class GdsApi::EmailAlertApi < GdsApi::Base
177
178
  # source
178
179
  # }
179
180
  def get_latest_matching_subscription(id)
180
- get_json("#{endpoint}/subscriptions/#{id}/latest")
181
+ get_json("#{endpoint}/subscriptions/#{uri_encode(id)}/latest")
181
182
  end
182
183
 
183
184
  # Get Subscriptions for a Subscriber
@@ -188,9 +189,9 @@ class GdsApi::EmailAlertApi < GdsApi::Base
188
189
  # @return [Hash] subscriber, subscriptions
189
190
  def get_subscriptions(id:, order: nil)
190
191
  if order
191
- get_json("#{endpoint}/subscribers/#{id}/subscriptions?order=#{order}")
192
+ get_json("#{endpoint}/subscribers/#{uri_encode(id)}/subscriptions?order=#{uri_encode(order)}")
192
193
  else
193
- get_json("#{endpoint}/subscribers/#{id}/subscriptions")
194
+ get_json("#{endpoint}/subscribers/#{uri_encode(id)}/subscriptions")
194
195
  end
195
196
  end
196
197
 
@@ -202,7 +203,7 @@ class GdsApi::EmailAlertApi < GdsApi::Base
202
203
  # @return [Hash] subscriber
203
204
  def change_subscriber(id:, new_address:)
204
205
  patch_json(
205
- "#{endpoint}/subscribers/#{id}",
206
+ "#{endpoint}/subscribers/#{uri_encode(id)}",
206
207
  new_address: new_address,
207
208
  )
208
209
  end
@@ -215,7 +216,7 @@ class GdsApi::EmailAlertApi < GdsApi::Base
215
216
  # @return [Hash] subscription
216
217
  def change_subscription(id:, frequency:)
217
218
  patch_json(
218
- "#{endpoint}/subscriptions/#{id}",
219
+ "#{endpoint}/subscriptions/#{uri_encode(id)}",
219
220
  frequency: frequency,
220
221
  )
221
222
  end
@@ -224,16 +225,14 @@ class GdsApi::EmailAlertApi < GdsApi::Base
224
225
  #
225
226
  # @param [string] address Address to send verification email to
226
227
  # @param [string] destination Path on GOV.UK that subscriber will be emailed
227
- # @param [string, nil] redirect Path on GOV.UK to be encoded into the token for redirecting
228
228
  #
229
229
  # @return [Hash] subscriber
230
230
  #
231
- def send_subscriber_verification_email(address:, destination:, redirect: nil)
231
+ def send_subscriber_verification_email(address:, destination:)
232
232
  post_json(
233
233
  "#{endpoint}/subscribers/auth-token",
234
234
  address: address,
235
235
  destination: destination,
236
- redirect: redirect,
237
236
  )
238
237
  end
239
238
 
@@ -20,8 +20,8 @@ class GdsApi::Imminence < GdsApi::Base
20
20
  end
21
21
 
22
22
  def self.parse_place_hash(place_hash)
23
- location = self.extract_location_hash(place_hash["location"])
24
- address = self.extract_address_hash(place_hash)
23
+ location = extract_location_hash(place_hash["location"])
24
+ address = extract_address_hash(place_hash)
25
25
 
26
26
  place_hash.merge(location).merge(address)
27
27
  end
@@ -31,7 +31,7 @@ class GdsApi::Imminence < GdsApi::Base
31
31
  end
32
32
 
33
33
  def areas_for_postcode(postcode)
34
- url = "#{@endpoint}/areas/#{ERB::Util.url_encode(postcode)}.json"
34
+ url = "#{@endpoint}/areas/#{uri_encode(postcode)}.json"
35
35
  get_json(url)
36
36
  end
37
37
 
@@ -29,7 +29,7 @@ module GdsApi
29
29
  end
30
30
 
31
31
  def self.default_request_with_json_body_headers
32
- self.default_request_headers.merge(self.json_body_headers)
32
+ default_request_headers.merge(json_body_headers)
33
33
  end
34
34
 
35
35
  def self.json_body_headers
@@ -109,7 +109,7 @@ module GdsApi
109
109
  end
110
110
 
111
111
  # If no custom response is given, just instantiate Response
112
- create_response ||= Proc.new { |r| Response.new(r) }
112
+ create_response ||= proc { |r| Response.new(r) }
113
113
  create_response.call(response)
114
114
  end
115
115
 
@@ -177,10 +177,10 @@ module GdsApi
177
177
  ::RestClient::Request.execute(method_params)
178
178
  rescue Errno::ECONNREFUSED => e
179
179
  logger.error loggable.merge(status: "refused", error_message: e.message, error_class: e.class.name, end_time: Time.now.to_f).to_json
180
- raise GdsApi::EndpointNotFound.new("Could not connect to #{url}")
180
+ raise GdsApi::EndpointNotFound, "Could not connect to #{url}"
181
181
  rescue RestClient::Exceptions::Timeout => e
182
182
  logger.error loggable.merge(status: "timeout", error_message: e.message, error_class: e.class.name, end_time: Time.now.to_f).to_json
183
- raise GdsApi::TimedOutException.new
183
+ raise GdsApi::TimedOutException
184
184
  rescue URI::InvalidURIError => e
185
185
  logger.error loggable.merge(status: "invalid_uri", error_message: e.message, error_class: e.class.name, end_time: Time.now.to_f).to_json
186
186
  raise GdsApi::InvalidUrl
@@ -192,10 +192,10 @@ module GdsApi
192
192
  raise
193
193
  rescue Errno::ECONNRESET => e
194
194
  logger.error loggable.merge(status: "connection_reset", error_message: e.message, error_class: e.class.name, end_time: Time.now.to_f).to_json
195
- raise GdsApi::TimedOutException.new
195
+ raise GdsApi::TimedOutException
196
196
  rescue SocketError => e
197
197
  logger.error loggable.merge(status: "socket_error", error_message: e.message, error_class: e.class.name, end_time: Time.now.to_f).to_json
198
- raise GdsApi::SocketErrorException.new
198
+ raise GdsApi::SocketErrorException
199
199
  end
200
200
  end
201
201
  end
@@ -25,7 +25,7 @@ module GdsApi
25
25
  end
26
26
 
27
27
  def has_next_page?
28
- ! page_link("next").nil?
28
+ !page_link("next").nil?
29
29
  end
30
30
 
31
31
  def next_page
@@ -39,7 +39,7 @@ module GdsApi
39
39
  end
40
40
 
41
41
  def has_previous_page?
42
- ! page_link("previous").nil?
42
+ !page_link("previous").nil?
43
43
  end
44
44
 
45
45
  def previous_page
@@ -70,12 +70,12 @@ module GdsApi
70
70
  # point. Note that the responses are stored so subsequent pages will not be
71
71
  # loaded multiple times.
72
72
  def with_subsequent_pages
73
- Enumerator.new { |yielder|
74
- self.each do |i| yielder << i end
73
+ Enumerator.new do |yielder|
74
+ each { |i| yielder << i }
75
75
  if has_next_page?
76
- next_page.with_subsequent_pages.each do |i| yielder << i end
76
+ next_page.with_subsequent_pages.each { |i| yielder << i }
77
77
  end
78
- }
78
+ end
79
79
  end
80
80
 
81
81
  private
@@ -70,40 +70,40 @@ module GdsApi
70
70
 
71
71
  def search_terms(slug)
72
72
  options = {
73
- slug: slug,
74
- transaction: "search-terms",
75
- group_by: "searchKeyword",
76
- collect: "searchUniques:sum",
73
+ slug: slug,
74
+ transaction: "search-terms",
75
+ group_by: "searchKeyword",
76
+ collect: "searchUniques:sum",
77
77
  }
78
78
  statistics(options)
79
79
  end
80
80
 
81
81
  def searches(slug, is_multipart)
82
82
  options = {
83
- slug: slug,
84
- transaction: "search-terms",
85
- group_by: "pagePath",
86
- collect: "searchUniques:sum",
83
+ slug: slug,
84
+ transaction: "search-terms",
85
+ group_by: "pagePath",
86
+ collect: "searchUniques:sum",
87
87
  }
88
88
  statistics(options, is_multipart)
89
89
  end
90
90
 
91
91
  def page_views(slug, is_multipart)
92
92
  options = {
93
- slug: slug,
94
- transaction: "page-statistics",
95
- group_by: "pagePath",
96
- collect: "uniquePageviews:sum",
93
+ slug: slug,
94
+ transaction: "page-statistics",
95
+ group_by: "pagePath",
96
+ collect: "uniquePageviews:sum",
97
97
  }
98
98
  statistics(options, is_multipart)
99
99
  end
100
100
 
101
101
  def problem_reports(slug, is_multipart)
102
102
  options = {
103
- slug: slug,
104
- transaction: "page-contacts",
105
- group_by: "pagePath",
106
- collect: "total:sum",
103
+ slug: slug,
104
+ transaction: "page-contacts",
105
+ group_by: "pagePath",
106
+ collect: "total:sum",
107
107
  }
108
108
  statistics(options, is_multipart)
109
109
  end
@@ -114,11 +114,11 @@ module GdsApi
114
114
  # Backdrop can be found here: https://github.com/alphagov/backdrop
115
115
  def statistics(options, is_multipart = false)
116
116
  params = {
117
- group_by: options[:group_by],
118
- collect: options[:collect],
119
- duration: 42,
120
- period: "day",
121
- end_at: Date.today.to_time.getutc.iso8601,
117
+ group_by: options[:group_by],
118
+ collect: options[:collect],
119
+ duration: 42,
120
+ period: "day",
121
+ end_at: Date.today.to_time.getutc.iso8601,
122
122
  }
123
123
 
124
124
  filter_param = is_multipart ? :filter_by_prefix : :filter_by
@@ -381,7 +381,7 @@ class GdsApi::PublishingApi < GdsApi::Base
381
381
  # @see https://github.com/alphagov/publishing-api/blob/master/doc/api.md#get-v2linkables
382
382
  def get_linkables(document_type: nil)
383
383
  if document_type.nil?
384
- raise ArgumentError.new("Please provide a `document_type`")
384
+ raise ArgumentError, "Please provide a `document_type`"
385
385
  end
386
386
 
387
387
  get_json("#{endpoint}/v2/linkables?document_type=#{document_type}")
@@ -486,7 +486,7 @@ class GdsApi::PublishingApi < GdsApi::Base
486
486
  # publishing_app: 'content-publisher',
487
487
  # rendering_app: 'government-frontend',
488
488
  # }
489
- #)
489
+ # )
490
490
  #
491
491
  # @see https://github.com/alphagov/publishing-api/blob/master/doc/api.md#put-publish-intentbase_path
492
492
  def put_intent(base_path, payload)
@@ -19,7 +19,7 @@ module GdsApi
19
19
  schema_name: options.fetch(:schema_name, "special_route"),
20
20
  title: options.fetch(:title),
21
21
  description: options.fetch(:description, ""),
22
- locale: "en",
22
+ locale: options.fetch(:locale, "en"),
23
23
  details: {},
24
24
  routes: [
25
25
  {
@@ -1,6 +1,5 @@
1
1
  require "json"
2
2
  require "forwardable"
3
- require "rack/cache"
4
3
 
5
4
  module GdsApi
6
5
  # This wraps an HTTP response with a JSON body.
@@ -21,6 +20,77 @@ module GdsApi
21
20
  extend Forwardable
22
21
  include Enumerable
23
22
 
23
+ class CacheControl < Hash
24
+ PATTERN = /([-a-z]+)(?:\s*=\s*([^,\s]+))?,?+/i.freeze
25
+
26
+ def initialize(value = nil)
27
+ parse(value)
28
+ end
29
+
30
+ def public?
31
+ self["public"]
32
+ end
33
+
34
+ def private?
35
+ self["private"]
36
+ end
37
+
38
+ def no_cache?
39
+ self["no-cache"]
40
+ end
41
+
42
+ def no_store?
43
+ self["no-store"]
44
+ end
45
+
46
+ def must_revalidate?
47
+ self["must-revalidate"]
48
+ end
49
+
50
+ def proxy_revalidate?
51
+ self["proxy-revalidate"]
52
+ end
53
+
54
+ def max_age
55
+ self["max-age"].to_i if key?("max-age")
56
+ end
57
+
58
+ def reverse_max_age
59
+ self["r-maxage"].to_i if key?("r-maxage")
60
+ end
61
+ alias_method :r_maxage, :reverse_max_age
62
+
63
+ def shared_max_age
64
+ self["s-maxage"].to_i if key?("r-maxage")
65
+ end
66
+ alias_method :s_maxage, :shared_max_age
67
+
68
+ def to_s
69
+ directives = []
70
+ values = []
71
+
72
+ each do |key, value|
73
+ if value == true
74
+ directives << key
75
+ elsif value
76
+ values << "#{key}=#{value}"
77
+ end
78
+ end
79
+
80
+ (directives.sort + values.sort).join(", ")
81
+ end
82
+
83
+ private
84
+
85
+ def parse(header)
86
+ return if header.nil? || header.empty?
87
+
88
+ header.scan(PATTERN).each do |name, value|
89
+ self[name.downcase] = value || true
90
+ end
91
+ end
92
+ end
93
+
24
94
  def_delegators :to_hash, :[], :"<=>", :each, :dig
25
95
 
26
96
  def initialize(http_response, options = {})
@@ -63,7 +133,7 @@ module GdsApi
63
133
  end
64
134
 
65
135
  def cache_control
66
- @cache_control ||= Rack::Cache::CacheControl.new(headers[:cache_control])
136
+ @cache_control ||= CacheControl.new(headers[:cache_control])
67
137
  end
68
138
 
69
139
  def to_hash
@@ -74,9 +144,13 @@ module GdsApi
74
144
  @parsed_content ||= transform_parsed(JSON.parse(@http_response.body))
75
145
  end
76
146
 
77
- def present?; true; end
147
+ def present?
148
+ true
149
+ end
78
150
 
79
- def blank?; false; end
151
+ def blank?
152
+ false
153
+ end
80
154
 
81
155
  private
82
156
 
@@ -85,7 +159,7 @@ module GdsApi
85
159
 
86
160
  case value
87
161
  when Hash
88
- Hash[value.map { |k, v|
162
+ Hash[value.map do |k, v|
89
163
  # NOTE: Don't bother transforming if the value is nil
90
164
  if k == "web_url" && v
91
165
  # Use relative URLs to route when the web_url value is on the
@@ -98,7 +172,7 @@ module GdsApi
98
172
  else
99
173
  [k, transform_parsed(v)]
100
174
  end
101
- }]
175
+ end]
102
176
  when Array
103
177
  value.map { |v| transform_parsed(v) }
104
178
  else