minfraud 2.9.0 → 2.10.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +12 -9
  5. data/lib/minfraud/assessments.rb +1 -1
  6. data/lib/minfraud/components/account.rb +1 -1
  7. data/lib/minfraud/components/base.rb +3 -0
  8. data/lib/minfraud/components/billing.rb +1 -1
  9. data/lib/minfraud/components/credit_card.rb +8 -7
  10. data/lib/minfraud/components/custom_inputs.rb +1 -1
  11. data/lib/minfraud/components/device.rb +8 -1
  12. data/lib/minfraud/components/email.rb +1 -1
  13. data/lib/minfraud/components/event.rb +3 -3
  14. data/lib/minfraud/components/order.rb +2 -2
  15. data/lib/minfraud/components/payment.rb +5 -1
  16. data/lib/minfraud/components/report/transaction.rb +5 -4
  17. data/lib/minfraud/components/shipping.rb +1 -1
  18. data/lib/minfraud/components/shopping_cart.rb +1 -1
  19. data/lib/minfraud/components/shopping_cart_item.rb +4 -4
  20. data/lib/minfraud/enum.rb +6 -0
  21. data/lib/minfraud/http_service/response.rb +4 -2
  22. data/lib/minfraud/model/abstract.rb +3 -0
  23. data/lib/minfraud/model/device.rb +1 -1
  24. data/lib/minfraud/model/email.rb +2 -1
  25. data/lib/minfraud/model/email_domain.rb +3 -2
  26. data/lib/minfraud/model/email_domain_visit.rb +10 -7
  27. data/lib/minfraud/model/factors.rb +1 -1
  28. data/lib/minfraud/model/geoip2_location.rb +1 -1
  29. data/lib/minfraud/model/insights.rb +1 -1
  30. data/lib/minfraud/model/ip_address.rb +1 -1
  31. data/lib/minfraud/model/reason.rb +1 -1
  32. data/lib/minfraud/model/shipping_address.rb +3 -1
  33. data/lib/minfraud/model/warning.rb +13 -2
  34. data/lib/minfraud/report.rb +1 -1
  35. data/lib/minfraud/validates.rb +139 -2
  36. data/lib/minfraud/version.rb +1 -1
  37. metadata +20 -19
  38. data/.rspec +0 -2
  39. data/.rubocop.yml +0 -113
  40. data/CLAUDE.md +0 -443
  41. data/CODE_OF_CONDUCT.md +0 -49
  42. data/Gemfile +0 -5
  43. data/README.dev.md +0 -28
  44. data/Rakefile +0 -12
  45. data/bin/console +0 -15
  46. data/bin/setup +0 -8
  47. data/minfraud.gemspec +0 -42
