usps-imis-api 0.9.8 → 0.9.10.pre.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: 80cb6198e125d3dc201641a6a70c639c8890094582fe6c293c807fb212a59738
4
- data.tar.gz: e5382f2b08c9bffd26204cd06c3d55bc8604e7fa62ebc612a5b88a42a4029ab1
3
+ metadata.gz: b481e216a4b7c245599a7086834c243178c30aeb2d1891370150e466b5f8ba25
4
+ data.tar.gz: cb1b01a149cbe94dac703a2be902a52a1810da7326d06410b159195f5f0bb69b
5
5
  SHA512:
6
- metadata.gz: ba2f8613ca13a61eda5d8a0c5a229e09d8226e74ca8611f83e80d6d3259e2f120f6fddab9ee2eb7740c0de150de8462537219147fe7d3c2aceafc5e0c8652dfc
7
- data.tar.gz: 472196fce4e1a5e05287889b6d70e56bc72f5326944dafd114527865688d82f3208d7e4ae38b4161a176ea43663a7981eee196e145bbeac3d62f9c2f9b941713
6
+ metadata.gz: 51d505d17d527c20112d7a5b81932bf8e28949a5515ab6ae1c6660687dd2d56227aa4b8d4883111b5724bf24e2f49efe6b37322dd0c627f66587a935b8b6f13b
7
+ data.tar.gz: 9878ebd3710155ee8ea752337155d8f88eec75a07d228fc2d2462da8bbd57e2e8e44a80f6fc94965c1cc33435bd0efc585d79db5ee96c26c04d250a236bad83c
data/Readme.md CHANGED
@@ -377,6 +377,8 @@ Testing is available by running:
377
377
  bundle exec rspec
378
378
  ```
379
379
 
380
+ API web requests are sanitized and recorded using VCR.
381
+
380
382
  Linting is available by running:
381
383
 
382
384
  ```ruby
data/lib/usps/imis/api.rb CHANGED
@@ -36,11 +36,10 @@ module Usps
36
36
 
37
37
  # A new instance of +Api+
38
38
  #
39
- # @param skip_authentication [bool] Skip authentication on initialization (used for tests)
40
39
  # @param imis_id [Integer, String] iMIS ID to select immediately on initialization
41
40
  #
42
- def initialize(skip_authentication: false, imis_id: nil)
43
- authenticate unless skip_authentication
41
+ def initialize(imis_id: nil)
42
+ authenticate
44
43
  self.imis_id = imis_id if imis_id
45
44
  end
46
45
 
@@ -104,9 +103,7 @@ module Usps
104
103
  #
105
104
  # @return [Hash] Response data from the API
106
105
  #
107
- def query(query_name, query_params = {})
108
- Query.new(self, query_name, query_params)
109
- end
106
+ def query(query_name, query_params = {}) = Query.new(self, query_name, **query_params)
110
107
 
111
108
  # Run requests as DSL, with specific +BusinessObject+ only maintained for this scope
112
109
  #
@@ -162,20 +159,22 @@ module Usps
162
159
  def authenticate
163
160
  logger.debug 'Authenticating with iMIS'
164
161
 
165
- uri = URI(File.join(Imis.configuration.hostname, AUTHENTICATION_PATH))
166
- req = Net::HTTP::Post.new(uri)
167
- authentication_data = {
162
+ request = http_post
163
+ request.body = URI.encode_www_form(
168
164
  grant_type: 'password',
169
165
  username: Imis.configuration.username,
170
166
  password: Imis.configuration.password
171
- }
172
- req.body = URI.encode_www_form(authentication_data)
173
- result = submit(uri, req)
167
+ )
168
+ result = submit(uri, request)
174
169
  json = JSON.parse(result.body)
175
170
 
176
171
  @token = json['access_token']
177
172
  @token_expiration = Time.parse(json['.expires'])
178
173
  end
174
+
175
+ # URI for the authentication endpoint
176
+ #
177
+ def uri(...) = URI(File.join(Imis.configuration.hostname, AUTHENTICATION_PATH))
179
178
  end
180
179
  end
181
180
  end
@@ -93,7 +93,7 @@ module Usps
93
93
  #
94
94
  # @return [Usps::Imis::Data] Response data from the API
95
95
  #
96
- def put(body) = put_object(Net::HTTP::Put.new(uri), body)
96
+ def put(body) = put_object(http_put, body)
97
97
  alias update put
