hi_energy_ai 0.1.0 → 0.1.1

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: b1e97ca461ca7e385a0230330b8630a893e4715141c294f494c296fda9e3c9c8
4
- data.tar.gz: b11f51535e0ea7b9ab64d972c6f8572f02c95c7802556c68505a56dd97cafacb
3
+ metadata.gz: cd365a1dbbaf333ae14c034bede81b783bad7cb87e0e73abeb7ff184e7bbc2a3
4
+ data.tar.gz: 945afdd255cbac87336953de997606713a90d9b12de830cf433c3f01de73ed8a
5
5
  SHA512:
6
- metadata.gz: fcff37287e8139af46061d328a0bab3b89797b628f0b50ccaf409363b7093dd8149b82134117cbba5f928df07ba09f3d2d3ab46426e8d5bf86a40ee1f038f2a2
7
- data.tar.gz: c322f67afd5c2330745a94f99a5ad6e68945d1eebe20be245c0f1e72dc937fa23957f850adbfb159efdb225ea678b679d16dc6ec7c98cbfba6a9bb029962363a
6
+ metadata.gz: e51de8b9891b030bd3e0e122bb3b519401755db2019088e6bdfb1f76e7a6e3bb12d4bd3ae67a1107724b088693b586ac06263e307ce718413200b780b3d1b7f0
7
+ data.tar.gz: 5daef3ce90013508381bef8a2bb9a6b91aa34c145a082ba023b0da2b8a5a3b4385a097f6b02912397799bd9ec5732f00626a70cba7fca3ace17d8a6360ede0be
data/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1 — 2026-07-14
4
+
5
+ ### Added
6
+
7
+ - `HiEnergyAi.new(server_dry_run: true)` and
8
+ `Configuration#server_dry_run` as the preferred name for the existing
9
+ `dry_run` option. The old `dry_run:` keyword still works as an alias.
10
+ Both only set `?dry_run=true` on the request — the server still
11
+ receives the call and your key must be valid.
12
+ - Mutating resource methods (`create`, `update`, `generate`, `add`) now
13
+ accept idiomatic kwargs in addition to the positional Hash:
14
+ `client.contacts.create(email: "x@y.com")` works the same as
15
+ `client.contacts.create({ email: "x@y.com" })`.
16
+ - `bin/publish` release workflow script.
17
+ - RuboCop linting (`bundle exec rubocop`), a combined `rake` task
18
+ (specs + lint), an `.rspec` config, and a RuboCop step in CI.
19
+
20
+ ### Changed
21
+
22
+ - `HiEnergyAi.new` now raises `HiEnergyAi::Error` (with `code: "MISSING_CREDENTIALS"`)
23
+ instead of `ArgumentError` when neither `api_key` nor `bearer_token` is
24
+ configured, so a single `rescue HiEnergyAi::Error` catches all SDK
25
+ failures.
26
+ - Non-JSON error responses (e.g. an HTML 502 from an upstream proxy) are
27
+ now wrapped as `HiEnergyAi::Error` with the HTTP status and
28
+ `code: "INVALID_RESPONSE_BODY"` instead of bubbling up a raw
29
+ `Faraday::ParsingError`.
30
+ - Rewrote the README with a complete Ruby resource reference plus
31
+ response, pagination, configuration, and error-handling documentation
32
+ aligned with the current client, and updated repository metadata URLs.
33
+
34
+ ### Removed
35
+
36
+ - `HiEnergyAi::Resources::Tags#search` (was a silent alias for `#list`).
37
+ Use `client.tags.list(...)` directly.
38
+
3
39
  ## 0.1.0 — 2026-05-18
4
40
 
5
41
  ### Added
