promoted-ruby-client 0.1.13 → 0.1.17

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: f57c1ad958cfd9bca73352dbc23f3436d006a37dc9dc23c0ae2fe8d77a834bb8
4
- data.tar.gz: a29f8acf50ff316cc52f1091efb8b43a9a4ee72bcb12862d952cf3e186c5811c
3
+ metadata.gz: c2cabc9fa3074e1eed3fd795eaa180a0e47ee18b8eceacb8c3516a347670a5d7
4
+ data.tar.gz: 8c32e19fe9227fe327ec3995e384477e087ed7d5b4ed48bb5dd0b6a78fdf7ac2
5
5
  SHA512:
6
- metadata.gz: 4b0dda2faab0218af8c0d89f199b5a07626a237d840a18f2ff8cef088f64d2fdba4404a43cbf4679a302ae45a2613747ae1b7e13b852213d88a0bc6c4d96964c
7
- data.tar.gz: 9485c248cfff1496c5294f7ce3164857b9f35ddbff23e4d3df3fd50e513803b22058defe896b59a9c8a7aaad7824b602336a119523dcce29c322644331263838
6
+ metadata.gz: daa95d7fa296498252bd833106fddd6f08c21c1ce3fcc3aa8afa2e79d30d04e914bdc978cafa13d436b5364d91d9467f568d210d86d5d948a0500be3de7b2c8a
7
+ data.tar.gz: 757d9829c35ab58512093f0940f1f5d02aef11b23f94523a09878eeb82b717f047e83936b867aa60f34fa8ce9da2aca45d1be8ecf8bb9cad6b4c8de5b047b723
data/Gemfile.lock CHANGED
@@ -1,11 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- promoted-ruby-client (0.1.13)
4
+ promoted-ruby-client (0.1.17)
5
5
  concurrent-ruby (~> 1)
6
- faraday (~> 1.4, >= 1.4.3)
7
- faraday-net_http (~> 1.0)
8
- faraday_middleware (~> 1.0, >= 1.0.0)
6
+ faraday (>= 0.9.0)
7
+ faraday_middleware (>= 0.9.0)
9
8
  net-http-persistent (~> 4.0)
10
9
 
11
10
  GEM