98
98
 
99
99
  # Create a business object for the current member
@@ -102,14 +102,14 @@ module Usps
102
102
  #
103
103
  # @return [Usps::Imis::Data] Response data from the API
104
104
  #
105
- def post(body) = put_object(Net::HTTP::Post.new(uri(id: '')), body)
105
+ def post(body) = put_object(http_post, body)
106
106
  alias create post
107
107
 
108
108
  # Remove a business object for the current member
109
109
  #
110
110
  # @return [true] Only on success response (i.e. blank string from the API)
111
111
  #
112
- def delete = submit(uri, authorize(Net::HTTP::Delete.new(uri))).body == '' # rubocop:disable Naming/PredicateMethod
112
+ def delete = submit(uri, authorize(http_delete)).body == '' # rubocop:disable Naming/PredicateMethod
113
113
  alias destroy delete
114
114
 
115
115
  # Ruby 3.5 instance variable filter
@@ -118,9 +118,6 @@ module Usps
118
118
 
119
119
  private
120
120
 
121
- def token = api.token
122
- def token_expiration = api.token_expiration
123
-
124
121
  def logger = Imis.logger('BusinessObject')
125
122
 
126
123
  # Construct a business object API endpoint address
@@ -173,9 +170,8 @@ module Usps
173
170
  # Useful for stubbing data in tests
174
171
  #
175
172
  def raw_object
176
- request = Net::HTTP::Get.new(uri)
177
- result = submit(uri, authorize(request))
178
- result = Data.from_json(result.body)
173
+ response = submit(uri, authorize(http_get))
174
+ result = Data.from_json(response.body)
179
175
  JSON.pretty_generate(result).split("\n").each { logger.debug " -> #{it}" }
180
176
  result
181
177
  end
@@ -184,8 +180,8 @@ module Usps
184
180
  #
185
181
  def put_object(request, body)
186
182
  request.body = JSON.dump(body)
187
- result = submit(uri, authorize(request))
188
- result = Data.from_json(result.body)
183
+ response = submit(uri, authorize(request))
184
+ result = Data.from_json(response.body)
189
185
  JSON.pretty_generate(result).split("\n").each { logger.debug " -> #{it}" }
190
186
  result
191
187
  end
@@ -44,7 +44,7 @@ module Usps
44
44
 
45
45
  # Ruby 3.5 instance variable filter
46
46
  #
47
- def instance_variables_to_inspect = %i[@environment @imis_id_query_name @username @logger_level]
47
+ def instance_variables_to_inspect = instance_variables - %i[@password @logger]
48
48
 
49
49
  # Parameters to filter out of logging
50
50
  #
@@ -50,6 +50,10 @@ module Usps
50
50
  .index_with { self[it] }
51
51
  end
52
52
 
53
+ def []=(...)
54
+ raise Errors::ApiError, '`Data` does not support setting values. If you need to modify it, call `.raw` on it.'
55
+ end
56
+
53
57
  def inspect
54
58
  stringio = StringIO.new
55
59
  PP.pp(self, stringio)
@@ -18,7 +18,7 @@ module Usps
18
18
  super(message)
19
19
  @metadata = metadata
20
20
 
21
- Imis.logger.debug self
21
+ Imis.logger(self.class.name).error self
22
22
  end
23
23
 
24
24
  # Additional metadata to include in Bugsnag reports
@@ -24,20 +24,32 @@ module Usps
24
24
  #
25
25
  attr_reader :query_params
26
26
 
27
+ # Current page size for paging through the Query
28
+ #
29
+ attr_accessor :page_size
30
+
27
31
  # Current offset for paging through the Query
28
32
  #
29
- attr_reader :offset
33
+ attr_accessor :offset
34
+
35
+ # Count of records processed
36
+ #
37
+ attr_reader :count
30
38
 
31
39
  # A new instance of +Query+
32
40
  #
33
41
  # @param api [Api] Parent to use for making requests
34
42
  # @param query_name [String] Full path of the query in IQA, e.g. +$/_ABC/Fiander/iMIS_ID+
35
- # @query_params [Hash] Conforms to pattern +{ param_name => param_value }+
43
+ # @param page_size [Integer] Number of records to return on each request page
44
+ # @param offset [Integer] Offset index of records to return on next request page
45
+ # @param query_params [Hash] Conforms to pattern +{ param_name => param_value }+
36
46
  #
