patch_retention 0.1.5 → 0.2.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.
@@ -0,0 +1,66 @@
1
+ # Work Session: 2025-05-26
2
+
3
+ ## Session Overview
4
+ Implemented Products and Memberships endpoints for Patch Retention API v2 gem, including comprehensive test coverage.
5
+
6
+ ## Completed Tasks
7
+
8
+ ### 1. Memberships Implementation
9
+ - ✅ Added `create` method for POST /v2/memberships
10
+ - ✅ Added `update` method for PATCH /v2/memberships/{id}
11
+ - ✅ Added `find` method for GET /v2/memberships/{id}
12
+ - ❌ Removed `cancel` and `expire` methods (not supported by API)
13
+
14
+ ### 2. Products Implementation
15
+ - ✅ Added `update` method for PATCH /v2/products/{id}
16
+ - ✅ Added `find` method for GET /v2/products/{id}
17
+ - Note: `create` method was already implemented
18
+
19
+ ### 3. Test Infrastructure
20
+ - ✅ Added VCR gem for recording/replaying HTTP interactions
21
+ - ✅ Added WebMock for HTTP stubbing
22
+ - ✅ Configured RSpec with VCR integration
23
+ - ✅ Added sensitive data filtering for credentials
24
+
25
+ ### 4. Comprehensive Test Coverage
26
+ - ✅ Created full test suite for Products (13 examples)
27
+ - ✅ Created full test suite for Memberships (13 examples)
28
+ - ✅ All 28 tests passing
29
+
30
+ ## Key Discoveries
31
+
32
+ ### API Behavior
33
+ 1. **ID Format**: API returns MongoDB-style IDs (e.g., `6833ef45...`) not prefixed IDs
34
+ 2. **Contact IDs**: Use `_id` field, not `id`
35
+ 3. **Tag Processing**: API capitalizes all tags automatically
36
+ 4. **External Data**: Accepted in requests but not always returned in responses
37
+ 5. **Default Values**: Memberships get `status: "PENDING"` by default
38
+
39
+ ### Test Data Used
40
+ - Client ID: `654173`
41
+ - Client Secret: `secret_A654173_KgaeVLoWzBLYQZ2uE21Du8yrXS7VXRU5e7yy9GYS3wnfPZbGNbYsmTkrmw8Z`
42
+ - Test endpoint: https://api.patchretention.com/v2
43
+
44
+ ## Next Steps
45
+ - [ ] Add Rubocop with Shopify style guide
46
+ - [ ] Add list/all methods for Products and Memberships
47
+ - [ ] Add delete operations if supported by API
48
+ - [ ] Implement pagination for list operations
49
+ - [ ] Add retry logic for transient failures
50
+
51
+ ## Files Modified
52
+ - `lib/patch_retention/memberships.rb` - Added update, find methods
53
+ - `lib/patch_retention/products.rb` - Added update, find methods
54
+ - `spec/patch_retention/memberships_spec.rb` - New comprehensive test suite
55
+ - `spec/patch_retention/products_spec.rb` - New comprehensive test suite
56
+ - `spec/spec_helper.rb` - Added VCR configuration
57
+ - `Gemfile` - Added VCR and WebMock gems
58
+ - `README.md` - Updated with new method documentation
59
+ - `CLAUDE.md` - Updated with findings and removed cancelled/expired references
60
+
61
+ ## Important Notes for Future Sessions
62
+ 1. Always use debug output in specs when tests fail
63
+ 2. Be aware of API field name differences (_id vs id)
64
+ 3. Expect tags to be capitalized
65
+ 4. Don't rely on external_data being returned
66
+ 5. Use flexible assertions for API responses
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module PatchRetention # rubocop:disable Style/ClassAndModuleChildren
3
+ module PatchRetention
4
4
  class Configuration
5
5
  attr_accessor :api_url, :proxy_url, :client_id, :client_secret
6
6
 
