client-api-builder 0.5.6 → 0.6.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +53 -0
- data/.rubocop.yml +79 -0
- data/ARCHITECTURE.md +161 -86
- data/CLAUDE.md +92 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +46 -39
- data/README.md +427 -92
- data/client-api-builder.gemspec +20 -4
- data/examples/basic_auth_example_client.rb +6 -5
- data/examples/imdb_datasets_client.rb +2 -0
- data/examples/lorem_ipsum_client.rb +3 -1
- data/lib/client-api-builder.rb +1 -0
- data/lib/client_api_builder/active_support_log_subscriber.rb +1 -0
- data/lib/client_api_builder/active_support_notifications.rb +8 -6
- data/lib/client_api_builder/nested_router.rb +3 -3
- data/lib/client_api_builder/net_http_request.rb +37 -5
- data/lib/client_api_builder/query_params.rb +9 -3
- data/lib/client_api_builder/router.rb +210 -125
- data/lib/client_api_builder/section.rb +11 -11
- data/script/console +1 -1
- metadata +20 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae35befd024cba590ae2f644877243c536612ef9ce0e66c6c7c69f2481a7fc20
|
|
4
|
+
data.tar.gz: cd843e5f96ed9a63f9fb4012ab94b7ebe7b8bd443fa2cec35fe04e1bf50e0806
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a97833f34c6dde60bc8edbe9d38180e6f2904884ceb5b85a38e43358856e6e9c95f57e06f4356c0a10fe50d33801513e89132b452482bd689abf610a9ac53894
|
|
7
|
+
data.tar.gz: 9686a7f14ed8144f3e310cddf9abdf0681bfd237149397adba9d1376ca5d47a106530e099e165d22264bd7178b663e026a9e0658a2e95ef6a9b1fb1c3beb874e
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
name: RuboCop
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Ruby
|
|
17
|
+
uses: ruby/setup-ruby@v1
|
|
18
|
+
with:
|
|
19
|
+
ruby-version: '3.4'
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
|
|
22
|
+
- name: Run RuboCop
|
|
23
|
+
run: bundle exec rubocop --format github
|
|
24
|
+
|
|
25
|
+
test:
|
|
26
|
+
name: Tests (Ruby ${{ matrix.ruby }})
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
ruby: ['3.2', '3.3', '3.4']
|
|
32
|
+
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
|
|
36
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
|
37
|
+
uses: ruby/setup-ruby@v1
|
|
38
|
+
with:
|
|
39
|
+
ruby-version: ${{ matrix.ruby }}
|
|
40
|
+
bundler-cache: true
|
|
41
|
+
|
|
42
|
+
- name: Run tests
|
|
43
|
+
run: bundle exec rspec
|
|
44
|
+
|
|
45
|
+
- name: Upload coverage to Codecov
|
|
46
|
+
if: matrix.ruby == '3.4'
|
|
47
|
+
uses: codecov/codecov-action@v4
|
|
48
|
+
with:
|
|
49
|
+
files: coverage/coverage.xml
|
|
50
|
+
fail_ci_if_error: false
|
|
51
|
+
verbose: true
|
|
52
|
+
env:
|
|
53
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.2
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
Exclude:
|
|
6
|
+
- 'bin/**/*'
|
|
7
|
+
- 'vendor/**/*'
|
|
8
|
+
- 'coverage/**/*'
|
|
9
|
+
|
|
10
|
+
# Relaxed metrics for existing codebase
|
|
11
|
+
Metrics/MethodLength:
|
|
12
|
+
Max: 100
|
|
13
|
+
|
|
14
|
+
Metrics/AbcSize:
|
|
15
|
+
Max: 80
|
|
16
|
+
|
|
17
|
+
Metrics/ClassLength:
|
|
18
|
+
Max: 200
|
|
19
|
+
|
|
20
|
+
Metrics/ModuleLength:
|
|
21
|
+
Max: 320
|
|
22
|
+
|
|
23
|
+
Metrics/CyclomaticComplexity:
|
|
24
|
+
Max: 30
|
|
25
|
+
|
|
26
|
+
Metrics/PerceivedComplexity:
|
|
27
|
+
Max: 30
|
|
28
|
+
|
|
29
|
+
Metrics/BlockLength:
|
|
30
|
+
Exclude:
|
|
31
|
+
- 'spec/**/*'
|
|
32
|
+
- '*.gemspec'
|
|
33
|
+
|
|
34
|
+
Metrics/ParameterLists:
|
|
35
|
+
Max: 7
|
|
36
|
+
|
|
37
|
+
# Style preferences
|
|
38
|
+
Style/Documentation:
|
|
39
|
+
Enabled: false
|
|
40
|
+
|
|
41
|
+
Style/FrozenStringLiteralComment:
|
|
42
|
+
EnforcedStyle: always
|
|
43
|
+
|
|
44
|
+
Layout/LineLength:
|
|
45
|
+
Max: 170
|
|
46
|
+
Exclude:
|
|
47
|
+
- 'spec/**/*'
|
|
48
|
+
|
|
49
|
+
# File naming - allow hyphenated gem name
|
|
50
|
+
Naming/FileName:
|
|
51
|
+
Exclude:
|
|
52
|
+
- 'lib/client-api-builder.rb'
|
|
53
|
+
|
|
54
|
+
# Allow short parameter names for unused exception variables
|
|
55
|
+
Naming/MethodParameterName:
|
|
56
|
+
AllowedNames:
|
|
57
|
+
- e
|
|
58
|
+
- _e
|
|
59
|
+
- io
|
|
60
|
+
|
|
61
|
+
# Gemspec settings
|
|
62
|
+
Gemspec/RequiredRubyVersion:
|
|
63
|
+
Enabled: false
|
|
64
|
+
|
|
65
|
+
# Style relaxations for existing code patterns
|
|
66
|
+
Style/OptionalBooleanParameter:
|
|
67
|
+
Enabled: false
|
|
68
|
+
|
|
69
|
+
Style/StringConcatenation:
|
|
70
|
+
Enabled: false
|
|
71
|
+
|
|
72
|
+
Style/FormatStringToken:
|
|
73
|
+
Enabled: false
|
|
74
|
+
|
|
75
|
+
# Note: Previously had exclusions for Style/ClassVars, Style/PerlBackrefs, and
|
|
76
|
+
# Lint/RescueException in router.rb - these have been fixed:
|
|
77
|
+
# - ClassVars: Changed to thread-local storage
|
|
78
|
+
# - PerlBackrefs: Changed to Regexp.last_match
|
|
79
|
+
# - RescueException: Changed to StandardError
|
data/ARCHITECTURE.md
CHANGED
|
@@ -6,143 +6,218 @@ This document describes the internal architecture and design of the Client API B
|
|
|
6
6
|
|
|
7
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
8
|
|
|
9
|
+
## File Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
lib/
|
|
13
|
+
├── client-api-builder.rb # Main entry point, autoloads, error classes
|
|
14
|
+
└── client_api_builder/
|
|
15
|
+
├── router.rb # Core Router module with route DSL
|
|
16
|
+
├── nested_router.rb # NestedRouter class for hierarchical APIs
|
|
17
|
+
├── section.rb # Section module for creating nested routers
|
|
18
|
+
├── net_http_request.rb # Net::HTTP request execution and streaming
|
|
19
|
+
├── query_params.rb # Custom query parameter builder
|
|
20
|
+
├── active_support_notifications.rb # ActiveSupport instrumentation
|
|
21
|
+
└── active_support_log_subscriber.rb # ActiveSupport logging
|
|
22
|
+
```
|
|
23
|
+
|
|
9
24
|
## Core Components
|
|
10
25
|
|
|
11
26
|
### 1. Router Module (`ClientApiBuilder::Router`)
|
|
12
27
|
|
|
13
|
-
The `Router` module is the core component that provides the main functionality for defining and executing API requests.
|
|
28
|
+
The `Router` module is the core component that provides the main functionality for defining and executing API requests.
|
|
29
|
+
|
|
30
|
+
**Class Methods** (defined in `ClassMethods`):
|
|
31
|
+
- `base_url`: Sets the base URL for all requests
|
|
32
|
+
- `header`: Adds headers to requests (supports values, symbols, or procs)
|
|
33
|
+
- `route`: Defines API endpoints with dynamic method generation
|
|
34
|
+
- `body_builder`: Configures request body formatting (`:to_json`, `:to_query`, `:query_params`, or custom)
|
|
35
|
+
- `query_builder`: Configures query parameter formatting
|
|
36
|
+
- `query_param`: Adds query parameters to all requests
|
|
37
|
+
- `connection_option`: Sets Net::HTTP connection options
|
|
38
|
+
- `configure_retries`: Sets retry behavior (max_retries, sleep time)
|
|
39
|
+
- `namespace`: Groups routes under a common path prefix
|
|
40
|
+
|
|
41
|
+
**Instance Methods**:
|
|
42
|
+
- `build_headers`: Constructs request headers, evaluating procs/symbols
|
|
43
|
+
- `build_connection_options`: Merges default and request-specific options
|
|
44
|
+
- `build_query`: Formats query parameters using configured builder
|
|
45
|
+
- `build_body`: Formats request body using configured builder
|
|
46
|
+
- `build_uri`: Constructs full URI with base_url, path, and query
|
|
47
|
+
- `handle_response`: Processes API responses, parses JSON by default
|
|
48
|
+
- `request_wrapper`: Manages request execution with retry and instrumentation
|
|
49
|
+
- `root_router`: Returns self (overridden in NestedRouter)
|
|
50
|
+
|
|
51
|
+
**Instance Attributes** (via `attr_reader`):
|
|
52
|
+
- `response`: The last Net::HTTP response object
|
|
53
|
+
- `request_options`: Hash of method, uri, body, headers, connection_options
|
|
54
|
+
- `total_request_time`: Duration of last request in seconds
|
|
55
|
+
- `request_attempts`: Number of attempts for last request
|
|
56
|
+
|
|
57
|
+
### 2. Route Code Generation
|
|
58
|
+
|
|
59
|
+
The `route` class method dynamically generates two methods per endpoint using `generate_route_code`:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
route :get_user, '/users/:id', expected_response_code: 200
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Generates:
|
|
66
|
+
- `get_user_raw_response(id:, **options, &block)` - Makes HTTP request, sets `@response` and `@request_options`
|
|
67
|
+
- `get_user(id:, **options, &block)` - Wraps raw_response with retry logic, response code validation, and response handling
|
|
14
68
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
69
|
+
**Path Parameters**: Extracted from `:param` or `{param}` syntax in path
|
|
70
|
+
**Body/Query Parameters**: Extracted from `body:` and `query:` options using symbol values
|
|
22
71
|
|
|
23
|
-
|
|
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
|
|
72
|
+
### 3. HTTP Method Auto-Detection
|
|
30
73
|
|
|
31
|
-
|
|
74
|
+
When `method:` is not specified in route options, `auto_detect_http_method` infers it from the method name:
|
|
32
75
|
|
|
33
|
-
|
|
76
|
+
| Prefix Pattern | HTTP Method |
|
|
77
|
+
|---------------|-------------|
|
|
78
|
+
| `post`, `create`, `add`, `insert` | POST |
|
|
79
|
+
| `put`, `update`, `modify`, `change` | PUT |
|
|
80
|
+
| `patch` | PATCH |
|
|
81
|
+
| `delete`, `remove` | DELETE |
|
|
82
|
+
| (default) | GET |
|
|
34
83
|
|
|
35
|
-
|
|
36
|
-
- Maintains a reference to its root router
|
|
37
|
-
- Allows for nested route definitions
|
|
38
|
-
- Shares configuration with its parent router
|
|
84
|
+
### 4. Nested Router (`ClientApiBuilder::NestedRouter`)
|
|
39
85
|
|
|
40
|
-
|
|
86
|
+
Enables hierarchical API client organization:
|
|
41
87
|
|
|
42
|
-
|
|
88
|
+
```ruby
|
|
89
|
+
section :users do
|
|
90
|
+
route :list, '/'
|
|
91
|
+
route :get, '/:id'
|
|
92
|
+
end
|
|
93
|
+
# Usage: client.users.get(id: 123)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Key behaviors:
|
|
97
|
+
- Includes `ClientApiBuilder::Router` module
|
|
98
|
+
- Stores `root_router` reference to access shared state
|
|
99
|
+
- Stores `nested_router_options` passed from section definition
|
|
100
|
+
- Overrides `base_url` to fall back to root_router's base_url
|
|
101
|
+
- Delegates `handle_response` to root_router
|
|
102
|
+
- Overrides `get_instance_method` to access root_router's instance variables in paths
|
|
43
103
|
|
|
44
|
-
|
|
45
|
-
- Dynamically generates methods for accessing nested routers
|
|
46
|
-
- Handles the creation of nested router classes
|
|
104
|
+
### 5. Section Module (`ClientApiBuilder::Section`)
|
|
47
105
|
|
|
48
|
-
|
|
106
|
+
Creates nested routers dynamically using `InheritanceHelper::ClassBuilder::Utils.create_class`:
|
|
49
107
|
|
|
50
|
-
|
|
108
|
+
```ruby
|
|
109
|
+
def section(name, nested_router_options={}, &block)
|
|
110
|
+
# Creates: MyClient::UsersNestedRouter < ClientApiBuilder::NestedRouter
|
|
111
|
+
# Defines: MyClient.users_router (class method)
|
|
112
|
+
# Defines: MyClient#users (instance method, memoized)
|
|
113
|
+
end
|
|
114
|
+
```
|
|
51
115
|
|
|
52
|
-
|
|
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
|
|
116
|
+
### 6. NetHTTP::Request Module
|
|
56
117
|
|
|
57
|
-
|
|
118
|
+
Provides HTTP request execution using Net::HTTP:
|
|
58
119
|
|
|
59
|
-
|
|
120
|
+
**Methods**:
|
|
121
|
+
- `request(method:, uri:, body:, headers:, connection_options:)` - Standard request with optional block
|
|
122
|
+
- `stream(...)` - Streams response body in chunks via `read_body`
|
|
123
|
+
- `stream_to_io(..., io:)` - Writes streamed chunks to an IO object
|
|
124
|
+
- `stream_to_file(..., file:)` - Opens file and streams to it
|
|
60
125
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- **Query Parameter Building**: Uses ActiveSupport's parameter formatting when available
|
|
126
|
+
**Supported HTTP Methods** (via `METHOD_TO_NET_HTTP_CLASS`):
|
|
127
|
+
`copy`, `delete`, `get`, `head`, `lock`, `mkcol`, `move`, `options`, `patch`, `post`, `propfind`, `proppatch`, `put`, `trace`, `unlock`
|
|
64
128
|
|
|
65
|
-
|
|
129
|
+
### 7. QueryParams Class
|
|
66
130
|
|
|
67
|
-
|
|
131
|
+
Standalone query parameter builder (used when ActiveSupport unavailable):
|
|
68
132
|
|
|
69
|
-
|
|
133
|
+
- Handles nested hashes with bracket notation: `user[name]=John`
|
|
134
|
+
- Handles arrays: `ids[]=1&ids[]=2`
|
|
135
|
+
- Configurable separators: `name_value_separator` (default `=`), `param_separator` (default `&`)
|
|
136
|
+
- Supports custom escape proc
|
|
137
|
+
|
|
138
|
+
### 8. ActiveSupport Integration
|
|
139
|
+
|
|
140
|
+
**ActiveSupportNotifications** (conditionally included when ActiveSupport defined):
|
|
141
|
+
- Overrides `instrument_request` to use `ActiveSupport::Notifications.instrument`
|
|
142
|
+
- Event name: `client_api_builder.request`
|
|
143
|
+
- Payload includes `client: self`
|
|
144
|
+
|
|
145
|
+
**ActiveSupportLogSubscriber**:
|
|
146
|
+
- Subscribes to `client_api_builder.request` events for logging
|
|
147
|
+
|
|
148
|
+
## Design Patterns
|
|
149
|
+
|
|
150
|
+
### Module Inclusion Pattern
|
|
70
151
|
|
|
71
152
|
```ruby
|
|
72
153
|
module ClientApiBuilder
|
|
73
154
|
module Router
|
|
74
155
|
def self.included(base)
|
|
156
|
+
base.extend InheritanceHelper::Methods
|
|
75
157
|
base.extend ClassMethods
|
|
76
|
-
base.include
|
|
158
|
+
base.include ::ClientApiBuilder::Section
|
|
159
|
+
base.include ::ClientApiBuilder::NetHTTP::Request
|
|
160
|
+
base.include(::ClientApiBuilder::ActiveSupportNotifications) if defined?(ActiveSupport)
|
|
161
|
+
base.send(:attr_reader, :response, :request_options, :total_request_time, :request_attempts)
|
|
77
162
|
end
|
|
78
163
|
end
|
|
79
164
|
end
|
|
80
165
|
```
|
|
81
166
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
### 2. Builder Pattern
|
|
85
|
-
|
|
86
|
-
The request building process follows the builder pattern:
|
|
167
|
+
### Builder Pattern
|
|
87
168
|
|
|
169
|
+
Request components built separately then combined:
|
|
88
170
|
```ruby
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
end
|
|
171
|
+
__uri__ = build_uri(__path__, __query__, __options__)
|
|
172
|
+
__body__ = build_body(__body__, __options__)
|
|
173
|
+
__headers__ = build_headers(__options__)
|
|
174
|
+
__connection_options__ = build_connection_options(__options__)
|
|
94
175
|
```
|
|
95
176
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
### 3. Decorator Pattern
|
|
99
|
-
|
|
100
|
-
The nested router implementation uses a decorator-like pattern:
|
|
177
|
+
### Configuration Inheritance
|
|
101
178
|
|
|
179
|
+
Uses `inheritance-helper` gem's `add_value_to_class_method` for configuration that properly inherits to subclasses:
|
|
102
180
|
```ruby
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
# ... adds additional functionality while delegating to root_router
|
|
181
|
+
def base_url(url = nil)
|
|
182
|
+
return default_options[:base_url] unless url
|
|
183
|
+
add_value_to_class_method(:default_options, base_url: url)
|
|
107
184
|
end
|
|
108
185
|
```
|
|
109
186
|
|
|
110
|
-
## Configuration
|
|
111
|
-
|
|
112
|
-
The gem uses a hierarchical configuration system:
|
|
187
|
+
## Configuration Hierarchy
|
|
113
188
|
|
|
114
|
-
1. **Default Options**:
|
|
115
|
-
2. **Class-level Configuration**: Set through
|
|
116
|
-
3. **Instance-level
|
|
117
|
-
4. **Request-level
|
|
189
|
+
1. **Default Options**: `Router.default_options` returns frozen hash with defaults
|
|
190
|
+
2. **Class-level Configuration**: Set through DSL methods, stored via `add_value_to_class_method`
|
|
191
|
+
3. **Instance-level**: Access class config, can override in method calls
|
|
192
|
+
4. **Request-level**: `**__options__` parameter on generated methods
|
|
118
193
|
|
|
119
194
|
## Error Handling
|
|
120
195
|
|
|
121
|
-
The error handling system includes:
|
|
122
|
-
|
|
123
196
|
- `ClientApiBuilder::Error`: Base error class
|
|
124
|
-
- `ClientApiBuilder::UnexpectedResponse`:
|
|
125
|
-
-
|
|
197
|
+
- `ClientApiBuilder::UnexpectedResponse`: Raised when response code doesn't match expected codes
|
|
198
|
+
- Stores `response` for inspection
|
|
199
|
+
- Response procs: Per-route custom response handling stored in `default_options[:response_procs]`
|
|
200
|
+
- Retry on exception: `retry_request?` method (always returns true by default, override to customize)
|
|
126
201
|
|
|
127
|
-
##
|
|
202
|
+
## Streaming Support
|
|
128
203
|
|
|
129
|
-
|
|
204
|
+
Routes can specify streaming behavior:
|
|
130
205
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
206
|
+
```ruby
|
|
207
|
+
route :download, '/file', stream: :file # stream_to_file, requires file: argument
|
|
208
|
+
route :stream, '/events', stream: :io # stream_to_io, requires io: argument
|
|
209
|
+
route :process, '/data', stream: :block # stream with block for each chunk
|
|
210
|
+
route :download, '/file', stream: true # alias for :file
|
|
211
|
+
```
|
|
135
212
|
|
|
136
213
|
## Dependencies
|
|
137
214
|
|
|
138
|
-
- `inheritance-helper`:
|
|
139
|
-
- `json`:
|
|
140
|
-
- `net/http`:
|
|
141
|
-
- `
|
|
215
|
+
- `inheritance-helper`: Class inheritance and configuration management
|
|
216
|
+
- `json`: JSON parsing and serialization (stdlib)
|
|
217
|
+
- `net/http`: HTTP request handling (stdlib)
|
|
218
|
+
- `cgi`: URL encoding in QueryParams (stdlib)
|
|
219
|
+
- `active_support` (optional): Enhanced query building and instrumentation
|
|
142
220
|
|
|
143
|
-
##
|
|
221
|
+
## Thread Safety
|
|
144
222
|
|
|
145
|
-
|
|
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
|
|
223
|
+
The library is not thread-safe. Each client instance maintains state (`@response`, `@request_options`, etc.) that would cause race conditions if shared across threads. Create separate client instances per thread.
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
Client API Builder is a Ruby gem for creating API clients through declarative configuration. It uses Ruby's module inclusion pattern with `ClientApiBuilder::Router` as the core component.
|
|
8
|
+
|
|
9
|
+
## Common Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install dependencies
|
|
13
|
+
bundle install
|
|
14
|
+
|
|
15
|
+
# Run all tests
|
|
16
|
+
bundle exec rspec
|
|
17
|
+
|
|
18
|
+
# Run a single test file
|
|
19
|
+
bundle exec rspec spec/client_api_builder/router_spec.rb
|
|
20
|
+
|
|
21
|
+
# Run a specific test by line number
|
|
22
|
+
bundle exec rspec spec/client_api_builder/router_spec.rb:42
|
|
23
|
+
|
|
24
|
+
# Run linter
|
|
25
|
+
bundle exec rubocop
|
|
26
|
+
|
|
27
|
+
# Build the gem
|
|
28
|
+
gem build client-api-builder.gemspec
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Architecture
|
|
32
|
+
|
|
33
|
+
### Core Components
|
|
34
|
+
|
|
35
|
+
- **Router** (`lib/client_api_builder/router.rb`): Main module providing `route`, `base_url`, `header`, `body_builder`, `query_builder`, and `configure_retries` class methods. Uses `InheritanceHelper::Methods` for configuration inheritance.
|
|
36
|
+
|
|
37
|
+
- **NestedRouter** (`lib/client_api_builder/nested_router.rb`): Enables hierarchical API organization. Maintains reference to `root_router` and shares configuration with parent.
|
|
38
|
+
|
|
39
|
+
- **Section** (`lib/client_api_builder/section.rb`): Provides `section` class method for creating nested route groups via dynamically generated classes.
|
|
40
|
+
|
|
41
|
+
- **NetHTTP::Request** (`lib/client_api_builder/net_http_request.rb`): HTTP request execution using `Net::HTTP`. Handles standard requests and streaming (`:file`, `:io`, `:block` modes).
|
|
42
|
+
|
|
43
|
+
- **QueryParams** (`lib/client_api_builder/query_params.rb`): Custom query parameter builder used when ActiveSupport's `to_query` is unavailable.
|
|
44
|
+
|
|
45
|
+
- **ActiveSupportNotifications/LogSubscriber**: Optional integration for logging and instrumentation when ActiveSupport is present.
|
|
46
|
+
|
|
47
|
+
### Route Code Generation
|
|
48
|
+
|
|
49
|
+
The `route` class method in Router uses `generate_route_code` to dynamically create two methods per route:
|
|
50
|
+
1. `method_name_raw_response` - Makes the HTTP request
|
|
51
|
+
2. `method_name` - Wraps the request with retry logic and response handling
|
|
52
|
+
|
|
53
|
+
### HTTP Method Auto-Detection
|
|
54
|
+
|
|
55
|
+
Methods are auto-detected from route names: `post/create/add/insert` → POST, `put/update/modify/change` → PUT, `patch` → PATCH, `delete/remove` → DELETE, others → GET.
|
|
56
|
+
|
|
57
|
+
### Configuration Hierarchy
|
|
58
|
+
|
|
59
|
+
1. `default_options` class method (base defaults)
|
|
60
|
+
2. Class-level configuration via DSL methods
|
|
61
|
+
3. Instance-level overrides
|
|
62
|
+
4. Request-level options (`**__options__`)
|
|
63
|
+
|
|
64
|
+
## Key Patterns
|
|
65
|
+
|
|
66
|
+
- Module inclusion with `self.included(base)` extending ClassMethods and including InstanceMethods
|
|
67
|
+
- `add_value_to_class_method` from `inheritance-helper` for configuration inheritance
|
|
68
|
+
- Response procs stored per method name for custom response handling
|
|
69
|
+
- `root_router` method for accessing the top-level router from nested routers
|
|
70
|
+
|
|
71
|
+
## Dependencies
|
|
72
|
+
|
|
73
|
+
- `inheritance-helper` (runtime): Class inheritance and method management
|
|
74
|
+
- `webmock` (test): HTTP request stubbing
|
|
75
|
+
- `activesupport` (optional): Enhanced query param building and instrumentation
|
|
76
|
+
|
|
77
|
+
## Code Commits
|
|
78
|
+
|
|
79
|
+
Format using angular formatting:
|
|
80
|
+
```
|
|
81
|
+
<type>(<scope>): <short summary>
|
|
82
|
+
```
|
|
83
|
+
- **type**: build|ci|docs|feat|fix|perf|refactor|test
|
|
84
|
+
- **scope**: The feature or component of the service we're working on
|
|
85
|
+
- **summary**: Summary in present tense. Not capitalized. No period at the end.
|
|
86
|
+
|
|
87
|
+
## Documentation Maintenance
|
|
88
|
+
|
|
89
|
+
When modifying the codebase, keep documentation in sync:
|
|
90
|
+
- **ARCHITECTURE.md** - Update when adding/removing classes, changing component relationships, or altering data flow patterns
|
|
91
|
+
- **README.md** - Update when adding new features, changing public APIs, or modifying usage examples
|
|
92
|
+
- **Code comments** - Update inline documentation when changing method signatures or behavior
|
data/Gemfile
CHANGED