37
- def initialize(api, query_name, query_params)
47
+ def initialize(api, query_name, page_size: 100, offset: nil, **query_params)
38
48
  @api = api
39
49
  @query_name = query_name
40
50
  @query_params = query_params
51
+ @page_size = page_size
52
+ @offset = offset
41
53
  end
42
54
 
43
55
  # Iterate through all results from the query
@@ -53,26 +65,37 @@ module Usps
53
65
  # Iterate through all results from the query, fetching one page at a time
54
66
  #
55
67
  def find_each(&)
56
- result = { 'HasNext' => true }
57
- count = 0
68
+ result = reset!
58
69
 
59
70
  while result['HasNext']
60
- logger.info 'Fetching IQA Query page'
71
+ result = fetch_next.tap do |result_page|
72
+ result_page['Items']['$values'].map { it.except('$type') }.each(&)
73
+ end
74
+ end
75
+
76
+ nil
77
+ end
78
+
79
+ # Fetch a raw query page
80
+ #
81
+ def fetch = JSON.parse(submit(uri, authorize(http_get)).body)
61
82
 
62
- result = fetch
83
+ # Fetch the next raw query page, and update the current offset
84
+ #
85
+ def fetch_next
86
+ logger.info 'Fetching IQA Query page'
63
87
 
64
- count += result['Count'] || 0
65
- logger.info " -> #{count} / #{result['TotalCount']} #{'item'.pluralize(count)}"
66
- logger.debug ' -> Query page data:'
67
- JSON.pretty_generate(result).split("\n").each { logger.debug " -> #{it}" }
88
+ result = fetch
68
89
 
69
- items = result['Items']['$values'].map { it.except('$type') }
70
- @offset = result['NextOffset']
90
+ @count += result['Count'] || 0
91
+ total = result['TotalCount']
92
+ logger.info " -> #{@count} / #{total} #{'item'.pluralize(total)}"
93
+ logger.debug ' -> Query page data:'
94
+ JSON.pretty_generate(result).split("\n").each { logger.debug " -> #{it}" }
71
95
 
72
- items.each(&)
73
- end
96
+ @offset = result['NextOffset']
74
97
 
75
- nil
98
+ result
76
99
  end
77
100
 
78
101
  # Ruby 3.5 instance variable filter
@@ -81,14 +104,20 @@ module Usps
81
104
 
82
105
  private
83
106
 
84
- def token = api.token
85
- def token_expiration = api.token_expiration
86
-
87
- def path = "#{QUERY_PATH}?#{query_params.merge(QueryName: query_name, Offset: offset).to_query}"
107
+ def path_params = query_params.merge(QueryName: query_name).merge({ Offset: offset, Limit: page_size }.compact)
108
+ def path = "#{QUERY_PATH}?#{path_params.to_query}"
88
109
  def uri = URI(File.join(Imis.configuration.hostname, path))
89
- def fetch = JSON.parse(submit(uri, authorize(Net::HTTP::Get.new(uri))).body)
90
110
 
91
111
  def logger = Imis.logger('Query')
112
+
113
+ def reset!
114
+ logger.debug 'Resetting Query progress'
115
+
116
+ @count = 0
117
+ @offset = 0
118
+
119
+ { 'HasNext' => true }
120
+ end
92
121
  end
93
122
  end
94
123
  end
@@ -9,6 +9,15 @@ module Usps
9
9
 
10
10
  def logger = Imis.logger
11
11
 
12
+ def token = api.token
13
+ def token_expiration = api.token_expiration
14
+ def authenticate = api.send(:authenticate)
15
+
16
+ def http_get = Net::HTTP::Get.new(uri)
17
+ def http_put = Net::HTTP::Put.new(uri)
18
+ def http_post = Net::HTTP::Post.new(uri(id: ''))
19
+ def http_delete = Net::HTTP::Delete.new(uri)
20
+
12
21
  def client(uri)
13
22
  Net::HTTP.new(uri.host, uri.port).tap do |http|
14
23
  http.use_ssl = true
@@ -45,9 +54,8 @@ module Usps
45
54
  body = request.body.dup
46
55
 
47
56
  Imis.config.filtered_parameters.each do |parameter|
48
- body =
49
- body.gsub(Imis.config.public_send(parameter), '[FILTERED]')
50
- .gsub(CGI.escape(Imis.config.public_send(parameter)), '[FILTERED]')
57
+ body.gsub!(Imis.config.public_send(parameter), '[FILTERED]')
58
+ body.gsub!(CGI.escape(Imis.config.public_send(parameter)), '[FILTERED]')
51
59
  end