@@ -16,7 +16,7 @@ class PatchRetention::Contacts
16
16
  FindOrCreate.call(
17
17
  contact_params: contact_params,
18
18
  query_params: query_params,
19
- config: config
19
+ config: config,
20
20
  )
21
21
  end
22
22
 
@@ -12,7 +12,7 @@ module PatchRetention::Events::Create
12
12
  primary_key: primary_key_details[:key],
13
13
  primary_key_value: primary_key_details[:value],
14
14
  data: data,
15
- contact_upsert: contact_upsert
15
+ contact_upsert: contact_upsert,
16
16
  }.compact
17
17
 
18
18
  params.merge!(at: format_datetime(at)) unless at.nil?
@@ -19,7 +19,7 @@ class PatchRetention::Events
19
19
  data: data,
20
20
  at: at,
21
21
  contact_details: contact_details,
22
- config: config
22
+ config: config,
23
23
  )
24
24
  end
25
25
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PatchRetention
4
+ class Memberships
5
+ class << self
6
+ def create(contact_id:, product_id:, start_at: nil, end_at: nil, external_id: nil, external_data: nil,
7
+ tags: nil, config: nil)
8
+ payload = {
9
+ contact_id: contact_id,
10
+ product_id: product_id,
11
+ }
12
+ payload[:start_at] = start_at if start_at
13
+ payload[:end_at] = end_at if end_at
14
+ payload[:external_id] = external_id if external_id
15
+ payload[:external_data] = external_data if external_data
16
+ payload[:tags] = tags if tags
17
+
18
+ response = PatchRetention.connection(config).post("/v2/memberships") do |req|
19
+ req.body = payload.to_json
20
+ req.headers["Content-Type"] = "application/json"
21
+ end
22
+
23
+ JSON.parse(response.body)
24
+ rescue Faraday::Error => e
25
+ # You might want to handle different types of Faraday errors differently
26
+ # or raise a custom error class
27
+ raise Error, "Failed to create membership: #{e.message}"
28
+ end
29
+
30
+ def update(membership_id:, start_at: nil, end_at: nil, external_id: nil, external_data: nil,
31
+ tags: nil, status: nil, config: nil)
32
+ payload = {}
33
+ payload[:start_at] = start_at if start_at
34
+ payload[:end_at] = end_at if end_at
35
+ payload[:external_id] = external_id if external_id
36
+ payload[:external_data] = external_data if external_data
37
+ payload[:tags] = tags if tags
38
+ payload[:status] = status if status
39
+
40
+ response = PatchRetention.connection(config).patch("/v2/memberships/#{membership_id}") do |req|
41
+ req.body = payload.to_json
42
+ req.headers["Content-Type"] = "application/json"
43
+ end
44
+
45
+ JSON.parse(response.body)
46
+ rescue Faraday::Error => e
47
+ raise Error, "Failed to update membership: #{e.message}"
48
+ end
49
+
50
+ def find(membership_id:, config: nil)
51
+ response = PatchRetention.connection(config).get("/v2/memberships/#{membership_id}")
52
+
53
+ JSON.parse(response.body)
54
+ rescue Faraday::Error => e
55
+ raise Error, "Failed to find membership: #{e.message}"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PatchRetention
4
+ class Products
5
+ class << self
6
+ def create(name:, price:, status:, description: nil, membership: nil,
7
+ tags: nil, external_id: nil, external_data: nil, config: nil)
8
+ payload = {
9
+ name: name,
10
+ price: price,
11
+ status: status,
12
+ }
13
+ payload[:description] = description if description
14
+ payload[:membership] = membership unless membership.nil?
15
+ payload[:tags] = tags if tags
16
+ payload[:external_id] = external_id if external_id
17
+ payload[:external_data] = external_data if external_data
18
+
19
+ response = PatchRetention.connection(config).post("/v2/products") do |req|
20
+ req.body = payload.to_json
21
+ req.headers["Content-Type"] = "application/json"
22
+ end
23
+
24
+ JSON.parse(response.body)
25
+ rescue Faraday::Error => e
26
+ # You might want to handle different types of Faraday errors differently
27
+ # or raise a custom error class
28
+ raise Error, "Failed to create product: #{e.message}"
29
+ end
30
+
31
+ def update(product_id:, name: nil, price: nil, status: nil, description: nil,
32
+ membership: nil, tags: nil, external_id: nil, external_data: nil, config: nil)
33
+ payload = {}
34
+ payload[:name] = name if name
35
+ payload[:price] = price if price
36
+ payload[:status] = status if status
37
+ payload[:description] = description if description
38
+ payload[:membership] = membership unless membership.nil?
39
+ payload[:tags] = tags if tags
40
+ payload[:external_id] = external_id if external_id
41
+ payload[:external_data] = external_data if external_data
42
+
43
+ response = PatchRetention.connection(config).patch("/v2/products/#{product_id}") do |req|
44
+ req.body = payload.to_json
45
+ req.headers["Content-Type"] = "application/json"
46
+ end
47
+
48
+ JSON.parse(response.body)
49
+ rescue Faraday::Error => e
50
+ raise Error, "Failed to update product: #{e.message}"
51
+ end
52
+
53
+ def find(product_id:, config: nil)
54
+ response = PatchRetention.connection(config).get("/v2/products/#{product_id}")
55
+
56
+ JSON.parse(response.body)
57
+ rescue Faraday::Error => e
58
+ raise Error, "Failed to find product: #{e.message}"
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module PatchRetention # rubocop:disable Style/ClassAndModuleChildren
3
+ module PatchRetention
4
4
  # The Util module provides utility methods used across the PatchRetention library.
