client-api-builder 0.6.1 → 0.7.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/CHANGELOG.md +10 -0
- data/README.md +4 -4
- data/lib/client-api-builder.rb +2 -0
- data/lib/client_api_builder/net_http_request.rb +9 -4
- data/lib/client_api_builder/router.rb +11 -8
- data/lib/client_api_builder/version.rb +6 -0
- metadata +13 -27
- data/.cursor.json +0 -29
- data/.github/workflows/ci.yml +0 -53
- data/.gitignore +0 -51
- data/.rubocop.yml +0 -79
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/ARCHITECTURE.md +0 -223
- data/CLAUDE.md +0 -92
- data/Gemfile +0 -15
- data/Gemfile.lock +0 -115
- data/client-api-builder.gemspec +0 -31
- data/examples/basic_auth_example_client.rb +0 -46
- data/examples/imdb_datasets_client.rb +0 -33
- data/examples/lorem_ipsum_client.rb +0 -18
- data/script/console +0 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6ee8b61c4583f46d39c0d502192fb713f502ece28b7ea98092b73572efc9ffd4
|
|
4
|
+
data.tar.gz: bc80ea2afb78cdacc5067ebb45005a4bbcc997d13e79e9c6a9cf6b7edc8d5696
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4fd43d2172214d83d6b235096bd17f6289a07243dae96f3911b66c783d11caf1695ee541c2a0d2b547acf305616819c749bb32168ca62b5fa98880b042050925
|
|
7
|
+
data.tar.gz: e2d29d72f3c076be3b24d0941f317745ce367342cc96157f7c8d2a8592bc2dea0a733da1f4d9d5d40f46e1bf2859a01023ebb57c9ca31be4efca07e18267751e
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.7.0](https://github.com/dougyouch/client-api-builder/compare/v0.6.1...v0.7.0) (2026-10-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Build System
|
|
7
|
+
|
|
8
|
+
* **release:** release 0.7.0 ([fff1354](https://github.com/dougyouch/client-api-builder/commit/fff13543d5d8189e2bb7a84eda7e8ca3181f3469))
|
|
9
|
+
|
|
10
|
+
## Changelog
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Client API Builder
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/rb/client-api-builder)
|
|
4
|
-
[](https://github.com/dougyouch/client-api-builder/actions/workflows/ci.yml)
|
|
5
|
-
[](https://github.com/dougyouch/client-api-builder/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/dougyouch/client-api-builder/actions/workflows/ci.yml)
|
|
6
6
|
|
|
7
7
|
A Ruby gem for building robust, secure API clients through declarative configuration. Define your API endpoints and their behavior with minimal boilerplate while benefiting from built-in security features, automatic retries, and comprehensive error handling.
|
|
8
8
|
|
|
@@ -544,7 +544,7 @@ end
|
|
|
544
544
|
|
|
545
545
|
## Requirements
|
|
546
546
|
|
|
547
|
-
- Ruby 3.
|
|
547
|
+
- Ruby 3.2+
|
|
548
548
|
- `inheritance-helper` gem (>= 0.2.5)
|
|
549
549
|
|
|
550
550
|
## Contributing
|
|
@@ -556,7 +556,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/dougyo
|
|
|
556
556
|
3. Write tests for your changes
|
|
557
557
|
4. Ensure all tests pass (`bundle exec rspec`)
|
|
558
558
|
5. Ensure code style compliance (`bundle exec rubocop`)
|
|
559
|
-
6. Commit your changes (`git commit -am '
|
|
559
|
+
6. Commit your changes using [conventional commits](https://www.conventionalcommits.org/) (`git commit -am 'feat(router): add my feature'`); release notes and version bumps are generated from them
|
|
560
560
|
7. Push to the branch (`git push origin feature/my-feature`)
|
|
561
561
|
8. Create a Pull Request
|
|
562
562
|
|
data/lib/client-api-builder.rb
CHANGED
|
@@ -51,7 +51,8 @@ module ClientApiBuilder
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def stream(method:, uri:, body:, headers:, connection_options:)
|
|
54
|
-
request(method: method, uri: uri, body: body, headers: headers,
|
|
54
|
+
request(method: method, uri: uri, body: body, headers: headers,
|
|
55
|
+
connection_options: connection_options) do |response|
|
|
55
56
|
response.read_body do |chunk|
|
|
56
57
|
yield response, chunk
|
|
57
58
|
end
|
|
@@ -59,7 +60,8 @@ module ClientApiBuilder
|
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def stream_to_io(method:, uri:, body:, headers:, connection_options:, io:)
|
|
62
|
-
stream(method: method, uri: uri, body: body, headers: headers,
|
|
63
|
+
stream(method: method, uri: uri, body: body, headers: headers,
|
|
64
|
+
connection_options: connection_options) do |_, chunk|
|
|
63
65
|
io.write chunk
|
|
64
66
|
end
|
|
65
67
|
end
|
|
@@ -75,12 +77,15 @@ module ClientApiBuilder
|
|
|
75
77
|
elsif ALLOWED_FILE_MODES.include?(mode.to_s)
|
|
76
78
|
mode.to_s
|
|
77
79
|
else
|
|
78
|
-
raise ArgumentError,
|
|
80
|
+
raise ArgumentError,
|
|
81
|
+
"Invalid file mode: #{mode.inspect}. Allowed modes: #{ALLOWED_FILE_MODES.join(', ')}"
|
|
79
82
|
end
|
|
80
83
|
|
|
81
84
|
# Validate file path - expand to absolute path and check for path traversal
|
|
82
85
|
expanded_path = File.expand_path(file)
|
|
83
|
-
|
|
86
|
+
if file.to_s.include?('..') || expanded_path.include?("\0")
|
|
87
|
+
raise ArgumentError, 'Invalid file path: potential path traversal detected'
|
|
88
|
+
end
|
|
84
89
|
|
|
85
90
|
File.open(expanded_path, mode) do |io|
|
|
86
91
|
stream_to_io(method: method, uri: uri, body: body, headers: headers, connection_options: opts, io: io)
|
|
@@ -75,13 +75,14 @@ module ClientApiBuilder
|
|
|
75
75
|
uri = URI.parse(url.to_s)
|
|
76
76
|
return if ALLOWED_URL_SCHEMES.include?(uri.scheme&.downcase)
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
allowed = ALLOWED_URL_SCHEMES.join(', ')
|
|
79
|
+
raise ArgumentError, "Invalid base_url scheme: #{uri.scheme.inspect}. Allowed: #{allowed}"
|
|
79
80
|
rescue URI::InvalidURIError => e
|
|
80
81
|
raise ArgumentError, "Invalid base_url: #{e.message}"
|
|
81
82
|
end
|
|
82
83
|
|
|
83
|
-
# set the builder to :to_json, :to_query, :query_params or specify a proc
|
|
84
|
-
# or get the body builder
|
|
84
|
+
# set the builder to :to_json, :to_query, :query_params or specify a proc
|
|
85
|
+
# to handle building the request body payload, or get the body builder
|
|
85
86
|
def body_builder(builder = nil, &block)
|
|
86
87
|
return default_options[:body_builder] if builder.nil? && block.nil?
|
|
87
88
|
|
|
@@ -332,7 +333,7 @@ module ClientApiBuilder
|
|
|
332
333
|
end
|
|
333
334
|
|
|
334
335
|
def extract_expected_response_codes(options)
|
|
335
|
-
codes = options[:expected_response_codes] || (options[:expected_response_code]
|
|
336
|
+
codes = options[:expected_response_codes] || Array(options[:expected_response_code])
|
|
336
337
|
codes.map(&:to_s)
|
|
337
338
|
end
|
|
338
339
|
|
|
@@ -374,7 +375,9 @@ module ClientApiBuilder
|
|
|
374
375
|
|
|
375
376
|
def generate_route_code(method_name, path, options = {})
|
|
376
377
|
# Validate method_name to prevent code injection
|
|
377
|
-
|
|
378
|
+
unless method_name.to_s.match?(/\A[a-z_][a-z0-9_]*\z/i)
|
|
379
|
+
raise ArgumentError, "Invalid method name: #{method_name.inspect}"
|
|
380
|
+
end
|
|
378
381
|
|
|
379
382
|
http_method = options[:method] || auto_detect_http_method(method_name)
|
|
380
383
|
path, path_arguments = process_route_path(path)
|
|
@@ -410,7 +413,7 @@ module ClientApiBuilder
|
|
|
410
413
|
code += " @request_options = {method: #{ctx[:http_method].inspect}, uri: __uri__, body: __body__, " \
|
|
411
414
|
"headers: __headers__, connection_options: __connection_options__}\n"
|
|
412
415
|
code += generate_request_call_code(ctx[:options], ctx[:stream_param])
|
|
413
|
-
code
|
|
416
|
+
"#{code}end\n\n"
|
|
414
417
|
end
|
|
415
418
|
|
|
416
419
|
def generate_wrapper_method(ctx)
|
|
@@ -424,7 +427,7 @@ module ClientApiBuilder
|
|
|
424
427
|
code += " expected_response_code!(@response, __expected_response_codes__, __options__)\n"
|
|
425
428
|
code += generate_response_handling_code(ctx[:options])
|
|
426
429
|
code += " end\n"
|
|
427
|
-
code
|
|
430
|
+
"#{code}end\n"
|
|
428
431
|
end
|
|
429
432
|
|
|
430
433
|
def route(method_name, path, options = {}, &block)
|
|
@@ -577,7 +580,7 @@ module ClientApiBuilder
|
|
|
577
580
|
end
|
|
578
581
|
end
|
|
579
582
|
|
|
580
|
-
def get_retry_request_sleep_time(
|
|
583
|
+
def get_retry_request_sleep_time(_exception, options)
|
|
581
584
|
options[:sleep] || self.class.default_options[:sleep] || 0.05
|
|
582
585
|
end
|
|
583
586
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: client-api-builder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Doug Youch
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: inheritance-helper
|
|
@@ -23,33 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 0.2.5
|
|
26
|
-
description:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
description: Client API Builder generates HTTP client methods from a declarative route
|
|
27
|
+
DSL. It infers HTTP methods from route names, builds query strings and request bodies,
|
|
28
|
+
and supports nested routers, configurable retries, and streaming responses to files
|
|
29
|
+
or IO. SSL verification, base URL scheme checks, and path traversal protection are
|
|
30
|
+
on by default. Optional ActiveSupport integration adds instrumentation and request
|
|
31
|
+
logging.
|
|
32
32
|
email: dougyouch@gmail.com
|
|
33
33
|
executables: []
|
|
34
34
|
extensions: []
|
|
35
35
|
extra_rdoc_files: []
|
|
36
36
|
files:
|
|
37
|
-
-
|
|
38
|
-
- ".github/workflows/ci.yml"
|
|
39
|
-
- ".gitignore"
|
|
40
|
-
- ".rubocop.yml"
|
|
41
|
-
- ".ruby-gemset"
|
|
42
|
-
- ".ruby-version"
|
|
43
|
-
- ARCHITECTURE.md
|
|
44
|
-
- CLAUDE.md
|
|
45
|
-
- Gemfile
|
|
46
|
-
- Gemfile.lock
|
|
37
|
+
- CHANGELOG.md
|
|
47
38
|
- LICENSE
|
|
48
39
|
- README.md
|
|
49
|
-
- client-api-builder.gemspec
|
|
50
|
-
- examples/basic_auth_example_client.rb
|
|
51
|
-
- examples/imdb_datasets_client.rb
|
|
52
|
-
- examples/lorem_ipsum_client.rb
|
|
53
40
|
- lib/client-api-builder.rb
|
|
54
41
|
- lib/client_api_builder/active_support_log_subscriber.rb
|
|
55
42
|
- lib/client_api_builder/active_support_notifications.rb
|
|
@@ -58,13 +45,12 @@ files:
|
|
|
58
45
|
- lib/client_api_builder/query_params.rb
|
|
59
46
|
- lib/client_api_builder/router.rb
|
|
60
47
|
- lib/client_api_builder/section.rb
|
|
61
|
-
-
|
|
48
|
+
- lib/client_api_builder/version.rb
|
|
62
49
|
homepage: https://github.com/dougyouch/client-api-builder
|
|
63
50
|
licenses:
|
|
64
51
|
- MIT
|
|
65
52
|
metadata:
|
|
66
53
|
rubygems_mfa_required: 'true'
|
|
67
|
-
homepage_uri: https://github.com/dougyouch/client-api-builder
|
|
68
54
|
source_code_uri: https://github.com/dougyouch/client-api-builder
|
|
69
55
|
changelog_uri: https://github.com/dougyouch/client-api-builder/blob/master/CHANGELOG.md
|
|
70
56
|
bug_tracker_uri: https://github.com/dougyouch/client-api-builder/issues
|
|
@@ -75,14 +61,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
75
61
|
requirements:
|
|
76
62
|
- - ">="
|
|
77
63
|
- !ruby/object:Gem::Version
|
|
78
|
-
version: '3.
|
|
64
|
+
version: '3.2'
|
|
79
65
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
66
|
requirements:
|
|
81
67
|
- - ">="
|
|
82
68
|
- !ruby/object:Gem::Version
|
|
83
69
|
version: '0'
|
|
84
70
|
requirements: []
|
|
85
|
-
rubygems_version:
|
|
71
|
+
rubygems_version: 4.0.20
|
|
86
72
|
specification_version: 4
|
|
87
|
-
summary: Build
|
|
73
|
+
summary: Build Ruby HTTP API clients from declarative route definitions
|
|
88
74
|
test_files: []
|
data/.cursor.json
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
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/.github/workflows/ci.yml
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
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/.gitignore
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# rcov generated
|
|
2
|
-
coverage
|
|
3
|
-
coverage.data
|
|
4
|
-
|
|
5
|
-
# rdoc generated
|
|
6
|
-
rdoc
|
|
7
|
-
|
|
8
|
-
# yard generated
|
|
9
|
-
doc
|
|
10
|
-
.yardoc
|
|
11
|
-
|
|
12
|
-
# bundler
|
|
13
|
-
.bundle
|
|
14
|
-
|
|
15
|
-
# jeweler generated
|
|
16
|
-
pkg
|
|
17
|
-
|
|
18
|
-
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
|
19
|
-
#
|
|
20
|
-
# * Create a file at ~/.gitignore
|
|
21
|
-
# * Include files you want ignored
|
|
22
|
-
# * Run: git config --global core.excludesfile ~/.gitignore
|
|
23
|
-
#
|
|
24
|
-
# After doing this, these files will be ignored in all your git projects,
|
|
25
|
-
# saving you from having to 'pollute' every project you touch with them
|
|
26
|
-
#
|
|
27
|
-
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
|
28
|
-
#
|
|
29
|
-
# For MacOS:
|
|
30
|
-
#
|
|
31
|
-
.DS_Store
|
|
32
|
-
|
|
33
|
-
# For TextMate
|
|
34
|
-
#*.tmproj
|
|
35
|
-
#tmtags
|
|
36
|
-
|
|
37
|
-
# For emacs:
|
|
38
|
-
*~
|
|
39
|
-
\#*
|
|
40
|
-
.\#*
|
|
41
|
-
|
|
42
|
-
# For vim:
|
|
43
|
-
*.swp
|
|
44
|
-
|
|
45
|
-
# For redcar:
|
|
46
|
-
#.redcar
|
|
47
|
-
|
|
48
|
-
# For rubinius:
|
|
49
|
-
#*.rbc
|
|
50
|
-
|
|
51
|
-
*.gem
|
data/.rubocop.yml
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
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: 340
|
|
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/.ruby-gemset
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
client-api-builder
|
data/.ruby-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.4.2
|
data/ARCHITECTURE.md
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
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
|
-
## 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
|
-
|
|
24
|
-
## Core Components
|
|
25
|
-
|
|
26
|
-
### 1. Router Module (`ClientApiBuilder::Router`)
|
|
27
|
-
|
|
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
|
|
68
|
-
|
|
69
|
-
**Path Parameters**: Extracted from `:param` or `{param}` syntax in path
|
|
70
|
-
**Body/Query Parameters**: Extracted from `body:` and `query:` options using symbol values
|
|
71
|
-
|
|
72
|
-
### 3. HTTP Method Auto-Detection
|
|
73
|
-
|
|
74
|
-
When `method:` is not specified in route options, `auto_detect_http_method` infers it from the method name:
|
|
75
|
-
|
|
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 |
|
|
83
|
-
|
|
84
|
-
### 4. Nested Router (`ClientApiBuilder::NestedRouter`)
|
|
85
|
-
|
|
86
|
-
Enables hierarchical API client organization:
|
|
87
|
-
|
|
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
|
|
103
|
-
|
|
104
|
-
### 5. Section Module (`ClientApiBuilder::Section`)
|
|
105
|
-
|
|
106
|
-
Creates nested routers dynamically using `InheritanceHelper::ClassBuilder::Utils.create_class`:
|
|
107
|
-
|
|
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
|
-
```
|
|
115
|
-
|
|
116
|
-
### 6. NetHTTP::Request Module
|
|
117
|
-
|
|
118
|
-
Provides HTTP request execution using Net::HTTP:
|
|
119
|
-
|
|
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
|
|
125
|
-
|
|
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`
|
|
128
|
-
|
|
129
|
-
### 7. QueryParams Class
|
|
130
|
-
|
|
131
|
-
Standalone query parameter builder (used when ActiveSupport unavailable):
|
|
132
|
-
|
|
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
|
|
151
|
-
|
|
152
|
-
```ruby
|
|
153
|
-
module ClientApiBuilder
|
|
154
|
-
module Router
|
|
155
|
-
def self.included(base)
|
|
156
|
-
base.extend InheritanceHelper::Methods
|
|
157
|
-
base.extend ClassMethods
|
|
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)
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### Builder Pattern
|
|
168
|
-
|
|
169
|
-
Request components built separately then combined:
|
|
170
|
-
```ruby
|
|
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__)
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Configuration Inheritance
|
|
178
|
-
|
|
179
|
-
Uses `inheritance-helper` gem's `add_value_to_class_method` for configuration that properly inherits to subclasses:
|
|
180
|
-
```ruby
|
|
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)
|
|
184
|
-
end
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Configuration Hierarchy
|
|
188
|
-
|
|
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
|
|
193
|
-
|
|
194
|
-
## Error Handling
|
|
195
|
-
|
|
196
|
-
- `ClientApiBuilder::Error`: Base error class
|
|
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)
|
|
201
|
-
|
|
202
|
-
## Streaming Support
|
|
203
|
-
|
|
204
|
-
Routes can specify streaming behavior:
|
|
205
|
-
|
|
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
|
-
```
|
|
212
|
-
|
|
213
|
-
## Dependencies
|
|
214
|
-
|
|
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
|
|
220
|
-
|
|
221
|
-
## Thread Safety
|
|
222
|
-
|
|
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
DELETED
|
@@ -1,92 +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
|
-
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
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
source 'http://rubygems.org'
|
|
4
|
-
|
|
5
|
-
gem 'inheritance-helper'
|
|
6
|
-
|
|
7
|
-
group :development do
|
|
8
|
-
gem 'activesupport'
|
|
9
|
-
gem 'rake'
|
|
10
|
-
gem 'rspec'
|
|
11
|
-
gem 'rubocop'
|
|
12
|
-
gem 'simplecov'
|
|
13
|
-
gem 'simplecov-cobertura'
|
|
14
|
-
gem 'webmock'
|
|
15
|
-
end
|
data/Gemfile.lock
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
GEM
|
|
2
|
-
remote: http://rubygems.org/
|
|
3
|
-
specs:
|
|
4
|
-
activesupport (8.1.2)
|
|
5
|
-
base64
|
|
6
|
-
bigdecimal
|
|
7
|
-
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
8
|
-
connection_pool (>= 2.2.5)
|
|
9
|
-
drb
|
|
10
|
-
i18n (>= 1.6, < 2)
|
|
11
|
-
json
|
|
12
|
-
logger (>= 1.4.2)
|
|
13
|
-
minitest (>= 5.1)
|
|
14
|
-
securerandom (>= 0.3)
|
|
15
|
-
tzinfo (~> 2.0, >= 2.0.5)
|
|
16
|
-
uri (>= 0.13.1)
|
|
17
|
-
addressable (2.8.8)
|
|
18
|
-
public_suffix (>= 2.0.2, < 8.0)
|
|
19
|
-
ast (2.4.3)
|
|
20
|
-
base64 (0.3.0)
|
|
21
|
-
bigdecimal (4.0.1)
|
|
22
|
-
concurrent-ruby (1.3.6)
|
|
23
|
-
connection_pool (3.0.2)
|
|
24
|
-
crack (1.0.1)
|
|
25
|
-
bigdecimal
|
|
26
|
-
rexml
|
|
27
|
-
diff-lcs (1.6.2)
|
|
28
|
-
docile (1.4.1)
|
|
29
|
-
drb (2.2.3)
|
|
30
|
-
hashdiff (1.2.1)
|
|
31
|
-
i18n (1.14.8)
|
|
32
|
-
concurrent-ruby (~> 1.0)
|
|
33
|
-
inheritance-helper (0.2.5)
|
|
34
|
-
json (2.18.0)
|
|
35
|
-
language_server-protocol (3.17.0.5)
|
|
36
|
-
lint_roller (1.1.0)
|
|
37
|
-
logger (1.7.0)
|
|
38
|
-
minitest (6.0.1)
|
|
39
|
-
prism (~> 1.5)
|
|
40
|
-
parallel (1.27.0)
|
|
41
|
-
parser (3.3.10.1)
|
|
42
|
-
ast (~> 2.4.1)
|
|
43
|
-
racc
|
|
44
|
-
prism (1.9.0)
|
|
45
|
-
public_suffix (7.0.2)
|
|
46
|
-
racc (1.8.1)
|
|
47
|
-
rainbow (3.1.1)
|
|
48
|
-
rake (13.3.1)
|
|
49
|
-
regexp_parser (2.11.3)
|
|
50
|
-
rexml (3.4.4)
|
|
51
|
-
rspec (3.13.2)
|
|
52
|
-
rspec-core (~> 3.13.0)
|
|
53
|
-
rspec-expectations (~> 3.13.0)
|
|
54
|
-
rspec-mocks (~> 3.13.0)
|
|
55
|
-
rspec-core (3.13.6)
|
|
56
|
-
rspec-support (~> 3.13.0)
|
|
57
|
-
rspec-expectations (3.13.5)
|
|
58
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
|
59
|
-
rspec-support (~> 3.13.0)
|
|
60
|
-
rspec-mocks (3.13.7)
|
|
61
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
|
62
|
-
rspec-support (~> 3.13.0)
|
|
63
|
-
rspec-support (3.13.7)
|
|
64
|
-
rubocop (1.84.0)
|
|
65
|
-
json (~> 2.3)
|
|
66
|
-
language_server-protocol (~> 3.17.0.2)
|
|
67
|
-
lint_roller (~> 1.1.0)
|
|
68
|
-
parallel (~> 1.10)
|
|
69
|
-
parser (>= 3.3.0.2)
|
|
70
|
-
rainbow (>= 2.2.2, < 4.0)
|
|
71
|
-
regexp_parser (>= 2.9.3, < 3.0)
|
|
72
|
-
rubocop-ast (>= 1.49.0, < 2.0)
|
|
73
|
-
ruby-progressbar (~> 1.7)
|
|
74
|
-
unicode-display_width (>= 2.4.0, < 4.0)
|
|
75
|
-
rubocop-ast (1.49.0)
|
|
76
|
-
parser (>= 3.3.7.2)
|
|
77
|
-
prism (~> 1.7)
|
|
78
|
-
ruby-progressbar (1.13.0)
|
|
79
|
-
securerandom (0.4.1)
|
|
80
|
-
simplecov (0.22.0)
|
|
81
|
-
docile (~> 1.1)
|
|
82
|
-
simplecov-html (~> 0.11)
|
|
83
|
-
simplecov_json_formatter (~> 0.1)
|
|
84
|
-
simplecov-cobertura (3.1.0)
|
|
85
|
-
rexml
|
|
86
|
-
simplecov (~> 0.19)
|
|
87
|
-
simplecov-html (0.13.2)
|
|
88
|
-
simplecov_json_formatter (0.1.4)
|
|
89
|
-
tzinfo (2.0.6)
|
|
90
|
-
concurrent-ruby (~> 1.0)
|
|
91
|
-
unicode-display_width (3.2.0)
|
|
92
|
-
unicode-emoji (~> 4.1)
|
|
93
|
-
unicode-emoji (4.2.0)
|
|
94
|
-
uri (1.1.1)
|
|
95
|
-
webmock (3.26.1)
|
|
96
|
-
addressable (>= 2.8.0)
|
|
97
|
-
crack (>= 0.3.2)
|
|
98
|
-
hashdiff (>= 0.4.0, < 2.0.0)
|
|
99
|
-
|
|
100
|
-
PLATFORMS
|
|
101
|
-
arm64-darwin-24
|
|
102
|
-
ruby
|
|
103
|
-
|
|
104
|
-
DEPENDENCIES
|
|
105
|
-
activesupport
|
|
106
|
-
inheritance-helper
|
|
107
|
-
rake
|
|
108
|
-
rspec
|
|
109
|
-
rubocop
|
|
110
|
-
simplecov
|
|
111
|
-
simplecov-cobertura
|
|
112
|
-
webmock
|
|
113
|
-
|
|
114
|
-
BUNDLED WITH
|
|
115
|
-
2.6.2
|
data/client-api-builder.gemspec
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Gem::Specification.new do |s|
|
|
4
|
-
s.name = 'client-api-builder'
|
|
5
|
-
s.version = '0.6.1'
|
|
6
|
-
s.licenses = ['MIT']
|
|
7
|
-
s.summary = 'Build robust, secure API clients through declarative configuration'
|
|
8
|
-
s.description = <<~DESC
|
|
9
|
-
A Ruby gem for building API clients through declarative configuration. Features include
|
|
10
|
-
automatic HTTP method detection, nested routing, streaming support, configurable retries,
|
|
11
|
-
and security features like SSL verification, SSRF protection, and path traversal prevention.
|
|
12
|
-
Define your API endpoints with a clean DSL and get comprehensive error handling, debugging
|
|
13
|
-
capabilities, and optional ActiveSupport integration for logging and instrumentation.
|
|
14
|
-
DESC
|
|
15
|
-
s.authors = ['Doug Youch']
|
|
16
|
-
s.email = 'dougyouch@gmail.com'
|
|
17
|
-
s.homepage = 'https://github.com/dougyouch/client-api-builder'
|
|
18
|
-
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
19
|
-
|
|
20
|
-
s.required_ruby_version = '>= 3.0'
|
|
21
|
-
|
|
22
|
-
s.add_dependency 'inheritance-helper', '>= 0.2.5'
|
|
23
|
-
|
|
24
|
-
s.metadata = {
|
|
25
|
-
'rubygems_mfa_required' => 'true',
|
|
26
|
-
'homepage_uri' => s.homepage,
|
|
27
|
-
'source_code_uri' => 'https://github.com/dougyouch/client-api-builder',
|
|
28
|
-
'changelog_uri' => 'https://github.com/dougyouch/client-api-builder/blob/master/CHANGELOG.md',
|
|
29
|
-
'bug_tracker_uri' => 'https://github.com/dougyouch/client-api-builder/issues'
|
|
30
|
-
}
|
|
31
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'base64'
|
|
4
|
-
require 'securerandom'
|
|
5
|
-
|
|
6
|
-
BasicAuthExampleClient = Struct.new(
|
|
7
|
-
:username,
|
|
8
|
-
:password
|
|
9
|
-
) do
|
|
10
|
-
include ClientApiBuilder::Router
|
|
11
|
-
include ClientApiBuilder::Section
|
|
12
|
-
|
|
13
|
-
base_url 'https://www.example.com'
|
|
14
|
-
|
|
15
|
-
configure_retries(2)
|
|
16
|
-
|
|
17
|
-
header 'Authorization', :basic_authorization
|
|
18
|
-
query_param('cache_buster') { (Time.now.to_f * 1000).to_i }
|
|
19
|
-
|
|
20
|
-
route :get_apps, '/apps'
|
|
21
|
-
route :get_app, '/apps/:app_id'
|
|
22
|
-
|
|
23
|
-
section :users do
|
|
24
|
-
header 'Authorization', :bearer_authorization
|
|
25
|
-
|
|
26
|
-
route :create_user, '/users?z={cache_buster}'
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def cache_buster
|
|
30
|
-
(Time.now.to_f * 1000).to_i
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def auth_token
|
|
36
|
-
@auth_token ||= SecureRandom.uuid
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def basic_authorization
|
|
40
|
-
'basic ' + Base64.strict_encode64(username + ':' + password)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def bearer_authorization
|
|
44
|
-
'bearer ' + auth_token
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class IMDBDatesetsClient
|
|
4
|
-
include ClientApiBuilder::Router
|
|
5
|
-
|
|
6
|
-
base_url 'https://datasets.imdbws.com'
|
|
7
|
-
|
|
8
|
-
route :get_name_basics, '/name.basics.tsv.gz', stream: :file
|
|
9
|
-
route :get_title_akas, '/title.akas.tsv.gz', stream: :io
|
|
10
|
-
route :get_title_basics, '/title.basics.tsv.gz', stream: :block
|
|
11
|
-
|
|
12
|
-
def self.stream_to_file
|
|
13
|
-
new.get_name_basics(file: 'name.basics.tsv.gz')
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def self.stream_to_io
|
|
17
|
-
File.open('title.akas.tsv.gz', 'wb') do |io|
|
|
18
|
-
new.get_title_akas(io: io)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def self.stream_with_block
|
|
23
|
-
File.open('title.basics.tsv.gz', 'wb') do |io|
|
|
24
|
-
total_read = 0.0
|
|
25
|
-
new.get_title_basics do |response, chunk|
|
|
26
|
-
total_read += chunk.bytesize
|
|
27
|
-
percentage_complete = ((total_read / response.content_length) * 100).to_i
|
|
28
|
-
puts "downloading title.basics.tsv.gz completed: #{percentage_complete}%"
|
|
29
|
-
io.write chunk
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class LoremIpsumClient
|
|
4
|
-
include ClientApiBuilder::Router
|
|
5
|
-
|
|
6
|
-
# by default it converts the body data to JSON
|
|
7
|
-
# to convert the body to query params (x=1&y=2) use the following
|
|
8
|
-
# if using active support change this to :to_query
|
|
9
|
-
body_builder :query_params
|
|
10
|
-
|
|
11
|
-
base_url 'https://www.lipsum.com'
|
|
12
|
-
|
|
13
|
-
header 'Content-Type', 'application/x-www-form-urlencoded'
|
|
14
|
-
header 'Accept', 'application/json'
|
|
15
|
-
|
|
16
|
-
# this creates a method called create_lorem_ipsum with 2 named arguments amont and what
|
|
17
|
-
route :create_lorem_ipsum, '/feed/json', body: { amount: :amount, what: :what, start: 'yes', generate: 'Generate Lorem Ipsum' }
|
|
18
|
-
end
|
data/script/console
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
$LOAD_PATH << File.expand_path('../lib', __dir__)
|
|
5
|
-
$LOAD_PATH << File.expand_path('../examples', __dir__)
|
|
6
|
-
require 'client-api-builder'
|
|
7
|
-
autoload :BasicAuthExampleClient, 'basic_auth_example_client'
|
|
8
|
-
autoload :IMDBDatesetsClient, 'imdb_datasets_client'
|
|
9
|
-
autoload :LoremIpsumClient, 'lorem_ipsum_client'
|
|
10
|
-
require 'logger'
|
|
11
|
-
LOG = Logger.new($stdout)
|
|
12
|
-
ClientApiBuilder.logger = LOG
|
|
13
|
-
ClientApiBuilder::ActiveSupportLogSubscriber.new(LOG).subscribe!
|
|
14
|
-
require 'irb'
|
|
15
|
-
IRB.start(__FILE__)
|