client-api-builder 0.5.5 → 0.5.6

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: 7ba2a10bca8dbdf440a037007da9858446a7c1b165575d49510e7ed37d9d53d6
4
- data.tar.gz: aeb66f9fbfeeefb4d946975641dba14002706ac07117dcaca5d41d89dbf10a7d
3
+ metadata.gz: 212fbedc035c5b2b1b406bd024a10b1739839d540a4be74ef2b67d57ed3a29d8
4
+ data.tar.gz: 18160a35e27ff5432a35a266b7062adda945de69f7c5e7eb6494a25cfbb66c5d
5
5
  SHA512:
6
- metadata.gz: a346879ca1a8dac4f6c849c68fb3c2553bdb298e490a0c5da6f38768d0a18f858d08c21ddbf119bff2975f5023d38592711292240437c721e111caa7023ec5bb
7
- data.tar.gz: 4f0faf034b9749c59cdb2c1d79adf7109e1bcd8ed1b85a1ffa189622a793d34d0f26df0c4e3a5f674700831c28f1ded0f40c8f9765657cc457e9da10b4f374e1
6
+ metadata.gz: fec92f548db39eec3123b5ea613f6d390af534def8c60fb1522323dd2d2c578cb6a657b896896aad05d36f7bcb9e93dcf60fc282f1c8cefa3c477113d675d106
7
+ data.tar.gz: ec306c4f5d78b85da0bbf5ea97c19a1e012053af75447d40600e2c358635a681579dbb933b44c40d46e4a0159a1ffe72f427270fa2833eeb34f9ad16db93575f
data/.cursor.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "rules": [
3
+ {
4
+ "name": "Ruby spec file",
5
+ "pattern": "^lib/(.+)\\.rb$",
6
+ "target": "spec/${1}_spec.rb"
7
+ },
8
+ {
9
+ "name": "Ruby implementation file",
10
+ "pattern": "^spec/(.+)_spec\\.rb$",
11
+ "target": "lib/${1}.rb"
12
+ },
13
+ {
14
+ "name": "Related client_api_builder files",
15
+ "pattern": "^(?:lib|spec)/client_api_builder/(.+)\\.rb$",
16
+ "related": [
17
+ "lib/client_api_builder/${1}.rb",
18
+ "spec/client_api_builder/${1}_spec.rb"
19
+ ]
20
+ },
21
+ {
22
+ "name": "Main library file",
23
+ "pattern": "^(?:lib|spec)/client_api_builder/.+\\.rb$",
24
+ "related": [
25
+ "lib/client-api-builder.rb"
26
+ ]
27
+ }
28
+ ]
29
+ }
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.4.2
data/ARCHITECTURE.md ADDED
@@ -0,0 +1,148 @@
1
+ # Client API Builder Architecture
2
+
3
+ This document describes the internal architecture and design of the Client API Builder gem.
4
+
5
+ ## Overview
6
+
7
+ Client API Builder is a Ruby gem that provides a declarative way to create API clients. It uses a modular architecture with several key components working together to provide a flexible and extensible API client framework.
8
+
9
+ ## Core Components
10
+
11
+ ### 1. Router Module (`ClientApiBuilder::Router`)
12
+
13
+ The `Router` module is the core component that provides the main functionality for defining and executing API requests. It includes:
14
+
15
+ - **Class Methods**: Configuration methods for setting up the API client
16
+ - `base_url`: Sets the base URL for all requests
17
+ - `header`: Adds headers to requests
18
+ - `route`: Defines API endpoints
19
+ - `body_builder`: Configures how request bodies are formatted
20
+ - `query_builder`: Configures how query parameters are formatted
21
+ - `configure_retries`: Sets retry behavior for failed requests
22
+
23
+ - **Instance Methods**: Methods for executing requests and handling responses
24
+ - `build_headers`: Constructs request headers
25
+ - `build_connection_options`: Sets up connection options
26
+ - `build_query`: Formats query parameters
27
+ - `build_body`: Formats request body
28
+ - `handle_response`: Processes API responses
29
+ - `request_wrapper`: Manages request execution and retries
30
+
31
+ ### 2. Nested Router (`ClientApiBuilder::NestedRouter`)
32
+
33
+ The `NestedRouter` class enables hierarchical API client organization by allowing routes to be grouped under namespaces. It:
34
+
35
+ - Inherits from the base Router functionality
36
+ - Maintains a reference to its root router
37
+ - Allows for nested route definitions
38
+ - Shares configuration with its parent router
39
+
40
+ ### 3. Section Module (`ClientApiBuilder::Section`)
41
+
42
+ The `Section` module provides the mechanism for creating nested routers. It:
43
+
44
+ - Defines the `section` class method for creating nested route groups
45
+ - Dynamically generates methods for accessing nested routers
46
+ - Handles the creation of nested router classes
47
+
48
+ ### 4. Request Handling
49
+
50
+ The request handling system is built on top of Ruby's `Net::HTTP` and includes:
51
+
52
+ - **Request Building**: Constructs HTTP requests with proper headers, body, and query parameters
53
+ - **Response Processing**: Handles different response formats and status codes
54
+ - **Error Handling**: Provides custom error classes for API-specific errors
55
+ - **Retry Mechanism**: Implements configurable retry logic for failed requests
56
+
57
+ ### 5. ActiveSupport Integration
58
+
59
+ The gem integrates with ActiveSupport for:
60
+
61
+ - **Logging**: Provides request/response logging
62
+ - **Notifications**: Implements instrumentation for request timing and monitoring
63
+ - **Query Parameter Building**: Uses ActiveSupport's parameter formatting when available
64
+
65
+ ## Design Patterns
66
+
67
+ ### 1. Module Inclusion Pattern
68
+
69
+ The gem uses Ruby's module inclusion pattern extensively:
70
+
71
+ ```ruby
72
+ module ClientApiBuilder
73
+ module Router
74
+ def self.included(base)
75
+ base.extend ClassMethods
76
+ base.include InstanceMethods
77
+ end
78
+ end
79
+ end
80
+ ```
81
+
82
+ This pattern allows for clean separation of class and instance methods while maintaining a single namespace.
83
+
84
+ ### 2. Builder Pattern
85
+
86
+ The request building process follows the builder pattern:
87
+
88
+ ```ruby
89
+ def build_headers(options)
90
+ headers = {}
91
+ # ... build headers
92
+ headers
93
+ end
94
+ ```
95
+
96
+ Each component (headers, body, query) is built separately and then combined into the final request.
97
+
98
+ ### 3. Decorator Pattern
99
+
100
+ The nested router implementation uses a decorator-like pattern:
101
+
102
+ ```ruby
103
+ class NestedRouter
104
+ include ::ClientApiBuilder::Router
105
+ attr_reader :root_router
106
+ # ... adds additional functionality while delegating to root_router
107
+ end
108
+ ```
109
+
110
+ ## Configuration Management
111
+
112
+ The gem uses a hierarchical configuration system:
113
+
114
+ 1. **Default Options**: Defined in `Router.default_options`
115
+ 2. **Class-level Configuration**: Set through class methods
116
+ 3. **Instance-level Configuration**: Can override class-level settings
117
+ 4. **Request-level Configuration**: Specific to individual requests
118
+
119
+ ## Error Handling
120
+
121
+ The error handling system includes:
122
+
123
+ - `ClientApiBuilder::Error`: Base error class
124
+ - `ClientApiBuilder::UnexpectedResponse`: For handling unexpected API responses
125
+ - Custom error handling through response procs
126
+
127
+ ## Extensibility
128
+
129
+ The architecture is designed to be extensible through:
130
+
131
+ 1. **Custom Body Builders**: Implement custom body formatting
132
+ 2. **Custom Query Builders**: Implement custom query parameter formatting
133
+ 3. **Response Processors**: Add custom response handling
134
+ 4. **Nested Routers**: Create custom nested router implementations
135
+
136
+ ## Dependencies
137
+
138
+ - `inheritance-helper`: For class inheritance and method management
139
+ - `json`: For JSON parsing and serialization
140
+ - `net/http`: For HTTP request handling
141
+ - `active_support` (optional): For additional functionality when available
142
+
143
+ ## Performance Considerations
144
+
145
+ 1. **Request Caching**: No built-in request caching
146
+ 2. **Connection Pooling**: Uses standard Net::HTTP connection handling
147
+ 3. **Memory Usage**: Minimal object creation during request processing
148
+ 4. **Thread Safety**: No explicit thread safety mechanisms
data/Gemfile CHANGED
@@ -7,9 +7,6 @@ gem 'inheritance-helper'
7
7
  group :development do