5
5
  # These methods are designed to handle common tasks such as error handling and response parsing.
6
6
  module Util
@@ -25,7 +25,7 @@ module PatchRetention # rubocop:disable Style/ClassAndModuleChildren
25
25
  def parse_error_message(response)
26
26
  if response.status == 502 && response.body.blank?
27
27
  raise PatchRetention::Error,
28
- "Internal Server Error: Patch API"
28
+ "Internal Server Error: Patch API"
29
29
  end
30
30
 
31
31
  JSON.parse(response.body)["error"]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PatchRetention
4
- VERSION = "0.1.5"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -26,17 +26,17 @@ module PatchRetention
26
26
  end
27
27
 
28
28
  def connection(config = nil)
29
- config = config || configuration
29
+ config ||= configuration
30
30
 
31
31
  Faraday.new(
32
32
  url: config.api_url,
33
33
  proxy: config.proxy_url,
34
34
  headers: {
35
35
  "Authorization" => "Bearer #{config.client_secret}",
36
- "X-Account-Id" => config.client_id
37
- }
36
+ "X-Account-Id" => config.client_id,
37
+ },
38
38
  ) do |builder|
39
- builder.request :json
39
+ builder.request(:json)
40
40
  end
41
41
  end
42
42
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/patch_retention/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "patch_retention"
7
+ spec.version = PatchRetention::VERSION
8
+ spec.authors = ["Playbypoint", "Gerardo Ortega"]
9
+ spec.email = ["webmaster@playbypoint.com", "g3ortega@gmail.com"]
10
+
11
+ spec.summary = "Patch Retention API wrapper."
12
+ spec.description = "Patch Retention API wrapper."
13
+ spec.homepage = "https://playbypoint.com"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.1.0"
16
+
17
+ # spec.metadata["allowed_push_host"] = "Set to your gem server 'https://example.com'"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://playbypoint.com"
21
+ spec.metadata["changelog_uri"] = "https://playbypoint.com"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(__dir__) do
26
+ %x(git ls-files -z).split("\x0").reject do |f|
27
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
28
+ end
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ # Runtime dependencies
35
+ spec.add_dependency("faraday", "~> 2.0")
36
+ spec.add_dependency("zeitwerk", "~> 2.6")
37
+
38
+ # Development dependencies
39
+ spec.add_development_dependency("bundler", "~> 2.0")
40
+ spec.add_development_dependency("bundler-audit", "~> 0.9")
41
+ spec.add_development_dependency("byebug")
42
+ spec.add_development_dependency("dotenv", "~> 2.8")
43
+ spec.add_development_dependency("pry")
44
+ spec.add_development_dependency("rake", "~> 13.0")
45
+ spec.add_development_dependency("rspec", "~> 3.0")
46
+ spec.add_development_dependency("rubocop", "~> 1.21")
47
+ spec.add_development_dependency("rubocop-shopify", "~> 2.15")
48
+ spec.add_development_dependency("vcr", "~> 6.0")
49
+ spec.add_development_dependency("webmock", "~> 3.0")
50
+
51
+ # Security update for rexml
52
+ spec.add_development_dependency("rexml", ">= 3.3.9")
53
+
54
+ # For more information and examples about making a new gem, check out our
55
+ # guide at: https://bundler.io/guides/creating_gem.html
56
+ end
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "fileutils"
5
+
6
+ puts "Setting up test environment..."
7
+
8
+ env_file = ".env.test"
9
+
10
+ # Check if .env.test already exists
11
+ if File.exist?(env_file)
12
+ print "#{env_file} already exists. Overwrite? (y/n): "
13
+ response = gets.chomp.downcase
14
+ exit unless response == "y"
15
+ end
16
+
17
+ # Check for environment variables
18
+ client_id = ENV["PATCH_RETENTION_TEST_CLIENT_ID"] || ENV["PATCH_RETENTION_CLIENT_ID"]
19
+ client_secret = ENV["PATCH_RETENTION_TEST_CLIENT_SECRET"] || ENV["PATCH_RETENTION_CLIENT_SECRET"]
20
+
21
+ if client_id.nil? || client_secret.nil?
22
+ puts "\nError: Test credentials not found in environment variables."
23
+ puts "\nPlease set the following environment variables:"
24
+ puts " PATCH_RETENTION_TEST_CLIENT_ID"
25
+ puts " PATCH_RETENTION_TEST_CLIENT_SECRET"
26
+ puts "\nYou can export them in your shell:"
27
+ puts " export PATCH_RETENTION_TEST_CLIENT_ID=your_client_id"
28
+ puts " export PATCH_RETENTION_TEST_CLIENT_SECRET=your_client_secret"
29
+ puts "\nOr create a .env.test file manually with:"
30
+ puts " PATCH_RETENTION_CLIENT_ID=your_client_id"
31
+ puts " PATCH_RETENTION_CLIENT_SECRET=your_client_secret"
32
+ puts " PATCH_RETENTION_API_URL=https://api.patchretention.com/v2"
33
+ exit 1
34
+ end
35
+
36
+ # Create .env.test file
37
+ File.open(env_file, "w") do |f|
38
+ f.puts "PATCH_RETENTION_CLIENT_ID=#{client_id}"
39
+ f.puts "PATCH_RETENTION_CLIENT_SECRET=#{client_secret}"
40
+ f.puts "PATCH_RETENTION_API_URL=https://api.patchretention.com/v2"
41
+ end
42
+
43
+ puts "✓ Created #{env_file} successfully!"
44
+ puts "\nYou can now run tests with: bundle exec rake spec"
metadata CHANGED
@@ -1,16 +1,212 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patch_retention
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Playbypoint
8
8
  - Gerardo Ortega
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2025-04-29 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2025-05-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faraday
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: zeitwerk
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '2.6'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '2.6'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '2.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '2.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: bundler-audit
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.9'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.9'
70
+ - !ruby/object:Gem::Dependency
71
+ name: byebug
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: dotenv
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '2.8'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '2.8'
98
+ - !ruby/object:Gem::Dependency
99
+ name: pry
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rake
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '13.0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '13.0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rspec
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '3.0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '3.0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: rubocop
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: '1.21'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '1.21'
154
+ - !ruby/object:Gem::Dependency
155
+ name: rubocop-shopify
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - "~>"
159
+ - !ruby/object:Gem::Version
160
+ version: '2.15'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - "~>"
166
+ - !ruby/object:Gem::Version
167
+ version: '2.15'
168
+ - !ruby/object:Gem::Dependency
169
+ name: vcr
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - "~>"
173
+ - !ruby/object:Gem::Version
174
+ version: '6.0'
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - "~>"
180
+ - !ruby/object:Gem::Version
181
+ version: '6.0'
182
+ - !ruby/object:Gem::Dependency
183
+ name: webmock
184
+ requirement: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - "~>"
187
+ - !ruby/object:Gem::Version
188
+ version: '3.0'
189
+ type: :development
190
+ prerelease: false
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - "~>"
194
+ - !ruby/object:Gem::Version
195
+ version: '3.0'
196
+ - !ruby/object:Gem::Dependency
197
+ name: rexml
198
+ requirement: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: 3.3.9
203
+ type: :development
204
+ prerelease: false
205
+ version_requirements: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - ">="
208
+ - !ruby/object:Gem::Version
209
+ version: 3.3.9
14
210
  description: Patch Retention API wrapper.