data/README.md CHANGED
@@ -26,7 +26,7 @@ Use this gem to integrate Hi Energy AI into Ruby on Rails apps, background jobs,
26
26
  - [Installation](#installation)
27
27
  - [Quick start](#quick-start)
28
28
  - [Authentication](#authentication)
29
- - [API endpoints covered](#api-endpoints-covered)
29
+ - [Ruby resource reference](#ruby-resource-reference)
30
30
  - [Affiliate networks and data sources](#affiliate-networks-and-data-sources)
31
31
  - [MCP and AI agent integration](#mcp-and-ai-agent-integration)
32
32
  - [Pagination, dry run, and rate limits](#pagination-dry-run-and-rate-limits)
@@ -144,35 +144,57 @@ Get your key: [API Documentation → API Key](https://app.hienergy.ai/api_docume
144
144
 
145
145
  ---
146
146
 
147
- ## API endpoints covered
148
-
149
- Every method maps to the [API playground](https://app.hienergy.ai/api_documentation):
150
-
151
- | Ruby client | HTTP | Use case |
152
- |-------------|------|----------|
153
- | `search` | `GET /api/v1/search` | Universal Searchkick search |
154
- | `deals` | `GET /api/v1/deals` | List and filter affiliate deals / coupons |
155
- | `advertisers` | `GET /api/v1/advertisers` | Advertiser discovery and filters |
156
- | `advertisers.search_by_domain` | `GET /api/v1/advertisers/search_by_domain` | Lookup by domain |
157
- | `advertisers.by_domain` | `GET /api/v1/advertisers?domain=` | Domain filter shorthand |
158
- | `contacts` | `GET`, `POST /api/v1/contacts` | Contact search and create |
159
- | `transactions` | `GET /api/v1/transactions` | Affiliate sales and commissions |
160
- | `clicks` | `GET /api/v1/clicks` | Click-level reporting (date range required) |
161
- | `opportunities` | `GET /api/v1/opportunities` | Publisher opportunity advertisers |
162
- | `verticals` | `GET /api/v1/verticals` | Industry / category list |
163
- | `tags` | `GET /api/v1/tags` | Tag search and tagged advertisers |
164
- | `publishers` | CRUD `/api/v1/publishers` | Publisher accounts and credentials |
165
- | `agencies` | `GET /api/v1/agencies` | Agency directory |
166
- | `networks` | `GET /api/v1/networks` | Affiliate network list |
167
- | `reports` | `GET /api/v1/reports` | Prebuilt analytics reports |
168
- | `users` | `/api/v1/users` | Scoped user management |
169
- | `status_changes` | `GET /api/v1/status_changes` | Advertiser approval history |
170
- | `deeplinks` | `POST /api/v1/deeplinks/generate` | Tracking link generator |
171
- | `domains` | `GET /api/v1/domains/search` | Domain search |
172
- | `tools` | `GET /api/v1/tools` | MCP tool catalog (JSON Schema) |
173
- | `schema` | `GET /api/v1/schema` | OpenAPI 3.0 download |
174
- | `exports` | `/api/v1/exports` | Async report exports |
175
- | `mcp` | `GET`, `POST /mcp` | MCP bootstrap and JSON-RPC |
147
+ ## Ruby resource reference
148
+
149
+ All optional keyword arguments are sent as query parameters. Methods that accept
150
+ `attributes` send JSON request bodies.
151
+
152
+ | Resource | Methods |
153
+ |----------|---------|
154
+ | `client.search` | `query(q:, **params)` |
155
+ | `client.advertisers` | `list`, `find(id)`, `search_by_domain(domain:)`, `by_domain(domain:)`, `contacts(id)`, `similar(id)`, `related(id)`, `find_more_contacts(id)` |
156
+ | `client.deals` | `list`, `find(id)`, `types`, `translate(id)` |
157
+ | `client.contacts` | `list`, `create(attributes)`, `add(attributes)` |
158
+ | `client.transactions` | `list`, `find(id)` |
159
+ | `client.clicks` | `list(start_date:, end_date:, **params)` |
160
+ | `client.opportunities` | `list` |
161
+ | `client.reports` | `list`, `find(id)` |
162
+ | `client.publishers` | `list`, `find(id)`, `create(attributes)`, `update(id, attributes)`, `find_linkedin_users(id)` |
163
+ | `client.agencies` | `list`, `find(id)` |
164
+ | `client.networks` | `list`, `find(id)` |
165
+ | `client.status_changes` | `list` |
166
+ | `client.tags` | `list`, `advertisers(id)` |
167
+ | `client.users` | `list`, `find(id)`, `create(attributes)`, `update(id, attributes)`, `resend_invitation(id)`, `rotate_api_key(id)` |
168
+ | `client.verticals` | `list` |
169
+ | `client.domains` | `search(domain:)` |
170
+ | `client.deeplinks` | `generate(attributes)` |
171
+ | `client.exports` | `list`, `find(id)`, `create(attributes)` |
172
+ | `client.tools` | `list` |
173
+ | `client.schema` | `fetch` |
174
+ | `client.mcp` | `bootstrap`, `integration`, `initialize_session`, `call(method, params: {})` |
175
+
176
+ Examples:
177
+
178
+ ```ruby
179
+ client.advertisers.search_by_domain(domain: "example.com", limit: 10)
180
+ client.clicks.list(start_date: "2026-07-01", end_date: "2026-07-13")
181
+
182
+ # Mutating methods accept idiomatic keyword arguments...
183
+ client.publishers.create(name: "Example Publisher", website: "https://example.com")
184
+ client.deeplinks.generate(url: "https://merchant.example/product")
185
+
186
+ # ...or a positional Hash (equivalent, useful for dynamic payloads):
187
+ client.publishers.create({ name: "Example Publisher", website: "https://example.com" })
188
+ ```
189
+
190
+ For endpoint-specific filters and request attributes, use the
191
+ [API playground](https://app.hienergy.ai/api_documentation) or fetch the
192
+ current OpenAPI document:
193
+
194
+ ```ruby
195
+ openapi = client.schema.fetch
196
+ openapi.body
197
+ ```
176
198
 
177
199
  ---
178
200
 
@@ -222,11 +244,31 @@ client.paginate("/deals", params: { limit: 50 }).each do |page|
222
244
  end
223
245
  ```
224
246
 
247
+ Every successful request returns a `HiEnergyAi::Response`:
248
+
249
+ ```ruby
250
+ response = client.deals.list(limit: 10)
251
+
252
+ response.success? # true for HTTP 2xx
253
+ response.status # HTTP status
254
+ response.headers # response headers
255
+ response.body # complete parsed JSON
256
+ response.data # the top-level "data" value
257
+ response.meta # the top-level "meta" value
258
+ response.to_h # normalized response hash
259
+ ```
260
+
225
261
  ### Dry run
226
262
 
227
- Test integration wiring without live data:
263
+ Add `?dry_run=true` to every request. The server still receives the call and
264
+ your key must be valid — it validates request wiring without treating the call
265
+ as production traffic. `server_dry_run:` is the preferred keyword; `dry_run:`
266
+ remains as an alias.
228
267
 
229
268
  ```ruby
269
+ HiEnergyAi.new(api_key: key, server_dry_run: true).deals.list(active: true)
270
+
271
+ # Equivalent (legacy alias):
230
272
  HiEnergyAi.new(api_key: key, dry_run: true).deals.list(active: true)
231
273
  ```
232
274
 
@@ -260,21 +302,54 @@ client = HiEnergyAi.new
260
302
  | `app_origin` | `https://app.hienergy.ai` |
261
303
  | `timeout` | `30` seconds |
262
304
 
305
+ Constructor options override global configuration for one client:
306
+
307
+ ```ruby
308
+ client = HiEnergyAi.new(
309
+ api_key: ENV.fetch("HI_ENERGY_API_KEY"),
310
+ timeout: 60,
311
+ user_agent: "my-app/1.0"
312
+ )
313
+ ```
314
+
315
+ For an endpoint without a resource helper, use the low-level client methods.
316
+ Paths are relative to `base_url`:
317
+
318
+ ```ruby
319
+ client.get("/advertisers", params: { limit: 5 })
320
+ client.post("/exports", body: { report: "transactions" })
321
+ client.patch("/publishers/42", body: { publisher: { name: "New name" } })
322
+ client.delete("/custom_resource/42")
323
+ ```
324
+
263
325
  ---
264
326
 
265
327
  ## Error handling
266
328
 
329
+ Every failure — HTTP errors, missing credentials, and unparseable responses —
330
+ raises `HiEnergyAi::Error`, so a single `rescue` catches all SDK failures.
331
+
267
332
  ```ruby
268
333
  begin
269
334
  client.advertisers.find(999)
270
335
  rescue HiEnergyAi::Error => e
271
- e.code # API error code
272
- e.message # Human-readable message
273
- e.request_id # Support / debugging
274
336
  e.status # HTTP status
337
+ e.code # API error code
338
+ e.message # human-readable message
339
+ e.request_id # support/debugging request ID
340
+ e.details # optional structured error details
341
+ e.response_body # parsed or raw response body
275
342
  end
276
343
  ```
277
344
 
345
+ Notable error codes:
346
+
347
+ | `e.code` | When |
348
+ |----------|------|
349
+ | `MISSING_CREDENTIALS` | `HiEnergyAi.new` was called without `api_key` or `bearer_token` |
350
+ | `INVALID_RESPONSE_BODY` | The server returned a non-JSON body (e.g. an HTML 502 from a proxy) |
351
+ | API-provided codes (`NOT_FOUND`, …) | Returned in the JSON error payload |
352
+
278
353
  ---
279
354
 
280
355
  ## Frequently asked questions
@@ -290,7 +365,7 @@ This repository is the **official Ruby gem**. Other languages can use the REST A
290
365
  ### How do I search advertisers by website domain?
291
366
 
292
367
  ```ruby
293
- client.advertisers.by_domain("amazon.com")
368
+ client.advertisers.by_domain(domain: "amazon.com")
294
369
  # or
295
370
  client.advertisers.search_by_domain(domain: "amazon.com")
296
371
  ```
@@ -310,16 +385,28 @@ Sign in at [app.hienergy.ai](https://app.hienergy.ai) and visit [API Documentati
310
385
  ```bash
311
386
  git clone https://github.com/HiEnergyAgency/hi_energy_api.git
312
387
  cd hi_energy_api
313
- bundle install
314
- bundle exec rspec
388
+ bin/setup # install dependencies
389
+ bundle exec rake # run specs + RuboCop
390
+ ```
391
+
392
+ Individual tasks:
393
+
394
+ ```bash
395
+ bundle exec rspec # run the test suite
396
+ bundle exec rubocop # run the linter
397
+ bundle exec rubocop -a # autocorrect safe offenses
398
+ bin/console # interactive REPL with the gem loaded
315
399
  ```
316
400
 
317
- Interactive console:
401
+ To run the checks, build the gem, and publish the current version to RubyGems:
318
402
 
319
403
  ```bash
320
- bin/console
404
+ bin/publish
321
405
  ```
322
406
 
407
+ Publishing requires an authenticated RubyGems account with MFA. Update
408
+ `HiEnergyAi::VERSION` and `CHANGELOG.md` before publishing a new release.
409
+
323
410
  ---
324
411
 
325
412
  ## Related links
@@ -17,7 +17,8 @@ module HiEnergyAi
17
17
  @configuration ||= Configuration.new
18
18
  end
19
19
 
20
- def initialize(api_key: nil, bearer_token: nil, base_url: nil, app_origin: nil, timeout: nil, user_agent: nil, dry_run: nil)
20
+ def initialize(api_key: nil, bearer_token: nil, base_url: nil, app_origin: nil, timeout: nil, user_agent: nil,
21
+ dry_run: nil, server_dry_run: nil)
21
22
  @config = self.class.configuration.dup
22
23
  @config.api_key = api_key if api_key
23
24
  @config.bearer_token = bearer_token if bearer_token
@@ -25,9 +26,16 @@ module HiEnergyAi
25
26
  @config.app_origin = app_origin if app_origin
26
27
  @config.timeout = timeout if timeout
27
28
  @config.user_agent = user_agent if user_agent
28
- @config.dry_run = dry_run unless dry_run.nil?
29
+ # `server_dry_run` is the preferred name; `dry_run` is kept for
30
+ # backwards compatibility. Both only set `?dry_run=true` on the
31
+ # request — the server still receives the call and your key must
32
+ # be valid. See README "Dry run" for details.
33
+ effective_dry_run = server_dry_run.nil? ? dry_run : server_dry_run
34
+ @config.dry_run = effective_dry_run unless effective_dry_run.nil?
29
35
 
30
- raise ArgumentError, "api_key or bearer_token is required" unless @config.credentials_present?
36
+ return if @config.credentials_present?
37
+
38
+ raise Error.new("api_key or bearer_token is required", code: "MISSING_CREDENTIALS")
31
39
  end
32
40
 
33
41
  def get(path, params: {})
@@ -179,6 +187,22 @@ module HiEnergyAi
179
187
  end
180
188
 
181
189
  handle_response(response)
190
+ rescue Faraday::ParsingError => e
191
+ status, raw_body = extract_parsing_error_context(e)
192
+ message = status ? "API request failed with status #{status}" : "Failed to parse API response"
193
+ raise Error.new(message, status: status, code: "INVALID_RESPONSE_BODY", response_body: raw_body)
194
+ end
195
+
196
+ def extract_parsing_error_context(error)
197
+ env = error.response
198
+ case env
199
+ when Hash
200
+ [env[:status], env[:body]]
201
+ when Faraday::Response
202
+ [env.status, env.body]
203
+ else
204
+ [nil, nil]
205
+ end
182
206
  end
183
207
 
184
208
  def apply_auth!(req)
@@ -9,6 +9,9 @@ module HiEnergyAi
9
9
 
10
10
  attr_accessor :api_key, :bearer_token, :base_url, :app_origin, :timeout, :user_agent, :dry_run
11
11
 
12
+ alias server_dry_run dry_run
13
+ alias server_dry_run= dry_run=
14
+
12
15
  def initialize
13
16
  @app_origin = APP_ORIGIN
14
17
  @base_url = API_BASE_URL
@@ -2,6 +2,8 @@
2
2
 
3
3
  module HiEnergyAi
4
4
  class Resource
5
+ UNSET = Object.new.freeze
6
+
5
7
  def initialize(client)
6
8
  @client = client
7
9
  end
@@ -33,5 +35,22 @@ module HiEnergyAi
33
35
  def app_post(path, params: {}, body: nil)
34
36
  client.app_post(path, params: params, body: body)
35
37
  end
38
+
39
+ # Used by mutating methods that accept either a positional Hash of
40
+ # body attributes (legacy) or keyword arguments (idiomatic Ruby).
41
+ # When a positional hash is given (including an explicit `nil`),
42
+ # kwargs are treated as query-string params; when the positional
43
+ # argument is omitted entirely, all kwargs become body attributes.
44
+ # Reject calls that provide neither, so mutating requests do not
45
+ # silently send an empty body.
46
+ def split_attributes(attributes, params)
47
+ if attributes.equal?(UNSET)
48
+ raise ArgumentError, "attributes are required" if params.empty?
49
+
50
+ [params, {}]
51
+ else
52
+ [attributes, params]
53
+ end
54
+ end
36
55
  end
37
56
  end
@@ -7,12 +7,14 @@ module HiEnergyAi
7
7
  get("/contacts", params: params)
8
8
  end
9
9
 
10
- def create(attributes, **params)
11
- post("/contacts", params: params, body: { contact: attributes })
10
+ def create(attributes = UNSET, **params)
11
+ body_attrs, query = split_attributes(attributes, params)
12
+ post("/contacts", params: query, body: { contact: body_attrs })
12
13
  end
13
14
 
14
- def add(attributes, **params)
15
- post("/contacts/add", params: params, body: { contact: attributes })
15
+ def add(attributes = UNSET, **params)
16
+ body_attrs, query = split_attributes(attributes, params)
17
+ post("/contacts/add", params: query, body: { contact: body_attrs })
16
18
  end
17
19
  end
18
20
  end
@@ -3,8 +3,9 @@
3
3
  module HiEnergyAi
4
4
  module Resources
5
5
  class Deeplinks < Resource
6
- def generate(attributes, **params)
7
- post("/deeplinks/generate", params: params, body: attributes)
6
+ def generate(attributes = UNSET, **params)
7
+ body_attrs, query = split_attributes(attributes, params)
8
+ post("/deeplinks/generate", params: query, body: body_attrs)
8
9
  end
9
10
  end
10
11
  end
@@ -11,8 +11,9 @@ module HiEnergyAi
11
11
  get("/exports/#{id}", params: params)
12
12
  end
13
13
 
14
- def create(attributes, **params)
15
- post("/exports", params: params, body: attributes)
14
+ def create(attributes = UNSET, **params)
15
+ body_attrs, query = split_attributes(attributes, params)
16
+ post("/exports", params: query, body: body_attrs)
16
17
  end
17
18
  end
18
19
  end
@@ -11,12 +11,14 @@ module HiEnergyAi
11
11
  get("/publishers/#{id}", params: params)
12
12
  end
13
13
 
14
- def create(attributes, **params)
15
- post("/publishers", params: params, body: { publisher: attributes })
14
+ def create(attributes = UNSET, **params)
15
+ body_attrs, query = split_attributes(attributes, params)
16
+ post("/publishers", params: query, body: { publisher: body_attrs })
16
17
  end
17
18
 
18
- def update(id, attributes, **params)
19
- patch("/publishers/#{id}", params: params, body: { publisher: attributes })
19
+ def update(id, attributes = UNSET, **params)
20
+ body_attrs, query = split_attributes(attributes, params)
21
+ patch("/publishers/#{id}", params: query, body: { publisher: body_attrs })
20
22
  end
21
23
 
22
24
  def find_linkedin_users(id, **params)
@@ -7,8 +7,6 @@ module HiEnergyAi
7
7
  get("/tags", params: params)
8
8
  end
9
9
 
10
- alias search list
11
-
12
10
  def advertisers(id, **params)
13
11
  get("/tags/#{id}/advertisers", params: params)
14
12
  end
@@ -11,12 +11,14 @@ module HiEnergyAi
11
11
  get("/users/#{id}", params: params)
12
12
  end
13
13
 
14
- def create(attributes, **params)
15
- post("/users", params: params, body: { user: attributes })
14
+ def create(attributes = UNSET, **params)
15
+ body_attrs, query = split_attributes(attributes, params)
16
+ post("/users", params: query, body: { user: body_attrs })
16
17
  end
17
18
 
18
- def update(id, attributes, **params)
19
- patch("/users/#{id}", params: params, body: { user: attributes })
19
+ def update(id, attributes = UNSET, **params)
20
+ body_attrs, query = split_attributes(attributes, params)
21
+ patch("/users/#{id}", params: query, body: { user: body_attrs })
20
22
  end
21
23
 
22
24
  def resend_invitation(id, **params)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HiEnergyAi
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
data/lib/hi_energy_ai.rb CHANGED
@@ -10,16 +10,16 @@ require_relative "hi_energy_ai/paginator"
10
10
  require_relative "hi_energy_ai/resource"
11
11
  require_relative "hi_energy_ai/client"
12
12
 
13
- Dir[File.join(__dir__, "hi_energy_ai/resources", "*.rb")].sort.each { |file| require file }
13
+ Dir[File.join(__dir__, "hi_energy_ai/resources", "*.rb")].each { |file| require file }
14
14
 
15
15
  module HiEnergyAi
16
16
  class << self
17
- def new(**options)
18
- Client.new(**options)
17
+ def new(**)
18
+ Client.new(**)
19
19
  end
20
20
 
21
- def configure(&block)
22
- Client.configure(&block)
21
+ def configure(&)
22
+ Client.configure(&)
23
23
  end
24
24
 
25
25
  def documentation_url
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hi_energy_ai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Karsh
@@ -79,6 +79,34 @@ dependencies:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
81
  version: '3.13'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rubocop
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.65'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.65'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rubocop-rspec
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.0'
82
110
  - !ruby/object:Gem::Dependency
83
111
  name: webmock
84
112
  requirement: !ruby/object:Gem::Requirement