8
8
  gem 'rake'
9
9
  gem 'rubocop'
10
- end
11
-
12
- group :spec do
13
10
  gem 'activesupport'
14
11
  gem 'rspec'
15
12
  gem 'simplecov'
data/Gemfile.lock CHANGED
@@ -1,67 +1,93 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activesupport (7.0.2.3)
5
- concurrent-ruby (~> 1.0, >= 1.0.2)
4
+ activesupport (7.2.2.1)
5
+ base64
6
+ benchmark (>= 0.3)
7
+ bigdecimal
8
+ concurrent-ruby (~> 1.0, >= 1.3.1)
9
+ connection_pool (>= 2.2.5)
10
+ drb
6
11
  i18n (>= 1.6, < 2)
12
+ logger (>= 1.4.2)
7
13
  minitest (>= 5.1)
8
- tzinfo (~> 2.0)
9
- addressable (2.8.0)
10
- public_suffix (>= 2.0.2, < 5.0)
11
- ast (2.4.2)
12
- concurrent-ruby (1.1.9)
13
- crack (0.4.5)
14
+ securerandom (>= 0.3)
15
+ tzinfo (~> 2.0, >= 2.0.5)
16
+ addressable (2.8.7)
17
+ public_suffix (>= 2.0.2, < 7.0)
18
+ ast (2.4.3)
19
+ base64 (0.2.0)
20
+ benchmark (0.4.0)
21
+ bigdecimal (3.1.9)
22
+ concurrent-ruby (1.3.5)
23
+ connection_pool (2.5.3)
24
+ crack (1.0.0)
25
+ bigdecimal
14
26
  rexml