15
211
  email:
16
212
  - webmaster@playbypoint.com
@@ -19,16 +215,27 @@ executables: []
19
215
  extensions: []
20
216
  extra_rdoc_files: []
21
217
  files:
218
+ - ".env.development"
219
+ - ".env.example"
220
+ - ".env.test.example"
22
221
  - ".rspec"
23
222
  - ".rubocop.yml"
223
+ - ".ruby-version"
24
224
  - ".tool-versions"
25
225
  - CHANGELOG.md
226
+ - CLAUDE.md
26
227
  - CODE_OF_CONDUCT.md
228
+ - DEPENDENCY_UPDATE_REPORT.md
27
229
  - Gemfile
28
230
  - Gemfile.lock
29
231
  - LICENSE.txt
30
232
  - README.md
31
233
  - Rakefile
234
+ - docs/decisions/001-api-implementation.md
235
+ - docs/patterns/testing-patterns.md
236
+ - docs/references/github-actions.md
237
+ - docs/references/troubleshooting.md
238
+ - docs/wip/session-2025-05-26.md
32
239
  - lib/patch_retention.rb
33
240
  - lib/patch_retention/configuration.rb
34
241
  - lib/patch_retention/contacts.rb
@@ -39,8 +246,12 @@ files:
39
246
  - lib/patch_retention/events.rb