data/CLAUDE.md DELETED
@@ -1,443 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Project Overview
6
-
7
- **minfraud-api-ruby** is MaxMind's official Ruby client library for the minFraud web services:
8
- - **minFraud Score, Insights, and Factors**: Fraud detection and risk scoring endpoints
9
- - **minFraud Report Transaction API**: Report fraudulent or legitimate transactions
10
-
11
- The library provides a request builder pattern through components that are assembled into assessments, then sent to the service endpoints. Responses are returned as strongly-typed model objects.
12
-
13
- **Key Technologies:**
14
- - Ruby 3.2+ (uses frozen string literals and modern Ruby features)
15
- - HTTP gem for web service client functionality and persistent connections
16
- - ConnectionPool for thread-safe connection management
17
- - RSpec for testing
18
- - RuboCop with multiple plugins for code quality
19
-
20
- ## Code Architecture
21
-
22
- ### Package Structure
23
-
24
- ```
25
- lib/minfraud/
26
- ├── components/ # Request components (Account, Device, Email, etc.)
27
- │ └── report/ # Report Transaction components
28
- ├── model/ # Response models (Score, Insights, Factors, etc.)
29
- ├── http_service/ # HTTP response wrapper
30
- ├── assessments.rb # Main request builder for Score/Insights/Factors
31
- ├── report.rb # Report Transaction API
32
- ├── resolver.rb # Maps component parameters to component objects
33
- ├── enum.rb # Enum helper for validated attributes
34
- ├── validates.rb # Input validation methods
35
- ├── errors.rb # Custom exceptions
36
- └── version.rb # Version constant
37
- ```
38
-
39
- ### Key Design Patterns
40
-
41
- #### 1. **Component-Based Request Building**
42
-
43
- Requests are built using component objects that represent different aspects of a transaction:
44
-
45
- ```ruby
46
- assessment = Minfraud::Assessments.new(
47
- device: { ip_address: '1.2.3.4' },
48
- event: { type: :purchase },
49
- billing: { country: 'US' },
50
- )
51
- ```
52
-
53
- **Component Architecture:**
54
- - All components extend `Minfraud::Components::Base`
55
- - Components use `attr_accessor` for attributes
56
- - Components implement `#to_json` to convert to API request format
57
- - The `Resolver` module maps hash parameters to component objects
58
- - Components with enum attributes include `Minfraud::Enum` module
59
-
60
- **Resolver Pattern:**
61
- The `Minfraud::Resolver` module handles converting hashes to component objects:
62
- - Maintains a `MAPPING` constant from keys to component classes
63
- - The `#assign` method creates components from hashes or uses existing component objects
64
- - Raises `RequestFormatError` for unknown keys
65
- - Used by `Minfraud::Assessments` in initialization
66
-
67
- #### 2. **Enum Attributes with Validation**
68
-
69
- Components with restricted value sets use the `Enum` module:
70
-
71
- ```ruby
72
- class Event < Base
73
- include ::Minfraud::Enum
74
-
75
- enum_accessor :type, %i[
76
- account_creation
77
- purchase
78
- payout
79
- recurring_purchase
80
- referral
81
- account_login
82
- ]
83
- end
84
- ```
85
-
86
- **Key Points:**
87
- - `enum_accessor` creates getter/setter methods with validation
88
- - Values are stored as symbols internally
89
- - Raises `NotEnumValueError` if invalid value assigned
90
- - The class gets a `{attribute}_values` method returning permitted values
91
-
92
- #### 3. **Optional Client-Side Validation**
93
-
94
- Components can optionally include `Minfraud::Validates` for input validation:
95
-
96
- ```ruby
97
- class Device < Base
98
- include ::Minfraud::Validates
99
-
100
- def ip_address=(ip_address)
101
- validate_ip('ip_address', ip_address) if Minfraud.enable_validation
102
- @ip_address = ip_address
103
- end
104
- end
105
- ```
106
-
107
- **Key Points:**
108
- - Validation is disabled by default
109
- - Enable with `Minfraud.configure { |c| c.enable_validation = true }`
110
- - Validation happens in setters before assignment
111
- - Validates types, formats, lengths, ranges, etc.
112
- - Raises `InvalidInputError` for invalid values
113
-
114
- #### 4. **Model Inheritance and Composition**
115
-
116
- Response models follow a clear hierarchy:
117
-
118
- ```
119
- Abstract → Score → Insights → Factors
120
- ```
121
-
122
- - `Abstract` provides `#get` method for safe hash access
123
- - `Score` has basic risk scoring fields
124
- - `Insights` extends `Score` with detailed fraud data (addresses, phones, device, etc.)
125
- - `Factors` extends `Insights` with subscores and risk reasons
126
-
127
- **Model Composition:**
128
- Models compose smaller model objects representing nested data:
129
-
130
- ```ruby
131
- class Insights < Score
132
- attr_reader :billing_address
133
- attr_reader :credit_card
134
- attr_reader :device
135
- attr_reader :email
136
-
137
- def initialize(record, locales)
138
- super
139
- @billing_address = Minfraud::Model::BillingAddress.new(get('billing_address'))
140
- @credit_card = Minfraud::Model::CreditCard.new(get('credit_card'))
141
- # ...
142
- end
143
- end
144
- ```
145
-
146
- #### 5. **Connection Pooling for Thread Safety**
147
-
148
- The library uses ConnectionPool for thread-safe persistent HTTP connections:
149
-
150
- ```ruby
151
- @connection_pool = ConnectionPool.new(size: 5) do
152
- HTTP.basic_auth(user: @account_id, pass: @license_key)
153
- .persistent("https://#{host}")
154
- end
155
- ```
156
-
157
- **Key Points:**
158
- - Pool is created in `Minfraud.configure`
159
- - Assessments and Reports use `Minfraud.connection_pool.with { |client| ... }`
160
- - Enables persistent connections without manual management
161
- - Safe for multi-threaded use
162
- - Must call `Minfraud.configure` before using from multiple threads
163
-
164
- ## Testing Conventions
165
-
166
- ### Running Tests
167
-
168
- ```bash
169
- # Install dependencies
170
- bundle install
171
-
172
- # Run all tests
173
- bundle exec rake spec
174
-
175
- # Run tests and RuboCop
176
- bundle exec rake # default task
177
-
178
- # Run RuboCop only
179
- bundle exec rake rubocop
180
-
181
- # Run specific test file
182
- bundle exec rspec spec/assessments_spec.rb
183
-
184
- # Run specific test
185
- bundle exec rspec spec/assessments_spec.rb:10
186
- ```
187
-
188
- ### Test Structure
189
-
190
- Tests use RSpec and are organized by functionality:
191
- - `spec/assessments_spec.rb` - Main assessment request builder tests
192
- - `spec/report_spec.rb` - Report Transaction API tests
193
- - `spec/components/*_spec.rb` - Component tests
194
- - `spec/model/*_spec.rb` - Response model tests
195
- - `spec/enum_spec.rb` - Enum validation tests
196
- - `spec/http_spec.rb` - HTTP integration tests
197
-
198
- ### Test Patterns
199
-
200
- Tests use RSpec with test data hashes:
201
-
202
- ```ruby
203
- describe Minfraud::Model::Score do
204
- let(:raw) do
205
- {
206
- 'disposition' => { 'action' => 'accept' },
207
- 'funds_remaining' => 10.01,
208
- 'id' => '27d26476-e2bc-11e4-92b8-962e705b4af5',
209
- 'risk_score' => 0.01,
210
- }
211
- end
212
-
213
- let(:model) { described_class.new(raw, ['en']) }
214
-
215
- it 'has risk_score' do
216
- expect(model.risk_score).to eq(0.01)
217
- end
218
- end
219
- ```
220
-
221
- When adding new fields:
222
- 1. Add field to test data hash
223
- 2. Add expectation to verify the field is accessible
224
- 3. Test nil handling if field is optional
225
- 4. Test validation if applicable
226
-
227
- ## Working with This Codebase
228
-
229
- ### Adding New Fields to Components
230
-
231
- For input components (request data):
232
-
233
- 1. **Add an attr_accessor**:
234
- ```ruby
235
- # A description of the field.
236
- #
237
- # @return [String, nil]
238
- attr_accessor :new_field
239
- ```
240
-
241
- 2. **Add validation if needed**:
242
- ```ruby
243
- def new_field=(value)
244
- validate_string('new_field', 255, value) if Minfraud.enable_validation
245
- @new_field = value
246
- end
247
- ```
248
-
249
- 3. **Update tests** to include the new field
250
-
251
- ### Adding New Fields to Models
252
-
253
- For output models (response data):
254
-
255
- 1. **Add an attr_reader** (models are immutable):
256
- ```ruby
257
- # A description of the field.
258
- #
259
- # @return [Type, nil]
260
- attr_reader :new_field
261
- ```
262
-
263
- 2. **Initialize in constructor**:
264
- ```ruby
265
- def initialize(record, locales)
266
- super
267
- @new_field = get('new_field')
268
- end
269
- ```
270
-
271
- 3. **For nested objects**, create a model class:
272
- ```ruby
273
- @new_field = Minfraud::Model::NewField.new(get('new_field'))
274
- ```
275
-
276
- 4. **Update tests** with test data and expectations
277
-
278
- ### Adding New Enum Values
279
-
280
- When adding new values to an enum attribute:
281
-
282
- 1. **Add to the enum_accessor array**:
283
- ```ruby
284
- enum_accessor :type, %i[
285
- existing_value
286
- new_value
287
- ]
288
- ```
289
-
290
- 2. **Update CHANGELOG.md** with the new value
291
- 3. **Add test** to verify the new value is accepted
292
-
293
- ### Adding New Components
294
-
295
- When creating a new component class:
296
-
297
- 1. **Extend Base** and include Enum/Validates if needed:
298
- ```ruby
299
- class NewComponent < Base
300
- include ::Minfraud::Enum
301
- include ::Minfraud::Validates
302
- end
303
- ```
304
-
305
- 2. **Add to Resolver::MAPPING** in lib/minfraud/resolver.rb:
306
- ```ruby
307
- MAPPING = {
308
- new_component: ::Minfraud::Components::NewComponent,
309
- # ...
310
- }.freeze
311
- ```
312
-
313
- 3. **Add attr_accessor** to Assessments or Report
314
- 4. **Require the file** in lib/minfraud.rb
315
- 5. **Add tests** for the component
316
-
317
- ### CHANGELOG.md Format
318
-
319
- Always update `CHANGELOG.md` for user-facing changes.
320
-
321
- **Important**: Do not add a date to changelog entries until release time.
322
-
323
- - If there's an existing version entry without a date (e.g., `v2.9.0`), add your changes there
324
- - If creating a new version entry, don't include a date - it will be added at release time
325
- - Use past tense for descriptions
326
- - Use bullet points starting with `*`
327
-
328
- ```markdown
329
- ## v2.10.0
330
-
331
- * Added the `new_field` attribute to `Minfraud::Components::Device`. This
332
- allows you to provide...
333
- * Added the `/output/path` to the Insights response. This is available
334
- as the `field_name` attribute on `Minfraud::Model::Insights`.
335
- ```
336
-
337
- ## Common Pitfalls and Solutions
338
-
339
- ### Problem: Components Not Registering
340
-
341
- A new component doesn't work when passed to Assessments.
342
-
343
- **Solution**: Make sure you:
344
- 1. Added the component to `Resolver::MAPPING`
345
- 2. Required the file in lib/minfraud.rb
346
- 3. Added the attr_accessor to Assessments or Report
347
-
348
- ### Problem: Thread Safety Issues
349
-
350
- Getting connection errors or state issues in multi-threaded code.
351
-
352
- **Solution**:
353
- - Always call `Minfraud.configure` before spawning threads
354
- - Never share `Minfraud::Assessments` or `Minfraud::Report` objects across threads
355
- - Create new Assessment/Report instances per thread
356
-
357
- ### Problem: Validation Not Working
358
-
359
- Validation doesn't catch invalid inputs.
360
-
361
- **Solution**:
362
- - Validation is disabled by default
363
- - Enable with `Minfraud.configure { |c| c.enable_validation = true }`
364
- - Make sure the component setter calls the appropriate `validate_*` method
365
-
366
- ### Problem: Enum Values Rejected
367
-
368
- A valid enum value raises `NotEnumValueError`.
369
-
370
- **Solution**:
371
- - Enum values must be symbols (`:value` not `"value"`)
372
- - Check the enum_accessor definition includes the value
373
- - Values are case-sensitive
374
-
375
- ## Code Style Requirements
376
-
377
- - **RuboCop enforced** with plugins: performance, rake, rspec, thread_safety
378
- - **Frozen string literals** (`# frozen_string_literal: true`) in all files
379
- - **Target Ruby 3.2+**
380
- - **Max line length: 150 characters**
381
- - **Hash alignment: table style** (aligned rockets and colons)
382
- - **Trailing commas allowed** in arrays, hashes, and arguments
383
- - **Use `if !condition`** instead of `unless condition` (NegatedIf disabled)
384
- - **Metrics cops disabled** - AbcSize, ClassLength, MethodLength, etc.
385
-
386
- Key RuboCop configurations:
387
- - Guard clauses not enforced
388
- - Conditional assignment not enforced
389
- - Format string token checks disabled
390
- - Multiple assertions allowed in RSpec tests
391
- - Extra spacing with force equal sign alignment enabled
392
-
393
- ## Development Workflow
394
-
395
- ### Setup
396
- ```bash
397
- bundle install
398
- ```
399
-
400
- ### Before Committing
401
- ```bash
402
- # Run tests and linting
403
- bundle exec rake
404
-
405
- # Or run separately
406
- bundle exec rake spec
407
- bundle exec rake rubocop
408
- ```
409
-
410
- ### Running Single Test
411
- ```bash
412
- bundle exec rspec spec/assessments_spec.rb
413
- ```
414
-
415
- ### Testing Against Sandbox
416
- ```ruby
417
- Minfraud.configure do |c|
418
- c.account_id = 12345
419
- c.license_key = 'your_license_key'
420
- c.host = 'sandbox.maxmind.com'
421
- end
422
- ```
423
-
424
- ### Version Requirements
425
- - **Ruby 3.2+** required
426
- - Target compatibility should match current supported Ruby versions
427
-
428
- ## API Endpoints
429
-
430
- The library supports three assessment endpoints:
431
- - `assessment.score` - Basic risk score (Score model)
432
- - `assessment.insights` - Score + detailed fraud data (Insights model)
433
- - `assessment.factors` - Insights + subscores + risk reasons (Factors model)
434
-
435
- Report Transaction API:
436
- - `reporter.report_transaction` - Report transaction outcomes
437
-
438
- ## Additional Resources
439
-
440
- - [API Documentation](https://www.rubydoc.info/gems/minfraud)
441
- - [minFraud Web Services Docs](https://dev.maxmind.com/minfraud)
442
- - [Report Transaction API Docs](https://dev.maxmind.com/minfraud/report-a-transaction)
443
- - GitHub Issues: https://github.com/maxmind/minfraud-api-ruby/issues
data/CODE_OF_CONDUCT.md DELETED
@@ -1,49 +0,0 @@
1
- # Contributor Code of Conduct
2
-
3
- As contributors and maintainers of this project, and in the interest of
4
- fostering an open and welcoming community, we pledge to respect all people who
5
- contribute through reporting issues, posting feature requests, updating
6
- documentation, submitting pull requests or patches, and other activities.
7
-
8
- We are committed to making participation in this project a harassment-free
9
- experience for everyone, regardless of level of experience, gender, gender
10
- identity and expression, sexual orientation, disability, personal appearance,
11
- body size, race, ethnicity, age, religion, or nationality.
12
-
13
- Examples of unacceptable behavior by participants include:
14
-
15
- * The use of sexualized language or imagery
16
- * Personal attacks
17
- * Trolling or insulting/derogatory comments
18
- * Public or private harassment
19
- * Publishing other's private information, such as physical or electronic
20
- addresses, without explicit permission
21
- * Other unethical or unprofessional conduct
22
-
23
- Project maintainers have the right and responsibility to remove, edit, or
24
- reject comments, commits, code, wiki edits, issues, and other contributions
25
- that are not aligned to this Code of Conduct, or to ban temporarily or
26
- permanently any contributor for other behaviors that they deem inappropriate,
27
- threatening, offensive, or harmful.
28
-
29
- By adopting this Code of Conduct, project maintainers commit themselves to
30
- fairly and consistently applying these principles to every aspect of managing
31
- this project. Project maintainers who do not follow or enforce the Code of
32
- Conduct may be permanently removed from the project team.
33
-
34
- This code of conduct applies both within project spaces and in public spaces
35
- when an individual is representing the project or its community.
36
-
37
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
- reported by contacting a project maintainer at support@maxmind.com. All
39
- complaints will be reviewed and investigated and will result in a response that
40
- is deemed necessary and appropriate to the circumstances. Maintainers are
41
- obligated to maintain confidentiality with regard to the reporter of an
42
- incident.
43
-
44
- This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
- version 1.3.0, available at
46
- [https://contributor-covenant.org/version/1/3/0/][version]
47
-
48
- [homepage]: https://contributor-covenant.org
49
- [version]: https://contributor-covenant.org/version/1/3/0/
data/Gemfile DELETED
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- gemspec
data/README.dev.md DELETED
@@ -1,28 +0,0 @@
1
- # Prereqs
2
-
3
- * You must have a local Ruby environment that can run the tests
4
- via `rake`.
5
- * You must have the [GitHub CLI tool (gh)](https://cli.github.com/)
6
- installed, in your path, and logged into an account that can
7
- make GitHub releases on the repo.
8
- * Your environment also must have `bash`, `git`, `perl`, and `sed`
9
- available.
10
-
11
- # How to release
12
-
13
- * Review open issues and PRs to see if anything needs to be addressed
14
- before release.
15
- * Create a branch e.g. `horgh/release` and switch to it.
16
- * `main` is protected.
17
- * Set the release version and release date in `CHANGELOG.md`. Be sure
18
- the version follows [Semantic Versioning](https://semver.org/).
19
- * Commit these changes.
20
- * Run `dev-bin/release.sh`.
21
- * Verify the release on the GitHub Releases page.
22
- * If everything goes well, the authorized releasers will receive an email
23
- to review the pending deployment. If you are an authorized releaser,
24
- you will need to approve the release deployment run. If you are not,
25
- you will have to wait for an authorized releaser to do so.
26
- * Double check it looks okay at https://rubygems.org/gems/minfraud and
27
- https://www.rubydoc.info/gems/minfraud.
28
- * Make a PR and get it merged.
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
- require 'rubocop/rake_task'
6
-
7
- RSpec::Core::RakeTask.new(:spec)
8
-
9
- RuboCop::RakeTask.new
10
-
11
- desc 'Run tests and RuboCop'
12
- task default: %i[spec rubocop]
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'minfraud'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require 'irb'
15
- IRB.start
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/minfraud.gemspec DELETED
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
-
6
- require 'minfraud/version'
7
-
8
- Gem::Specification.new do |spec|
9
- spec.name = 'minfraud'
10
- spec.version = Minfraud::VERSION
11
- spec.authors = ['kushnir.yb', 'William Storey']
12
- spec.email = ['support@maxmind.com']
13
-
14
- spec.summary = 'Ruby API for the minFraud Score, Insights, Factors, and Report Transactions services'
15
- spec.homepage = 'https://github.com/maxmind/minfraud-api-ruby'
16
- spec.license = 'MIT'
17
-
18
- spec.required_ruby_version = '>= 3.2'
19
-
20
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^.gitignore$|^(?:\.github|dev-bin|spec)/}) }
21
- spec.bindir = 'exe'
22
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
- spec.require_paths = ['lib']
24
-
25
- spec.add_dependency 'connection_pool', '~> 2.2'
26
- spec.add_dependency 'http', '>= 4.3', '< 6.0'
27
- spec.add_dependency 'maxmind-geoip2', '~> 1.4'
28
- spec.add_dependency 'simpleidn', '~> 0.1', '>= 0.1.1'
29
-
30
- spec.add_development_dependency 'bundler', '~> 2.2'
31
- spec.add_development_dependency 'rake', '~> 13.0'
32
- spec.add_development_dependency 'rspec', '~> 3.0'
33
- spec.add_development_dependency 'rubocop', '~> 1.23'
34
- spec.add_development_dependency 'rubocop-performance'
35
- spec.add_development_dependency 'rubocop-rake'
36
- spec.add_development_dependency 'rubocop-rspec'
37
- spec.add_development_dependency 'rubocop-thread_safety'
38
- spec.add_development_dependency 'webmock', '~> 3.14'
39
- spec.metadata = {
40
- 'rubygems_mfa_required' => 'true'
41
- }
42
- end