15
- diff-lcs (1.5.0)
16
- docile (1.4.0)
17
- hashdiff (1.0.1)
18
- i18n (1.10.0)
27
+ diff-lcs (1.6.1)
28
+ docile (1.4.1)
29
+ drb (2.2.1)
30
+ hashdiff (1.1.2)
31
+ i18n (1.14.7)
19
32
  concurrent-ruby (~> 1.0)
20
33
  inheritance-helper (0.2.5)
21
- minitest (5.15.0)
22
- parallel (1.21.0)
23
- parser (3.1.1.0)
34
+ json (2.11.3)
35
+ language_server-protocol (3.17.0.4)
36
+ lint_roller (1.1.0)
37
+ logger (1.7.0)
38
+ minitest (5.25.5)
39
+ parallel (1.27.0)
40
+ parser (3.3.8.0)
24
41
  ast (~> 2.4.1)
25
- public_suffix (4.0.6)
42
+ racc
43
+ prism (1.4.0)
44
+ public_suffix (6.0.2)
45
+ racc (1.8.1)
26
46
  rainbow (3.1.1)
27
- rake (13.0.6)
28
- regexp_parser (2.2.1)
29
- rexml (3.2.5)
30
- rspec (3.11.0)
31
- rspec-core (~> 3.11.0)
32
- rspec-expectations (~> 3.11.0)
33
- rspec-mocks (~> 3.11.0)
34
- rspec-core (3.11.0)
35
- rspec-support (~> 3.11.0)
36
- rspec-expectations (3.11.0)
47
+ rake (13.2.1)
48
+ regexp_parser (2.10.0)
49
+ rexml (3.4.1)
50
+ rspec (3.13.0)
51
+ rspec-core (~> 3.13.0)
52
+ rspec-expectations (~> 3.13.0)
53
+ rspec-mocks (~> 3.13.0)
54
+ rspec-core (3.13.3)
55
+ rspec-support (~> 3.13.0)
56
+ rspec-expectations (3.13.4)
37
57
  diff-lcs (>= 1.2.0, < 2.0)