40
247
  - lib/patch_retention/events/create.rb
41
248
  - lib/patch_retention/events/find.rb
249
+ - lib/patch_retention/memberships.rb
250
+ - lib/patch_retention/products.rb
42
251
  - lib/patch_retention/util.rb
43
252
  - lib/patch_retention/version.rb
253
+ - patch_retention.gemspec
254
+ - script/setup_test_env
44
255
  - sig/patch_retention.rbs
45
256
  homepage: https://playbypoint.com
46
257
  licenses:
@@ -49,7 +260,7 @@ metadata:
49
260
  homepage_uri: https://playbypoint.com
50
261
  source_code_uri: https://playbypoint.com
51
262
  changelog_uri: https://playbypoint.com
52
- post_install_message:
263
+ post_install_message:
53
264
  rdoc_options: []
54
265
  require_paths:
55
266
  - lib
@@ -57,15 +268,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
268
  requirements:
58
269
  - - ">="
59
270
  - !ruby/object:Gem::Version
60
- version: 2.6.0
271
+ version: 3.1.0
61
272
  required_rubygems_version: !ruby/object:Gem::Requirement
62
273
  requirements:
63
274
  - - ">="
64
275
  - !ruby/object:Gem::Version
65
276
  version: '0'
66
277
  requirements: []
67
- rubygems_version: 3.0.3.1
68
- signing_key:
278
+ rubygems_version: 3.3.27
279
+ signing_key:
69
280
  specification_version: 4
70
281
  summary: Patch Retention API wrapper.
71
282
  test_files: []