data/README.md CHANGED
@@ -17,6 +17,8 @@ More information at [http://www.promoted.ai](http://www.promoted.ai)
17
17
 
18
18
  ### [Faraday](https://github.com/lostisland/faraday)
19
19
  HTTP client for calling Promoted.
20
+ ### [Net::HTTP::Persistent](https://github.com/drbrain/net-http-persistent)
21
+ Faraday binding (provides connection pool support)
20
22
  ### [Concurrent Ruby](https://github.com/ruby-concurrency/concurrent-ruby)
21
23
  Provides a thread pool for making shadow traffic requests to Delivery API in the background on a subset of calls to ```prepare_for_logging```
22
24
  ## Creating a Client
@@ -50,6 +52,7 @@ Name | Type | Description
50
52
  ```:default_request_headers``` | Hash | Additional headers to send on the request beyond ```x-api-key```. Defaults to {}
51
53
  ```:default_only_log``` | Boolean | If true, the ```deliver``` method will not direct traffic to Delivery API but rather return a request suitable for logging. Defaults to false.
52
54
  ```:should_apply_treatment_func``` | Proc | Called during delivery, accepts an experiment and returns a Boolean indicating whether the request should be considered part of the control group (false) or in the experiment (true). If nil, the default behavior of checking the experiement ```:arm``` is applied.
55
+ ```:warmup``` | Boolean | If true, the client will prime the `Net::HTTP::Persistent` connection pool on construction; this can make the first few calls to Promoted complete faster. Defaults to false.
53
56
 
54
57
  ## Data Types
55
58
 
@@ -216,12 +219,13 @@ metrics_request = {
216
219
  }
217
220
 
218
221
  # OPTIONAL: You can pass a custom function to "compact" insertions before metrics logging.
219
- # Note that the PromotedClient has a class method helper, copy_and_remove_properties, that does just this.
220
- to_compact_metrics_insertion_func = Proc.new do |insertion|
221
- insertion.delete(:properties)
222
- insertion
222
+ # Note that the PromotedClient has a class method helper, remove_all_properties, that does
223
+ # an implementation of this, returning nil to remove ALL properties.
224
+ to_compact_metrics_properties_func = Proc.new do |properties|
225
+ properties[:struct].delete(:active)
226
+ properties
223
227
  end
224
- # metrics_request[:to_compact_metrics_insertion_func] = to_compact_metrics_insertion
228
+ # metrics_request[:to_compact_metrics_properties_func] = to_compact_metrics_properties_func
225
229
 
226
230
  # Create a client
227
231
  client = Promoted::Ruby::Client::PromotedClient.new
data/dev.md CHANGED
@@ -4,5 +4,5 @@
4
4
  2. Get credentials for deployment from 1password.
5
5
  3. Modify `promoted-ruby-client.gemspec`'s push block.
6
6
  4. Run `gem build promoted-ruby-client.gemspec` to generate `gem`.
7
- 5. Run (using new output) `gem push promoted-ruby-client-0.1.13.gem`
7
+ 5. Run (using new output) `gem push promoted-ruby-client-0.1.17.gem`
8
8
  6. Update README with new version.
@@ -30,12 +30,10 @@ module Promoted
30
30
  end
31
31
 
32
32
  ##
33
- # A common compact method implementation.
34
- def self.copy_and_remove_properties
35
- Proc.new do |insertion|
36
- insertion = Hash[insertion]
37
- insertion.delete(:properties)
38
- insertion
33
+ # A common compact properties method implementation.
34
+ def self.remove_all_properties
35
+ Proc.new do |properties|
36
+ nil
39
37
  end
40
38
  end
41
39
 
@@ -73,7 +71,7 @@ module Promoted
73
71
  @delivery_timeout_millis = params[:delivery_timeout_millis] || DEFAULT_DELIVERY_TIMEOUT_MILLIS
74
72
  @metrics_timeout_millis = params[:metrics_timeout_millis] || DEFAULT_METRICS_TIMEOUT_MILLIS
75
73
 
76
- @http_client = FaradayHTTPClient.new
74
+ @http_client = FaradayHTTPClient.new(@logger)
77
75
  @validator = Promoted::Ruby::Client::Validator.new
78
76
 
79
77
  @async_shadow_traffic = true
@@ -97,6 +95,10 @@ module Promoted
97
95
  if params[:enabled] != nil
98
96
  @enabled = params[:enabled] || false
99
97
  end
98
+
99
+ if params[:warmup]
100
+ do_warmup
101
+ end
100
102
  end
101
103
 
102
104
  ##
@@ -109,9 +111,9 @@ module Promoted
109
111
  end
110
112
 
111
113
  ##
112
- # Make a delivery request.
114
+ # Make a delivery request. If @perform_checks is set, input validation will occur and possibly raise errors.
113
115
  def deliver args, headers={}
114
- args = Promoted::Ruby::Client::Util.translate_args(args)
116
+ args = Promoted::Ruby::Client::Util.translate_hash(args)
115
117
 
116
118
  # Respect the enabled state
117
119
  if !@enabled
@@ -124,7 +126,16 @@ module Promoted
124
126
  delivery_request_builder = RequestBuilder.new
125
127
  delivery_request_builder.set_request_params(args)
126
128
 
127
- perform_common_checks!(args) if @perform_checks
129
+ # perform_checks raises errors.
130
+ if @perform_checks
131
+ perform_common_checks!(args)
132
+
133
+ if args[:insertion_page_type] == Promoted::Ruby::Client::INSERTION_PAGING_TYPE['PRE_PAGED'] then
134
+ err = DeliveryInsertionPageType.new
135
+ @logger.error(err) if @logger
136
+ raise err
137
+ end
138
+ end
128
139
 
129
140
  delivery_request_builder.ensure_client_timestamp
130
141
 
@@ -135,10 +146,15 @@ module Promoted
135
146
  only_log = delivery_request_builder.only_log != nil ? delivery_request_builder.only_log : @default_only_log
136
147
  deliver_err = false
137
148
 
138
- if !@pager.validate_paging(delivery_request_builder.full_insertion, delivery_request_builder.request[:paging])
149
+ begin
150
+ @pager.validate_paging(delivery_request_builder.full_insertion, delivery_request_builder.request[:paging])
151
+ rescue InvalidPagingError => err
139
152
  # Invalid input, log and do SDK-side delivery.
140
- @logger.warn("Invalid paging parameters") if @logger
141
- only_log = true
153
+ @logger.warn(err) if @logger
154
+ return {
155
+ insertion: err.default_insertions_page
156
+ # No log request returned when no response insertions due to invalid paging
157
+ }
142
158
  end
143
159
 
144
160
  if !only_log
@@ -203,7 +219,7 @@ module Promoted
203
219
  # Generate a log request for a subsequent call to send_log_request
204
220
  # or for logging via alternative means.
205
221
  def prepare_for_logging args, headers={}
206
- args = Promoted::Ruby::Client::Util.translate_args(args)
222
+ args = Promoted::Ruby::Client::Util.translate_hash(args)
207
223
 
208
224
  if !@enabled
209
225
  return {
@@ -248,6 +264,22 @@ module Promoted
248
264
 
249
265
  private
250
266
 
267
+ def do_warmup
268
+ if !@delivery_endpoint
269
+ # Warmup only supported when delivery is enabled.
270
+ return
271
+ end
272
+
273
+ warmup_url = @delivery_endpoint.reverse.sub("/deliver".reverse, "/healthz".reverse).reverse
274
+ @logger.info("Warming up at #{warmup_url}") if @logger
275
+ 1.upto(20) do
276
+ resp = @http_client.get(warmup_url)
277
+ if resp != "ok"
278
+ @logger.warn("Got a failure warming up") if @logger
279
+ end
280
+ end
281
+ end
282
+
251
283
  def send_request payload, endpoint, timeout_millis, api_key, headers={}, send_async=false
252
284
  resp = nil
253
285
 
@@ -300,13 +332,14 @@ module Promoted
300
332
  delivery_request_builder = RequestBuilder.new
301
333
  delivery_request_builder.set_request_params args
302
334
 
303
- delivery_request_params = delivery_request_builder.delivery_request_params(should_compact: false)
335
+ delivery_request_params = delivery_request_builder.delivery_request_params
304
336
  delivery_request_params[:client_info][:traffic_type] = Promoted::Ruby::Client::TRAFFIC_TYPE['SHADOW']
305
337
 
306
338
  # Call Delivery API and log/ignore errors.
307
339
  start_time = Time.now
340
+ response = nil
308
341
  begin
309
- send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, @delivery_api_key, headers, @async_shadow_traffic)
342
+ response = send_request(delivery_request_params, @delivery_endpoint, @delivery_timeout_millis, @delivery_api_key, headers, @async_shadow_traffic)
310
343
  rescue StandardError => err
311
344
  @logger.warn("Shadow traffic call failed with #{err}") if @logger
312
345
  return
@@ -314,7 +347,8 @@ module Promoted
314
347
 
315
348
  if !@async_shadow_traffic
316
349
  ellapsed_time = Time.now - start_time
317
- @logger.info("Shadow traffic call completed in #{ellapsed_time.to_f * 1000} ms") if @logger
350
+ insertions = response ? response[:insertion] : []
351
+ @logger.info("Shadow traffic call completed in #{ellapsed_time.to_f * 1000} ms with #{insertions.length} insertions") if @logger
318
352
  end
319
353
  end
320
354
 
@@ -7,6 +7,12 @@ module Promoted
7
7
  end
8
8
  end
9
9
 
10
+ class DeliveryInsertionPageType < StandardError
11
+ def message
12
+ 'Delivery insertions must be unpaged'
13
+ end
14
+ end
15
+
10
16
  class EndpointError < StandardError
11
17
  attr_reader :cause
12
18
  def initialize(cause)
@@ -1,15 +1,19 @@
1
1
  require 'faraday'
2
2
  require 'faraday_middleware'
3
+ require 'promoted/ruby/client/util'
3
4
 
4
5
  module Promoted
5
6
  module Ruby
6
7
  module Client
7
8
  class FaradayHTTPClient
8
9
 
9
- def initialize
10
+ def initialize(logger = nil)
10
11
  @conn = Faraday.new do |f|
11
12
  f.request :json
12
13
  f.request :retry, max: 3
14
+ if logger
15
+ f.response :logger, logger, { headers: false, bodies: true, log_level: :debug }
16
+ end
13
17
  f.use Faraday::Response::RaiseError # raises on 4xx and 5xx responses
14
18
  f.adapter :net_http_persistent
15
19
  end
@@ -25,11 +29,15 @@ module Promoted
25
29
 
26
30
  norm_headers = response.headers.transform_keys(&:downcase)
27
31
  if norm_headers["content-type"] != nil && norm_headers["content-type"].start_with?("application/json")
28
- JSON.parse(response.body, :symbolize_names => true)
32
+ Promoted::Ruby::Client::Util.translate_hash(JSON.parse(response.body))
29
33
  else
30
34
  response.body
31
35
  end
32
36
  end
37
+
38
+ def get(endpoint)
39
+ @conn.get(endpoint).body
40
+ end
33
41
  end
34
42
  end
35
43
  end
@@ -1,18 +1,28 @@
1
1
  module Promoted
2
2
  module Ruby
3
3
  module Client
4
+ class InvalidPagingError < StandardError
5
+ attr_reader :default_insertions_page
6
+
7
+ def initialize(message, default_insertions_page)
8
+ super(message)
9
+ @default_insertions_page = default_insertions_page
10
+ end
11
+ end
12
+
4
13
  class Pager
5
14
  def validate_paging (insertions, paging)
6
- if paging && paging[:offset]
7
- return paging[:offset] < insertions.length
15
+ if paging && paging[:offset] && paging[:offset] >= insertions.length
16
+ raise InvalidPagingError.new("Invalid page offset (insertion size #{insertions.length}, offset #{paging[:offset]})", [])
8
17
  end
9
- return true
10
18
  end
11
19
 
12
20
  def apply_paging (insertions, insertion_page_type, paging = nil)
13
- # This is invalid input, stop it before it goes to the server.
14
- if !validate_paging(insertions, paging)
15
- return []
21
+ begin
22
+ validate_paging(insertions, paging)
23
+ rescue InvalidPagingError => err
24
+ # This is invalid input, stop it before it goes to the server.
25
+ return err.default_insertions_page
16
26
  end
17
27
 
18
28
  if !paging
@@ -54,4 +64,4 @@ module Promoted
54
64
  end
55
65
  end
56
66
  end
57
- end
67
+ end
@@ -3,8 +3,8 @@ module Promoted
3
3
  module Client
4
4
  class RequestBuilder
5
5
  attr_reader :session_id, :only_log, :experiment, :client_info,
6
- :view_id, :insertion, :to_compact_delivery_insertion_func,
7
- :request_id, :full_insertion, :use_case, :request, :to_compact_metrics_insertion_func
6
+ :view_id, :insertion, :to_compact_delivery_properties_func,
7
+ :request_id, :full_insertion, :use_case, :request, :to_compact_metrics_properties_func
8
8
 
9
9
  attr_accessor :timing, :user_info, :platform_id
10
10
 
@@ -29,8 +29,8 @@ module Promoted
29
29
  @full_insertion = args[:full_insertion]
30
30
  @user_info = request[:user_info] || { :user_id => nil, :log_user_id => nil}
31
31
  @timing = request[:timing] || { :client_log_timestamp => Time.now.to_i }
32
- @to_compact_metrics_insertion_func = args[:to_compact_metrics_insertion_func]
33
- @to_compact_delivery_insertion_func = args[:to_compact_delivery_insertion_func]
32
+ @to_compact_metrics_properties_func = args[:to_compact_metrics_properties_func]
33
+ @to_compact_delivery_properties_func = args[:to_compact_delivery_properties_func]
34
34
 
35
35
  # If the user didn't create a client request id, we do it for them.
36
36
  request[:client_request_id] = request[:client_request_id] || @id_generator.newID
@@ -52,7 +52,7 @@ module Promoted
52
52
  end
53
53
 
54
54
  # Only used in delivery
55
- def delivery_request_params(should_compact: true)
55
+ def delivery_request_params
56
56
  params = {
57
57
  user_info: user_info,
58
58
  timing: timing,
@@ -66,7 +66,7 @@ module Promoted
66
66
  paging: request[:paging],
67
67
  client_request_id: request[:client_request_id]
68
68
  }
69
- params[:insertion] = should_compact ? compact_delivery_insertions : full_insertion
69
+ params[:insertion] = insertions_with_compact_props(@to_compact_delivery_properties_func)
70
70
 
71
71
  params.clean!
72
72
  end
@@ -80,7 +80,10 @@ module Promoted
80
80
  end
81
81
 
82
82
  props = @full_insertion.each_with_object({}) do |insertion, hash|
83
- hash[insertion[:content_id]] = insertion[:properties]
83
+ if insertion.has_key?(:properties)
84
+ # Don't add nil properties to response insertions.
85
+ hash[insertion[:content_id]] = insertion[:properties]
86
+ end
84
87
  end
85
88
 
86
89
  filled_in_copy = []
@@ -99,10 +102,13 @@ module Promoted
99
102
  params = {
100
103
  user_info: user_info,
101
104
  timing: timing,
102
- cohort_membership: @experiment,
103
105
  client_info: @client_info
104
106
  }
105
107
 
108
+ if @experiment
109
+ params[:cohort_membership] = [@experiment]
110
+ end
111
+
106
112
  # Log request allows for multiple requests but here we only send one.
107
113
  if include_request
108
114
  request[:request_id] = request[:request_id] || @id_generator.newID
@@ -110,18 +116,33 @@ module Promoted
110
116
  end
111
117
 
112
118
  if include_insertions
113
- params[:insertion] = compact_metrics_insertions if include_insertions
119
+ params[:insertion] = compact_metrics_properties if include_insertions
114
120
  add_missing_ids_on_insertions! request, params[:insertion]
115
121
  end
116
122
 
117
123
  params.clean!
118
124
  end
119
125
 
120
- def compact_delivery_insertions
121
- if !@to_compact_delivery_insertion_func
126
+ def compact_one_insertion(insertion, compact_func)
127
+ return insertion if (!compact_func || !insertion[:properties])
128
+
129
+ # Only need a copy if there are properties to compact.
130
+ compact_insertion = insertion.dup
131
+ compact_insertion[:properties] = compact_func.call(insertion[:properties])
132
+ compact_insertion.clean!
133
+ return compact_insertion
134
+ end
135
+
136
+ def insertions_with_compact_props(compact_func)
137
+ if !compact_func
138
+ # Nothing to do, avoid copying the whole array.
122
139
  full_insertion
123
140
  else
124
- full_insertion.map {|insertion| @to_compact_delivery_insertion_func.call(insertion) }
141
+ compact_insertions = Array.new(full_insertion.length)
142
+ full_insertion.each_with_index {|insertion, index|
143
+ compact_insertions[index] = compact_one_insertion(insertion, compact_func)
144
+ }
145
+ compact_insertions
125
146
  end
126
147
  end
127
148
 
@@ -132,7 +153,7 @@ module Promoted
132
153
  end
133
154
 
134
155
  # TODO: This looks overly complicated.
135
- def compact_metrics_insertions
156
+ def compact_metrics_properties
136
157
  @insertion = [] # insertion should be set according to the compact insertion
137
158
  paging = request[:paging] || {}
138
159
  size = paging[:size] ? paging[:size].to_i : 0
@@ -152,7 +173,7 @@ module Promoted
152
173
  insertion_obj[:insertion_id] = @id_generator.newID
153
174
  insertion_obj[:request_id] = request_id
154
175
  insertion_obj[:position] = offset + index
155
- insertion_obj = @to_compact_metrics_insertion_func.call(insertion_obj) if @to_compact_metrics_insertion_func
176
+ insertion_obj = compact_one_insertion(insertion_obj, @to_compact_metrics_properties_func)
156
177
  @insertion << insertion_obj.clean!
157
178
  end
158
179
  @insertion
@@ -2,16 +2,39 @@ module Promoted
2
2
  module Ruby
3
3
  module Client
4
4
  module Util
5
- def self.translate_args(args)
5
+ def self.translate_array(arr)
6
+ sym_arr = Array.new(arr.length)
7
+ arr.each_with_index do |v, i|
8
+ new_v = v
9
+ case v
10
+ when Hash
11
+ new_v = translate_hash(v)
12
+ when Array
13
+ new_v = translate_array(v)
14
+ end
15
+ sym_arr[i] = new_v
16
+ end
17
+ sym_arr
18
+ end
19
+
20
+ def self.translate_hash(args)
6
21
  sym_hash = {}
7
22
  args.each do |k, v|
8
- sym_hash[k.to_s.to_underscore.to_sym] = v.is_a?(Hash) ? translate_args(v) : v
23
+ new_key = k.to_s.to_underscore.to_sym
24
+ case v
25
+ when Hash
26
+ sym_hash[new_key] = translate_hash(v)
27
+ when Array
28
+ sym_hash[new_key] = translate_array(v)
29
+ else
30
+ sym_hash[new_key] = v
31
+ end
9
32
  end
10
33
  sym_hash
11
34
  rescue => e
12
35
  raise 'Unable to parse args. Please pass correct arguments. Must be JSON'
13
36
  end
14
- end
37
+ end
15
38
  end
16
39
  end
17
40
  end
@@ -1,7 +1,7 @@
1
1
  module Promoted
2
2
  module Ruby
3
3
  module Client
4
- VERSION = "0.1.13"
4
+ VERSION = "0.1.17"
5
5
  end
6
6
  end
7
7
  end
@@ -28,9 +28,8 @@ Gem::Specification.new do |spec|
28
28
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ["lib"]
30
30
 
31
- spec.add_runtime_dependency 'faraday', '~> 1.4', '>= 1.4.3'
32
- spec.add_runtime_dependency 'faraday_middleware', '~> 1.0', '>= 1.0.0'
33
- spec.add_runtime_dependency 'faraday-net_http', '~> 1.0'
31
+ spec.add_runtime_dependency 'faraday', '>= 0.9.0'
32
+ spec.add_runtime_dependency 'faraday_middleware', '>= 0.9.0'
34
33
  spec.add_runtime_dependency 'net-http-persistent', '~> 4.0'
35
34
  spec.add_runtime_dependency 'concurrent-ruby', '~> 1'
36
35
 
metadata CHANGED
@@ -1,69 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: promoted-ruby-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - scottmcmaster
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-24 00:00:00.000000000 Z
11
+ date: 2021-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.4'
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: 1.4.3
19
+ version: 0.9.0
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '1.4'
30
24
  - - ">="
31
25
  - !ruby/object:Gem::Version
32
- version: 1.4.3
26
+ version: 0.9.0
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: faraday_middleware
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
31
  - - ">="
38
32
  - !ruby/object:Gem::Version
39
- version: 1.0.0
40
- - - "~>"
41
- - !ruby/object:Gem::Version
42
- version: '1.0'
33
+ version: 0.9.0
43
34
  type: :runtime
44
35
  prerelease: false
45
36
  version_requirements: !ruby/object:Gem::Requirement
46
37
  requirements:
47
38
  - - ">="
48
39
  - !ruby/object:Gem::Version
49
- version: 1.0.0
50
- - - "~>"
51
- - !ruby/object:Gem::Version
52
- version: '1.0'
53
- - !ruby/object:Gem::Dependency
54
- name: faraday-net_http
55
- requirement: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - "~>"
58
- - !ruby/object:Gem::Version
59
- version: '1.0'
60
- type: :runtime
61
- prerelease: false
62
- version_requirements: !ruby/object:Gem::Requirement
63
- requirements:
64
- - - "~>"
65
- - !ruby/object:Gem::Version
66
- version: '1.0'
40
+ version: 0.9.0
67
41
  - !ruby/object:Gem::Dependency
68
42
  name: net-http-persistent
69
43
  requirement: !ruby/object:Gem::Requirement
@@ -193,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
167
  - !ruby/object:Gem::Version
194
168
  version: '0'
195
169
  requirements: []
196
- rubygems_version: 3.0.3
170
+ rubygems_version: 3.2.24
197
171
  signing_key:
198
172
  specification_version: 4
199
173
  summary: A Ruby Client to contact Promoted APIs.