38
- rspec-support (~> 3.11.0)
39
- rspec-mocks (3.11.0)
58
+ rspec-support (~> 3.13.0)
59
+ rspec-mocks (3.13.3)
40
60
  diff-lcs (>= 1.2.0, < 2.0)
41
- rspec-support (~> 3.11.0)
42
- rspec-support (3.11.0)
43
- rubocop (1.26.0)
61
+ rspec-support (~> 3.13.0)
62
+ rspec-support (3.13.3)
63
+ rubocop (1.75.4)
64
+ json (~> 2.3)
65
+ language_server-protocol (~> 3.17.0.2)
66
+ lint_roller (~> 1.1.0)
44
67
  parallel (~> 1.10)
45
- parser (>= 3.1.0.0)
68
+ parser (>= 3.3.0.2)
46
69
  rainbow (>= 2.2.2, < 4.0)
47
- regexp_parser (>= 1.8, < 3.0)
48
- rexml
49
- rubocop-ast (>= 1.16.0, < 2.0)
70
+ regexp_parser (>= 2.9.3, < 3.0)
71
+ rubocop-ast (>= 1.44.0, < 2.0)
50
72
  ruby-progressbar (~> 1.7)
51
- unicode-display_width (>= 1.4.0, < 3.0)
52
- rubocop-ast (1.16.0)
53
- parser (>= 3.1.1.0)
54
- ruby-progressbar (1.11.0)
55
- simplecov (0.21.2)
73
+ unicode-display_width (>= 2.4.0, < 4.0)
74
+ rubocop-ast (1.44.1)
75
+ parser (>= 3.3.7.2)
76
+ prism (~> 1.4)
77
+ ruby-progressbar (1.13.0)
78
+ securerandom (0.4.1)
79
+ simplecov (0.22.0)
56
80
  docile (~> 1.1)
57
81
  simplecov-html (~> 0.11)
58
82
  simplecov_json_formatter (~> 0.1)
59
- simplecov-html (0.12.3)
83
+ simplecov-html (0.13.1)
60
84
  simplecov_json_formatter (0.1.4)
61
- tzinfo (2.0.4)
85
+ tzinfo (2.0.6)
62
86
  concurrent-ruby (~> 1.0)
63
- unicode-display_width (2.1.0)
64
- webmock (3.14.0)
87
+ unicode-display_width (3.1.4)
88
+ unicode-emoji (~> 4.0, >= 4.0.4)
89
+ unicode-emoji (4.0.4)
90
+ webmock (3.25.1)
65
91
  addressable (>= 2.8.0)
66
92
  crack (>= 0.3.2)
67
93
  hashdiff (>= 0.4.0, < 2.0.0)
data/README.md CHANGED
@@ -1,33 +1,208 @@
1
- # client-api-builder
1
+ # Client API Builder
2
2
 
3
- Utility for creating API clients through configuration
3
+ A Ruby gem that provides a simple and elegant way to create API clients through configuration. It allows you to define API endpoints and their behavior declaratively, making it easy to create and maintain API clients.
4
4
 
5
- Example:
5
+ ## Features
6
+
7
+ - Declarative API client configuration
8
+ - Support for different request body formats (JSON, query params)
9
+ - Customizable headers
10
+ - Nested routing support
11
+ - ActiveSupport integration for logging and notifications
12
+ - Error handling with detailed response information
13
+ - Flexible parameter handling
14
+ - Automatic HTTP method detection based on method names
15
+ - Streaming support for handling large payloads
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'client-api-builder'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ ```bash
28
+ $ bundle install
29
+ ```
30
+
31
+ Or install it yourself as:
32
+
33
+ ```bash
34
+ $ gem install client-api-builder
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Basic Usage
40
+
41
+ Create an API client by including the `ClientApiBuilder::Router` module and defining your endpoints:
42
+
43
+ ```ruby
44
+ class MyApiClient
45
+ include ClientApiBuilder::Router
46
+
47
+ # Set the base URL for all requests
48
+ base_url 'https://api.example.com'
49
+
50
+ # Set default headers
51
+ header 'Content-Type', 'application/json'
52
+ header 'Accept', 'application/json'
6
53
 