52
60
 
53
61
  body
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Usps
4
4
  module Imis
5
- VERSION = '0.9.8'
5
+ VERSION = '0.9.10-1'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: usps-imis-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.8
4
+ version: 0.9.10.pre.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julian Fiander
@@ -29,18 +29,7 @@ executables: []
29
29
  extensions: []
30
30
  extra_rdoc_files: []
31
31
  files:
32
- - ".github/workflows/main.yml"
33
- - ".gitignore"
34
- - ".rspec"
35
- - ".rubocop.yml"
36
- - ".ruby-version"
37
- - ".simplecov"
38
- - Gemfile
39
- - Gemfile.lock
40
- - Rakefile
41
32
  - Readme.md
42
- - bin/console
43
- - bin/setup
44
33
  - lib/usps/imis.rb
45
34
  - lib/usps/imis/api.rb
46
35
  - lib/usps/imis/business_object.rb
@@ -66,21 +55,6 @@ files:
66
55
  - lib/usps/imis/query.rb
67
56
  - lib/usps/imis/requests.rb
68
57
  - lib/usps/imis/version.rb
69
- - spec/lib/usps/imis/api_spec.rb
70
- - spec/lib/usps/imis/business_object_spec.rb
71
- - spec/lib/usps/imis/config_spec.rb
72
- - spec/lib/usps/imis/data_spec.rb
73
- - spec/lib/usps/imis/error_spec.rb
74
- - spec/lib/usps/imis/errors/response_error_spec.rb
75
- - spec/lib/usps/imis/mapper_spec.rb
76
- - spec/lib/usps/imis/mocks/business_object_spec.rb
77
- - spec/lib/usps/imis/panels/base_panel_spec.rb
78
- - spec/lib/usps/imis/panels/education_spec.rb
79
- - spec/lib/usps/imis/panels/vsc_spec.rb
80
- - spec/lib/usps/imis/properties_spec.rb
81
- - spec/lib/usps/imis_spec.rb
82
- - spec/spec_helper.rb
83
- - usps-imis-api.gemspec
84
58
  homepage: https://github.com/unitedstatespowersquadrons/imis-api-ruby
85
59
  licenses: []
86
60
  metadata:
@@ -1,57 +0,0 @@
1
- name: 'USPS iMIS API - Ruby'
2
- on:
3
- push:
4
- paths:
5
- - '**.rb'
6
- - '**.yml'
7
- - '**.json'
8
- - 'Gemfile'
9
- - '.simplecov'
10
- branches:
11
- - 'main'
12
- - '**'
13
- workflow_dispatch:
14
-
15
- jobs:
16
- rubocop:
17
- runs-on: ubuntu-24.04
18
- environment: "USPS iMIS API - Ruby"
19
- strategy:
20
- matrix:
21
- ruby: ['3.4', head]
22
- steps:
23
- - name: Checkout Code
24
- uses: actions/checkout@v4
25
- - name: Setup Ruby
26
- uses: ruby/setup-ruby@v1
27
- with:
28
- ruby-version: ${{ matrix.ruby }}
29
- bundler-cache: true
30
- - name: Run Rubocop
31
- run: bundle exec rubocop
32
- rspec:
33
- runs-on: ubuntu-24.04
34
- environment: "USPS iMIS API - Ruby"
35
- strategy:
36
- matrix:
37
- ruby: ['3.4', head]
38
- env:
39
- IMIS_USERNAME: ${{ secrets.IMIS_USERNAME }}
40
- IMIS_PASSWORD: ${{ secrets.IMIS_PASSWORD }}
41
- IMIS_ID_QUERY_NAME: ${{ secrets.IMIS_ID_QUERY_NAME }}
42
- steps:
43
- - name: Checkout Code
44
- uses: actions/checkout@v4
45
- - name: Setup Ruby
46
- uses: ruby/setup-ruby@v1
47
- with:
48
- ruby-version: ${{ matrix.ruby }}
49
- bundler-cache: true
50
- - name: Run Rspec
51
- run: bundle exec rspec --format documentation --order rand --color --tty
52
- - name: Store Coverage
53
- uses: actions/upload-artifact@v4
54
- with:
55
- name: coverage-rspec-${{ matrix.ruby }}
56
- include-hidden-files: true
57
- path: coverage/
data/.gitignore DELETED
@@ -1,5 +0,0 @@
1
- coverage/
2
- tmp/
3
- .env
4
- *.gem
5
- .yardoc
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --require spec_helper
2
- --order rand
data/.rubocop.yml DELETED
@@ -1,89 +0,0 @@
1
- plugins:
2
- - rubocop-rspec
3
-
4
- AllCops:
5
- TargetRubyVersion: 3.4
6
- Exclude:
7
- - bin/**/*
8
- - config/**/*
9
- - db/**/*
10
- - vendor/**/*
11
- - tmp/**/*
12
- NewCops: enable
13
- SuggestExtensions: false
14
-
15
- Layout/FirstHashElementIndentation:
16
- EnforcedStyle: consistent
17
- Layout/AccessModifierIndentation:
18
- EnforcedStyle: outdent
19
- Layout/EmptyLinesAroundAccessModifier:
20
- Enabled: true
21
- Layout/ArrayAlignment:
22
- Enabled: true
23
- Layout/HashAlignment:
24
- Enabled: true
25
- Layout/EmptyLineAfterGuardClause:
26
- Enabled: true
27
- Layout/SpaceInsideBlockBraces:
28
- EnforcedStyle: space
29
- EnforcedStyleForEmptyBraces: no_space
30
- Layout/SpaceInsideHashLiteralBraces:
31
- EnforcedStyle: space
32
- EnforcedStyleForEmptyBraces: no_space
33
- Layout/SpaceInsideArrayLiteralBrackets:
34
- EnforcedStyle: no_space
35
-
36
- Lint/UnusedMethodArgument:
37
- Enabled: true
38
- Lint/UselessAssignment:
39
- Enabled: true
40
-
41
- Metrics/MethodLength:
42
- Enabled: true
43
- Max: 15
44
- Metrics/ClassLength:
45
- Enabled: true
46
- Max: 150
47
- Metrics/ModuleLength:
48
- Max: 150
49
- Metrics/ParameterLists:
50
- Enabled: true
51
- Metrics/CyclomaticComplexity:
52
- Enabled: true
53
- Metrics/AbcSize:
54
- Enabled: true
55
- Max: 30
56
-
57
- Naming/MemoizedInstanceVariableName:
58
- Enabled: false
59
- Naming/MethodParameterName:
60
- Enabled: false
61
- Naming/FileName:
62
- Enabled: false
63
-
64
- Style/Documentation:
65
- Enabled: false
66
- Style/FrozenStringLiteralComment:
67
- Enabled: true
68
- Style/NumericLiterals:
69
- Enabled: false
70
- Style/StringLiterals:
71
- EnforcedStyle: single_quotes
72
- Style/AndOr:
73
- Enabled: true
74
- Style/ClassCheck:
75
- Enabled: true
76
- Style/GuardClause:
77
- Enabled: true
78
- Style/OptionalBooleanParameter:
79
- Enabled: false
80
-
81
- Security/Eval:
82
- Enabled: true
83
- Security/JSONLoad:
84
- Enabled: true
85
- Security/YAMLLoad:
86
- Enabled: true
87
-
88
- RSpec/NestedGroups:
89
- Max: 4
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 3.5-dev
data/.simplecov DELETED
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- SimpleCov.start do
4
- enable_coverage :branch
5
- primary_coverage :branch
6
-
7
- add_filter '/spec'
8
- end
data/Gemfile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
- gemspec
5
-
6
- gem 'dotenv', '>= 3.1.4'
7
- gem 'irb', '>= 1.15.2'
8
- gem 'rake', '>= 13.2.1'
9
- gem 'rspec', '>= 3.13.0'
10
- gem 'rubocop', '>= 1.66.1'
11
- gem 'rubocop-rspec', '>= 3.1.0'
12
- gem 'simplecov', '>= 0.22.0'
data/Gemfile.lock DELETED
@@ -1,129 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- usps-imis-api (0.9.8)
5
- activesupport (~> 8.0)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- activesupport (8.1.0)
11
- base64
12
- bigdecimal
13
- concurrent-ruby (~> 1.0, >= 1.3.1)
14
- connection_pool (>= 2.2.5)
15
- drb
16
- i18n (>= 1.6, < 2)
17
- json
18
- logger (>= 1.4.2)
19
- minitest (>= 5.1)
20
- securerandom (>= 0.3)
21
- tzinfo (~> 2.0, >= 2.0.5)
22
- uri (>= 0.13.1)
23
- ast (2.4.3)
24
- base64 (0.3.0)
25
- bigdecimal (3.3.1)
26
- concurrent-ruby (1.3.5)
27
- connection_pool (2.5.4)
28
- date (3.4.1)
29
- diff-lcs (1.6.2)
30
- docile (1.4.1)
31
- dotenv (3.1.8)
32
- drb (2.2.3)
33
- erb (5.1.1)
34
- i18n (1.14.7)
35
- concurrent-ruby (~> 1.0)
36
- io-console (0.8.1)
37
- irb (1.15.2)
38
- pp (>= 0.6.0)
39
- rdoc (>= 4.0.0)
40
- reline (>= 0.4.2)
41
- json (2.15.1)
42
- language_server-protocol (3.17.0.5)
43
- lint_roller (1.1.0)
44
- logger (1.7.0)
45
- minitest (5.26.0)
46
- parallel (1.27.0)
47
- parser (3.3.9.0)
48
- ast (~> 2.4.1)
49
- racc
50
- pp (0.6.3)
51
- prettyprint
52
- prettyprint (0.2.0)
53
- prism (1.6.0)
54
- psych (5.2.6)
55
- date
56
- stringio
57
- racc (1.8.1)
58
- rainbow (3.1.1)
59
- rake (13.3.0)
60
- rdoc (6.15.0)
61
- erb
62
- psych (>= 4.0.0)
63
- tsort
64
- regexp_parser (2.11.3)
65
- reline (0.6.2)
66
- io-console (~> 0.5)
67
- rspec (3.13.2)
68
- rspec-core (~> 3.13.0)
69
- rspec-expectations (~> 3.13.0)
70
- rspec-mocks (~> 3.13.0)
71
- rspec-core (3.13.6)
72
- rspec-support (~> 3.13.0)
73
- rspec-expectations (3.13.5)
74
- diff-lcs (>= 1.2.0, < 2.0)
75
- rspec-support (~> 3.13.0)
76
- rspec-mocks (3.13.6)
77
- diff-lcs (>= 1.2.0, < 2.0)
78
- rspec-support (~> 3.13.0)
79
- rspec-support (3.13.6)
80
- rubocop (1.81.6)
81
- json (~> 2.3)
82
- language_server-protocol (~> 3.17.0.2)
83
- lint_roller (~> 1.1.0)
84
- parallel (~> 1.10)
85
- parser (>= 3.3.0.2)
86
- rainbow (>= 2.2.2, < 4.0)
87
- regexp_parser (>= 2.9.3, < 3.0)
88
- rubocop-ast (>= 1.47.1, < 2.0)
89
- ruby-progressbar (~> 1.7)
90
- unicode-display_width (>= 2.4.0, < 4.0)
91
- rubocop-ast (1.47.1)
92
- parser (>= 3.3.7.2)
93
- prism (~> 1.4)
94
- rubocop-rspec (3.7.0)
95
- lint_roller (~> 1.1)
96
- rubocop (~> 1.72, >= 1.72.1)
97
- ruby-progressbar (1.13.0)
98
- securerandom (0.4.1)
99
- simplecov (0.22.0)
100
- docile (~> 1.1)
101
- simplecov-html (~> 0.11)
102
- simplecov_json_formatter (~> 0.1)
103
- simplecov-html (0.13.2)
104
- simplecov_json_formatter (0.1.4)
105
- stringio (3.1.7)
106
- tsort (0.2.0)
107
- tzinfo (2.0.6)
108
- concurrent-ruby (~> 1.0)
109
- unicode-display_width (3.2.0)
110
- unicode-emoji (~> 4.1)
111
- unicode-emoji (4.1.0)
112
- uri (1.0.4)
113
-
114
- PLATFORMS
115
- arm64-darwin-23
116
- ruby
117
-
118
- DEPENDENCIES
119
- dotenv (>= 3.1.4)
120
- irb (>= 1.15.2)
121
- rake (>= 13.2.1)
122
- rspec (>= 3.13.0)
123
- rubocop (>= 1.66.1)
124
- rubocop-rspec (>= 3.1.0)
125
- simplecov (>= 0.22.0)
126
- usps-imis-api!
127
-
128
- BUNDLED WITH
129
- 2.5.6