54
+ # Define an endpoint
55
+ route :get_user, '/users/:id', method: :get, expected_response_code: 200
56
+ route :create_user, '/users', method: :post, expected_response_code: 201, body: { name: :name, email: :email }
57
+ end
58
+
59
+ # Use the client
60
+ client = MyApiClient.new
61
+ user = client.get_user(id: 123)
62
+ new_user = client.create_user(name: 'John', email: 'john@example.com')
7
63
  ```
8
- class LoremIpsumClient
64
+
65
+ ### Automatic HTTP Method Detection
66
+
67
+ The Router automatically detects the HTTP method based on the method name if not explicitly specified. This makes your API client code more intuitive and reduces boilerplate. The detection rules are:
68
+
69
+ - Methods starting with `post`, `create`, `add`, or `insert` → `POST`
70
+ - Methods starting with `put`, `update`, `modify`, or `change` → `PUT`
71
+ - Methods starting with `patch` → `PATCH`
72
+ - Methods starting with `delete` or `remove` → `DELETE`
73
+ - All other methods → `GET`
74
+
75
+ Example:
76
+
77
+ ```ruby
78
+ class MyApiClient
9
79
  include ClientApiBuilder::Router
80
+
81
+ base_url 'https://api.example.com'
82
+
83
+ # These will automatically use the appropriate HTTP methods
84
+ route :get_users, '/users' # Uses GET
85
+ route :create_user, '/users', body: { name: :name } # Uses POST
86
+ route :update_user, '/users/:id', body: { name: :name } # Uses PUT
87
+ route :delete_user, '/users/:id' # Uses DELETE
88
+
89
+ # You can still explicitly specify the method if needed
90
+ route :custom_action, '/custom', method: :post
91
+ end
92
+ ```
93
+
94
+ ### Request Body Formats
95
+
96
+ By default, the client converts the body data to JSON. To use query parameters instead:
10
97
 
11
- # by default it converts the body data to JSON
12
- # to convert the body to query params (x=1&y=2) use the following
13
- # if using active support change this to :to_query
98
+ ```ruby
99
+ class MyApiClient
100
+ include ClientApiBuilder::Router
101
+
102
+ # Use query parameters for the body
14
103
  body_builder :query_params
104
+
105
+ base_url 'https://api.example.com'
106
+
107
+ route :search, '/search', body: { q: :query, page: :page }
108
+ end
109
+ ```
15
110
 
16
- base_url 'https://www.lipsum.com'
111
+ ### Nested Routing
17
112
 
18
- header 'Content-Type', 'application/x-www-form-urlencoded'
19
- header 'Accept', 'application/json'
113
+ For APIs with nested resources, you can use the `NestedRouter`:
20
114
 
21
- # this creates a method called create_lorem_ipsum with 2 named arguments amont and what
22
- route :create_lorem_ipsum, '/feed/json', body: {amount: :amount, what: :what, start: 'yes', generate: 'Generate Lorem Ipsum'}
115
+ ```ruby
116
+ class MyApiClient
117
+ include ClientApiBuilder::Router
118
+
119
+ base_url 'https://api.example.com'
120
+
121
+ section :users do
122
+ route :list, '/', method: :get
123
+ route :get, '/:id', method: :get
124
+ end
23
125
  end
126
+
127
+ client = MyApiClient.new
128
+ users = client.users.list
129
+ user = client.users.get(id: 123)
24
130
  ```
25
131
 
26
- How to use:
132
+ ### Error Handling
27
133
 
134
+ The gem provides custom error classes for better error handling:
135
+
136
+ ```ruby
137
+ begin
138
+ client.get_user(id: 123)
139
+ rescue ClientApiBuilder::UnexpectedResponse => e
140
+ puts "Request failed with status #{e.response.status}"
141
+ puts "Response body: #{e.response.body}"
142
+ end
28
143
  ```
29
- client = LoremIpsumClient.new
30
- payload = client.create_lorem_ipsum(amount: 10, what: 'words')
31
- puts payload.dig('feed', 'lipsum')
32
- # outputs: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam at.
144
+
145
+ ### ActiveSupport Integration
146
+
147
+ The gem integrates with ActiveSupport for logging and notifications:
148
+
149
+ ```ruby
150
+ # Enable logging
151
+ ClientApiBuilder.logger = Logger.new(STDOUT)
152
+
153
+ # Subscribe to notifications
154
+ ActiveSupport::Notifications.subscribe('request.client_api_builder') do |*args|
155
+ event = ActiveSupport::Notifications::Event.new(*args)
156
+ puts "Request took #{event.duration}ms"
157
+ end
33
158
  ```
159
+
160
+ ### Streaming Support
161
+
162
+ The library supports streaming responses, which is particularly useful for handling large payloads. You can stream responses directly to a file or process them in chunks:
163
+
164
+ ```ruby
165
+ class MyApiClient
166
+ include ClientApiBuilder::Router
167
+
168
+ base_url 'https://api.example.com'
169
+
170
+ # Stream response directly to a file
171
+ route :download_users, '/users', stream: :file
172
+
173
+ # Stream response in chunks for custom processing
174
+ route :stream_users, '/users', stream: true
175
+ end
176
+
177
+ # Use the client
178
+ client = MyApiClient.new
179
+
180
+ # Stream to file
181
+ client.download_users('users.json') # Saves response directly to users.json
182
+
183
+ # Stream with custom processing
184
+ client.stream_users do |chunk|
185
+ # Process each chunk of the response
186
+ puts "Received #{chunk.bytesize} bytes"
187
+ end
188
+ ```
189
+
190
+ When using `stream: :file`, the response is written directly to disk as it's received, which is memory-efficient for large responses. The file path is passed as an argument to the method.
191
+
192
+ For custom streaming, you can provide a block that will be called with each chunk of the response as it's received. This allows for custom processing of large responses without loading the entire response into memory.
193
+
194
+ ## Configuration Options
195
+
196
+ - `base_url`: Set the base URL for all requests
197
+ - `header`: Add headers to all requests
198
+ - `body_builder`: Configure how request bodies are formatted
199
+ - `route`: Define API endpoints with their paths and parameters
200
+ - `nested_route`: Define nested resource routes
201
+
202
+ ## Contributing
203
+
204
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dougyouch/client-api-builder.
205
+
206
+ ## License
207
+
208
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'client-api-builder'
5
- s.version = '0.5.5'
5
+ s.version = '0.5.6'
6
6
  s.licenses = ['MIT']
7
7
  s.summary = 'Utility for creating API clients through configuration'
8
8
  s.description = 'Create API clients through configuration with complete transparency'
@@ -147,6 +147,8 @@ module ClientApiBuilder
147
147
  :post
148
148
  when /^(?:put|update|modify|change)/i
149
149
  :put
150
+ when /^(?:patch)/i
151
+ :patch
150
152
  when /^(?:delete|remove)/i
151
153
  :delete
152
154
  else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: client-api-builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doug Youch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-20 00:00:00.000000000 Z
11
+ date: 2025-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inheritance-helper
@@ -30,9 +30,11 @@ executables: []
30
30
  extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
+ - ".cursor.json"
33
34
  - ".gitignore"
34
35
  - ".ruby-gemset"
35
36
  - ".ruby-version"
37
+ - ARCHITECTURE.md
36
38
  - Gemfile
37
39
  - Gemfile.lock
38
40
  - LICENSE