jsonrpc-middleware 0.6.0 → 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/.aiignore +6 -1
- data/.claude/agents/entire-search.md +25 -0
- data/.claude/agents/rbs-specialist.md +89 -0
- data/.claude/settings.json +84 -0
- data/.devcontainer/devcontainer.json +17 -0
- data/.dockerignore +16 -0
- data/.entire/.gitignore +5 -0
- data/.entire/settings.json +4 -0
- data/.rubocop.yml +26 -1
- data/.tool-versions +1 -1
- data/.yard-lint.yml +283 -0
- data/AGENTS.md +142 -0
- data/CHANGELOG.md +19 -0
- data/CLAUDE.md +2 -113
- data/Dockerfile +144 -0
- data/README.md +9 -17
- data/Rakefile +62 -11
- data/examples/procedures.rb +3 -1
- data/examples/rack/Gemfile.lock +4 -4
- data/examples/rack-echo/Gemfile.lock +4 -4
- data/examples/rails/Gemfile.lock +12 -5
- data/examples/rails/config/initializers/jsonrpc.rb +1 -1
- data/examples/rails-routing-dsl/config.ru +5 -5
- data/examples/rails-single-file/config.ru +1 -1
- data/examples/rails-single-file-routing/config.ru +1 -1
- data/examples/sinatra-classic/Gemfile.lock +11 -4
- data/examples/sinatra-modular/Gemfile.lock +11 -4
- data/lib/jsonrpc/batch_request.rb +8 -11
- data/lib/jsonrpc/batch_response.rb +6 -8
- data/lib/jsonrpc/configuration.rb +30 -4
- data/lib/jsonrpc/error.rb +7 -8
- data/lib/jsonrpc/errors/internal_error.rb +2 -0
- data/lib/jsonrpc/errors/invalid_params_error.rb +2 -0
- data/lib/jsonrpc/errors/invalid_request_error.rb +2 -0
- data/lib/jsonrpc/errors/method_not_found_error.rb +2 -0
- data/lib/jsonrpc/errors/parse_error.rb +2 -0
- data/lib/jsonrpc/helpers.rb +6 -0
- data/lib/jsonrpc/middleware.rb +12 -11
- data/lib/jsonrpc/notification.rb +7 -8
- data/lib/jsonrpc/parser.rb +13 -12
- data/lib/jsonrpc/railtie/batch_constraint.rb +1 -0
- data/lib/jsonrpc/railtie/mapper_extension.rb +2 -2
- data/lib/jsonrpc/railtie/method_constraint.rb +9 -0
- data/lib/jsonrpc/railtie/routes_dsl.rb +10 -15
- data/lib/jsonrpc/railtie.rb +2 -0
- data/lib/jsonrpc/response.rb +2 -2
- data/lib/jsonrpc/types.rb +1 -1
- data/lib/jsonrpc/validator.rb +14 -4
- data/lib/jsonrpc/version.rb +1 -1
- data/lib/jsonrpc.rb +3 -0
- data/rbs_collection.lock.yaml +476 -0
- data/rbs_collection.yaml +21 -0
- data/sig/jsonrpc/batch_request.rbs +17 -0
- data/sig/jsonrpc/batch_response.rbs +17 -0
- data/sig/jsonrpc/configuration.rbs +18 -0
- data/sig/jsonrpc/error.rbs +17 -0
- data/sig/jsonrpc/errors/internal_error.rbs +5 -0
- data/sig/jsonrpc/errors/invalid_params_error.rbs +5 -0
- data/sig/jsonrpc/errors/invalid_request_error.rbs +5 -0
- data/sig/jsonrpc/errors/method_not_found_error.rbs +5 -0
- data/sig/jsonrpc/errors/parse_error.rbs +5 -0
- data/sig/jsonrpc/middleware.rbs +20 -3
- data/sig/jsonrpc/notification.rbs +15 -0
- data/sig/jsonrpc/parser.rbs +7 -1
- data/sig/jsonrpc/request.rbs +18 -0
- data/sig/jsonrpc/response.rbs +19 -0
- data/sig/jsonrpc/validator.rbs +8 -0
- data/sig/jsonrpc.rbs +3 -156
- data/sig/multi_json.rbs +17 -0
- data/sig/type_definitions.rbs +11 -0
- data/sig/zeitwerk.rbs +10 -0
- metadata +34 -12
- data/.claude/commands/document.md +0 -105
- data/.claude/commands/gemfile/update.md +0 -52
- data/.claude/commands/test.md +0 -561
- data/.claude/docs/yard.md +0 -602
- data/.claude/settings.local.json +0 -15
- data/.yardstick.yml +0 -22
data/AGENTS.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to AI agents when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Development Commands
|
|
6
|
+
|
|
7
|
+
### Testing
|
|
8
|
+
- `bundle exec rspec` - Run all tests
|
|
9
|
+
- `bundle exec rspec spec/path/to/spec.rb` - Run specific test file
|
|
10
|
+
- `bundle exec rake coverage` - Run tests with test coverage and write a LLM-friendly report in `coverage/report.md`
|
|
11
|
+
|
|
12
|
+
### Code Quality
|
|
13
|
+
- `bundle exec rake qa` - Complete quality check (tests, linting, security, docs)
|
|
14
|
+
- `bundle exec rubocop` - Run Ruby linter
|
|
15
|
+
- `bundle exec rubocop --autocorrect` - Auto-fix safe Ruby style issues
|
|
16
|
+
- `bundle exec rubocop --autocorrect-all` - Auto-fix all Ruby style issues
|
|
17
|
+
- `bundle exec steep check` - Run type checking with Steep/RBS
|
|
18
|
+
|
|
19
|
+
### Security & Dependencies
|
|
20
|
+
- `bundle exec rake bundle:audit:check` - Check for vulnerable dependencies
|
|
21
|
+
- `bundle exec rake bundle:audit:update` - Update vulnerability database
|
|
22
|
+
|
|
23
|
+
### Documentation
|
|
24
|
+
- `bundle exec rake yard` - Generate YARD documentation
|
|
25
|
+
- `bundle exec rake yard:format` - Format YARD comments in code
|
|
26
|
+
- `bundle exec rake yard:lint` - Lint YARD comments for issues
|
|
27
|
+
|
|
28
|
+
### Build & Release
|
|
29
|
+
- `bundle exec rake build` - Build gem package
|
|
30
|
+
- `bundle exec rake install` - Install gem locally
|
|
31
|
+
- `bin/console` - Interactive console with gem loaded
|
|
32
|
+
|
|
33
|
+
### Containerized development (cloud agents)
|
|
34
|
+
|
|
35
|
+
A ready-to-develop container is defined by `Dockerfile` and `.devcontainer/devcontainer.json`
|
|
36
|
+
(Ruby 4.0.5 + the full dev toolchain). Cloud agent platforms / Codespaces consume the
|
|
37
|
+
devcontainer directly. Locally:
|
|
38
|
+
|
|
39
|
+
- `docker build -t jsonrpc-middleware-dev .`
|
|
40
|
+
- `docker run --rm -it -v "$PWD":/app jsonrpc-middleware-dev bash`
|
|
41
|
+
- Inside the container: `bundle exec rake qa`, `bundle exec rspec`, `bundle exec steep check`.
|
|
42
|
+
|
|
43
|
+
To run an example app on Linux, first add Linux platforms to that example's lockfile:
|
|
44
|
+
`cd examples/<name> && bundle lock --add-platform x86_64-linux aarch64-linux && bundle install`.
|
|
45
|
+
|
|
46
|
+
## Architecture Overview
|
|
47
|
+
|
|
48
|
+
### Core Components
|
|
49
|
+
|
|
50
|
+
**JSONRPC Module** (`lib/jsonrpc.rb`)
|
|
51
|
+
- Main entry point with configuration DSL
|
|
52
|
+
- Uses Zeitwerk for autoloading with specific inflection rules
|
|
53
|
+
- Singleton Configuration pattern for procedure definitions
|
|
54
|
+
|
|
55
|
+
**Middleware** (`lib/jsonrpc/middleware.rb`)
|
|
56
|
+
- Rack middleware implementing JSON-RPC 2.0 specification
|
|
57
|
+
- Handles parsing, validation, and error responses
|
|
58
|
+
- Supports single requests, notifications, and batch operations
|
|
59
|
+
- Complex batch processing with mixed error/success handling
|
|
60
|
+
|
|
61
|
+
**Parser** (`lib/jsonrpc/parser.rb`)
|
|
62
|
+
- Converts JSON strings into Request/Notification/BatchRequest objects
|
|
63
|
+
- Handles malformed JSON and invalid request structures
|
|
64
|
+
|
|
65
|
+
**Validator** (`lib/jsonrpc/validator.rb`)
|
|
66
|
+
- Validates requests against procedure definitions using dry-validation
|
|
67
|
+
- Supports both positional and named parameters
|
|
68
|
+
- Handles method existence and parameter validation
|
|
69
|
+
|
|
70
|
+
**Helpers** (`lib/jsonrpc/helpers.rb`)
|
|
71
|
+
- Framework-agnostic helper methods for apps
|
|
72
|
+
- Provides convenience methods for checking request types
|
|
73
|
+
- Response generation helpers for different scenarios
|
|
74
|
+
|
|
75
|
+
### Request/Response Objects
|
|
76
|
+
- `Request` - Standard JSON-RPC request with id
|
|
77
|
+
- `Notification` - Request without id (no response expected)
|
|
78
|
+
- `BatchRequest` - Collection of requests/notifications
|
|
79
|
+
- `Response` - JSON-RPC response with result or error
|
|
80
|
+
- `BatchResponse` - Collection of responses
|
|
81
|
+
|
|
82
|
+
### Error System
|
|
83
|
+
Structured error hierarchy in `lib/jsonrpc/errors/`:
|
|
84
|
+
- `ParseError` (-32700) - Invalid JSON
|
|
85
|
+
- `InvalidRequestError` (-32600) - Invalid request object
|
|
86
|
+
- `MethodNotFoundError` (-32601) - Method not found
|
|
87
|
+
- `InvalidParamsError` (-32602) - Invalid parameters
|
|
88
|
+
- `InternalError` (-32603) - Server error
|
|
89
|
+
|
|
90
|
+
### Configuration DSL
|
|
91
|
+
```ruby
|
|
92
|
+
JSONRPC.configure do
|
|
93
|
+
procedure('method_name') do
|
|
94
|
+
params do
|
|
95
|
+
required(:param).value(:type)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
rule(:param) do
|
|
99
|
+
# Custom validation rules
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Type System
|
|
106
|
+
|
|
107
|
+
The project uses RBS (Ruby Type Signatures) with Steep for type checking:
|
|
108
|
+
- Type definitions in `sig/` directory
|
|
109
|
+
- Run `bundle exec steep check` for type validation
|
|
110
|
+
- Generate types with `bundle exec typeprof FILENAME`
|
|
111
|
+
|
|
112
|
+
### Third-party RBS signatures (RBS collection)
|
|
113
|
+
|
|
114
|
+
Signatures for third-party gems are managed with the RBS collection:
|
|
115
|
+
- `rbs_collection.yaml` declares the requested gems (activesupport, dry-struct,
|
|
116
|
+
dry-validation, multi_json, zeitwerk) and the `ruby/gem_rbs_collection` source.
|
|
117
|
+
- `rbs_collection.lock.yaml` pins the resolved versions and revisions (committed).
|
|
118
|
+
- `bundle exec rbs collection install` downloads them into `.gem_rbs_collection/`
|
|
119
|
+
(gitignored). Run this after cloning, before `bundle exec steep check`.
|
|
120
|
+
- `bundle exec rbs collection update` re-resolves and refreshes the lock file when
|
|
121
|
+
adding a gem to `rbs_collection.yaml` or bumping sources.
|
|
122
|
+
|
|
123
|
+
Steep loads the collection automatically via the lock file. A few gems still have
|
|
124
|
+
hand-written stubs in `sig/` (e.g. `sig/zeitwerk.rbs`, `sig/multi_json.rbs`) where the
|
|
125
|
+
collection's coverage is incomplete for this project's usage.
|
|
126
|
+
|
|
127
|
+
## Examples
|
|
128
|
+
|
|
129
|
+
Comprehensive examples in `examples/` directory showing integration with:
|
|
130
|
+
- Pure Rack applications
|
|
131
|
+
- Sinatra (classic and modular)
|
|
132
|
+
- Single-file applications with bundler/inline
|
|
133
|
+
|
|
134
|
+
Each example implements calculator operations (add, subtract, multiply, divide) demonstrating different usage patterns.
|
|
135
|
+
|
|
136
|
+
## Testing Strategy
|
|
137
|
+
|
|
138
|
+
- RSpec with comprehensive test coverage
|
|
139
|
+
- Factory Bot for test data generation
|
|
140
|
+
- Rack::Test for middleware testing
|
|
141
|
+
- SimpleCov for coverage reporting with multiple formatters
|
|
142
|
+
- Examples include both unit and integration tests
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.0] - 2026-06-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Configurable `Logger` support for JSON-RPC components
|
|
12
|
+
- `JSONRPC.configuration.logger` (defaults to `Logger.new($stdout, progname: 'JSONRPC')`)
|
|
13
|
+
- `JSONRPC::Middleware` accepts a `:logger` option and uses it for internal-error and validation logging
|
|
14
|
+
- Development container support (`.devcontainer`, `Dockerfile`, `.dockerignore`)
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Updated Ruby to v4.0.5
|
|
18
|
+
- Updated Bundler to v4.0.8
|
|
19
|
+
- Updated runtime dependencies
|
|
20
|
+
- `multi_json` to `~> 1.21` (removed the local `JSON::Ext::Generator::State#except` workaround now that the upstream fix has shipped)
|
|
21
|
+
- `zeitwerk` to `~> 2.8`
|
|
22
|
+
- Replaced the README architecture diagram (Mermaid) with an inline SVG
|
|
23
|
+
- Replaced yardstick/yard-junk with yard-lint for documentation linting
|
|
24
|
+
- Achieved 100% test coverage
|
|
25
|
+
|
|
8
26
|
## [0.6.0] - 2025-09-17
|
|
9
27
|
|
|
10
28
|
### Added
|
|
@@ -158,6 +176,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
158
176
|
- Helper methods for request and response processing
|
|
159
177
|
- Examples for basic and advanced usage scenarios
|
|
160
178
|
|
|
179
|
+
[0.7.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.6.0...v0.7.0
|
|
161
180
|
[0.6.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.5.0...v0.6.0
|
|
162
181
|
[0.5.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.4.0...v0.5.0
|
|
163
182
|
[0.4.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.3.0...v0.4.0
|
data/CLAUDE.md
CHANGED
|
@@ -1,114 +1,3 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Claude Code Instructions
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Development Commands
|
|
6
|
-
|
|
7
|
-
### Testing
|
|
8
|
-
- `bundle exec rspec` - Run all tests
|
|
9
|
-
- `bundle exec rspec spec/path/to/spec.rb` - Run specific test file
|
|
10
|
-
- `rake coverage` - Run tests with coverage report (opens in browser)
|
|
11
|
-
|
|
12
|
-
### Code Quality
|
|
13
|
-
- `bundle exec rake qa` - Complete quality check (tests, linting, security, docs)
|
|
14
|
-
- `bundle exec rubocop` - Run Ruby linter
|
|
15
|
-
- `bundle exec rubocop --autocorrect` - Auto-fix safe Ruby style issues
|
|
16
|
-
- `bundle exec rubocop --autocorrect-all` - Auto-fix all Ruby style issues
|
|
17
|
-
- `bundle exec steep check` - Run type checking with Steep/RBS
|
|
18
|
-
|
|
19
|
-
### Security & Dependencies
|
|
20
|
-
- `bundle exec rake bundle:audit:check` - Check for vulnerable dependencies
|
|
21
|
-
- `bundle exec rake bundle:audit:update` - Update vulnerability database
|
|
22
|
-
|
|
23
|
-
### Documentation
|
|
24
|
-
- `bundle exec rake yard` - Generate YARD documentation
|
|
25
|
-
- `bundle exec rake yard:format` - Format YARD comments in code
|
|
26
|
-
- `bundle exec rake verify_measurements` - Verify 100% documentation coverage
|
|
27
|
-
|
|
28
|
-
### Build & Release
|
|
29
|
-
- `bundle exec rake build` - Build gem package
|
|
30
|
-
- `bundle exec rake install` - Install gem locally
|
|
31
|
-
- `bin/console` - Interactive console with gem loaded
|
|
32
|
-
|
|
33
|
-
## Architecture Overview
|
|
34
|
-
|
|
35
|
-
### Core Components
|
|
36
|
-
|
|
37
|
-
**JSONRPC Module** (`lib/jsonrpc.rb`)
|
|
38
|
-
- Main entry point with configuration DSL
|
|
39
|
-
- Uses Zeitwerk for autoloading with specific inflection rules
|
|
40
|
-
- Singleton Configuration pattern for procedure definitions
|
|
41
|
-
|
|
42
|
-
**Middleware** (`lib/jsonrpc/middleware.rb`)
|
|
43
|
-
- Rack middleware implementing JSON-RPC 2.0 specification
|
|
44
|
-
- Handles parsing, validation, and error responses
|
|
45
|
-
- Supports single requests, notifications, and batch operations
|
|
46
|
-
- Complex batch processing with mixed error/success handling
|
|
47
|
-
|
|
48
|
-
**Parser** (`lib/jsonrpc/parser.rb`)
|
|
49
|
-
- Converts JSON strings into Request/Notification/BatchRequest objects
|
|
50
|
-
- Handles malformed JSON and invalid request structures
|
|
51
|
-
|
|
52
|
-
**Validator** (`lib/jsonrpc/validator.rb`)
|
|
53
|
-
- Validates requests against procedure definitions using dry-validation
|
|
54
|
-
- Supports both positional and named parameters
|
|
55
|
-
- Handles method existence and parameter validation
|
|
56
|
-
|
|
57
|
-
**Helpers** (`lib/jsonrpc/helpers.rb`)
|
|
58
|
-
- Framework-agnostic helper methods for apps
|
|
59
|
-
- Provides convenience methods for checking request types
|
|
60
|
-
- Response generation helpers for different scenarios
|
|
61
|
-
|
|
62
|
-
### Request/Response Objects
|
|
63
|
-
- `Request` - Standard JSON-RPC request with id
|
|
64
|
-
- `Notification` - Request without id (no response expected)
|
|
65
|
-
- `BatchRequest` - Collection of requests/notifications
|
|
66
|
-
- `Response` - JSON-RPC response with result or error
|
|
67
|
-
- `BatchResponse` - Collection of responses
|
|
68
|
-
|
|
69
|
-
### Error System
|
|
70
|
-
Structured error hierarchy in `lib/jsonrpc/errors/`:
|
|
71
|
-
- `ParseError` (-32700) - Invalid JSON
|
|
72
|
-
- `InvalidRequestError` (-32600) - Invalid request object
|
|
73
|
-
- `MethodNotFoundError` (-32601) - Method not found
|
|
74
|
-
- `InvalidParamsError` (-32602) - Invalid parameters
|
|
75
|
-
- `InternalError` (-32603) - Server error
|
|
76
|
-
|
|
77
|
-
### Configuration DSL
|
|
78
|
-
```ruby
|
|
79
|
-
JSONRPC.configure do
|
|
80
|
-
procedure('method_name') do
|
|
81
|
-
params do
|
|
82
|
-
required(:param).value(:type)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
rule(:param) do
|
|
86
|
-
# Custom validation rules
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Type System
|
|
93
|
-
|
|
94
|
-
The project uses RBS (Ruby Type Signatures) with Steep for type checking:
|
|
95
|
-
- Type definitions in `sig/` directory
|
|
96
|
-
- Run `bundle exec steep check` for type validation
|
|
97
|
-
- Generate types with `bundle exec typeprof FILENAME`
|
|
98
|
-
|
|
99
|
-
## Examples
|
|
100
|
-
|
|
101
|
-
Comprehensive examples in `examples/` directory showing integration with:
|
|
102
|
-
- Pure Rack applications
|
|
103
|
-
- Sinatra (classic and modular)
|
|
104
|
-
- Single-file applications with bundler/inline
|
|
105
|
-
|
|
106
|
-
Each example implements calculator operations (add, subtract, multiply, divide) demonstrating different usage patterns.
|
|
107
|
-
|
|
108
|
-
## Testing Strategy
|
|
109
|
-
|
|
110
|
-
- RSpec with comprehensive test coverage
|
|
111
|
-
- Factory Bot for test data generation
|
|
112
|
-
- Rack::Test for middleware testing
|
|
113
|
-
- SimpleCov for coverage reporting with multiple formatters
|
|
114
|
-
- Examples include both unit and integration tests
|
|
3
|
+
See [AGENTS.md](./AGENTS.md) for all contribution guidelines.
|
data/Dockerfile
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
#
|
|
3
|
+
# Development image for the jsonrpc-middleware gem.
|
|
4
|
+
# ============================================================================
|
|
5
|
+
# This image is a *development environment* for AI agents (and humans) who work
|
|
6
|
+
# ON the gem — not a *production runtime* that ships the gem to end users.
|
|
7
|
+
#
|
|
8
|
+
# That single fact inverts most of the usual Docker advice:
|
|
9
|
+
# - We do NOT use a multi-stage build or a distroless final stage. The whole
|
|
10
|
+
# point is to keep the full toolchain available — compilers, git, and every
|
|
11
|
+
# development gem (RSpec, RuboCop, Steep, YARD, bundler-audit, Guard, ...) —
|
|
12
|
+
# so an agent can run the test/lint/type-check/audit suite and rebuild or
|
|
13
|
+
# install native gems on demand. Slimming those away would defeat the image.
|
|
14
|
+
# - Optimising for a tiny final image is therefore a non-goal; optimising for
|
|
15
|
+
# "everything an agent needs is already here and reproducible" is the goal.
|
|
16
|
+
# ============================================================================
|
|
17
|
+
|
|
18
|
+
# Why this exact base:
|
|
19
|
+
# - ruby:4.0.5 -> `.tool-versions` pins Ruby 4.0.5 as the repo's canonical
|
|
20
|
+
# version (CI runs on it too). The gemspec only requires `>= 3.4.0`, but the
|
|
21
|
+
# container must match the pinned version to faithfully reproduce CI results
|
|
22
|
+
# — otherwise an agent could see green/red locally that disagrees with CI.
|
|
23
|
+
# - -slim -> Debian-slim: small, but still glibc + `apt`. We deliberately
|
|
24
|
+
# avoid Alpine here: Alpine's musl libc forces many native gems to recompile
|
|
25
|
+
# from source (slower builds) and occasionally behaves differently from the
|
|
26
|
+
# glibc CI environment. Debian-slim keeps us byte-for-byte closer to CI while
|
|
27
|
+
# letting us add only the few build packages we actually need.
|
|
28
|
+
FROM ruby:4.0.5-slim
|
|
29
|
+
|
|
30
|
+
# System packages. Each line below earns its place — nothing here is "just in
|
|
31
|
+
# case", because every package widens the image and the attack surface:
|
|
32
|
+
# build-essential, pkg-config Compile native gem extensions when a gem has no
|
|
33
|
+
# precompiled platform build (or an agent adds one
|
|
34
|
+
# later). Most deps resolve to precompiled gems,
|
|
35
|
+
# but the toolchain must be present so `bundle
|
|
36
|
+
# install` never dead-ends.
|
|
37
|
+
# git Required for THREE distinct reasons:
|
|
38
|
+
# 1. `yard-lint` is sourced from GitHub in the
|
|
39
|
+
# Gemfile (`github: 'mensfeld/yard-lint'`),
|
|
40
|
+
# so bundler git-clones it during install.
|
|
41
|
+
# 2. `overcommit --install` (run by bin/setup in
|
|
42
|
+
# the devcontainer) writes git hooks.
|
|
43
|
+
# 3. The gemspec calls `git ls-files` to build
|
|
44
|
+
# its file list.
|
|
45
|
+
# libyaml-dev Psych (Ruby's YAML) links against libyaml; tools
|
|
46
|
+
# and configs throughout the stack parse YAML.
|
|
47
|
+
# curl, ca-certificates TLS roots + a fetch tool for rubygems.org and
|
|
48
|
+
# the bundler-audit advisory database.
|
|
49
|
+
# `--no-install-recommends` skips suggested extras; deleting the apt lists in the
|
|
50
|
+
# same layer keeps this layer from carrying ~tens of MB of package indexes.
|
|
51
|
+
RUN apt-get update \
|
|
52
|
+
&& apt-get install -y --no-install-recommends \
|
|
53
|
+
build-essential \
|
|
54
|
+
ca-certificates \
|
|
55
|
+
curl \
|
|
56
|
+
git \
|
|
57
|
+
libyaml-dev \
|
|
58
|
+
pkg-config \
|
|
59
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
60
|
+
|
|
61
|
+
# Pin bundler to the version recorded in Gemfile.lock's `BUNDLED WITH` (4.0.8).
|
|
62
|
+
# Bundler is sensitive to this: a mismatched bundler can re-resolve or warn, which
|
|
63
|
+
# undermines the whole point of a locked, reproducible dependency set. Matching it
|
|
64
|
+
# keeps installs identical to what a contributor (and CI) get.
|
|
65
|
+
RUN gem install bundler -v 4.0.8
|
|
66
|
+
|
|
67
|
+
# Build/runtime environment:
|
|
68
|
+
# BUNDLE_JOBS=4 Install gems in parallel — meaningfully faster on multi-core
|
|
69
|
+
# cloud builders.
|
|
70
|
+
# BUNDLE_RETRY=3 Retry a gem fetch up to 3x; absorbs transient network blips
|
|
71
|
+
# that would otherwise fail an unattended cloud build.
|
|
72
|
+
# LANG=C.UTF-8 Slim images ship with no locale, so Ruby's default external
|
|
73
|
+
# encoding would be US-ASCII and choke on non-ASCII I/O. Force
|
|
74
|
+
# UTF-8 to avoid Encoding errors in tooling and specs.
|
|
75
|
+
ENV BUNDLE_JOBS=4 \
|
|
76
|
+
BUNDLE_RETRY=3 \
|
|
77
|
+
LANG=C.UTF-8
|
|
78
|
+
|
|
79
|
+
# Conventional, stable workdir. The devcontainer bind-mounts the live repo here
|
|
80
|
+
# (see .devcontainer/devcontainer.json -> workspaceFolder/workspaceMount).
|
|
81
|
+
WORKDIR /app
|
|
82
|
+
|
|
83
|
+
# Run as a non-root user (security: containers should not run as root). UID/GID
|
|
84
|
+
# 1000 is chosen on purpose — it's the first non-root user on most Linux hosts
|
|
85
|
+
# and CI runners, so files created in a bind-mounted workspace get sane, non-root
|
|
86
|
+
# ownership on the host instead of being owned by root.
|
|
87
|
+
RUN groupadd --gid 1000 dev \
|
|
88
|
+
&& useradd --uid 1000 --gid 1000 --create-home --shell /bin/bash dev
|
|
89
|
+
|
|
90
|
+
# --- Dependency layer --------------------------------------------------------
|
|
91
|
+
# Copy ONLY the dependency manifests before the source so this (expensive) layer
|
|
92
|
+
# is cached and re-runs only when dependencies change — not on every code edit.
|
|
93
|
+
#
|
|
94
|
+
# Why the gemspec AND lib/jsonrpc/version.rb are needed here: the Gemfile's
|
|
95
|
+
# `gemspec` directive evaluates jsonrpc-middleware.gemspec at install time, and
|
|
96
|
+
# that file `require_relative`s lib/jsonrpc/version.rb (for `spec.version`). Both
|
|
97
|
+
# must be present or `bundle install` fails before it ever reads the lock.
|
|
98
|
+
# (The gemspec also runs `git ls-files`; with .git excluded via .dockerignore it
|
|
99
|
+
# simply returns an empty file list — harmless, because the *dependencies* come
|
|
100
|
+
# from the `add_dependency` lines, not from `spec.files`.)
|
|
101
|
+
COPY Gemfile Gemfile.lock jsonrpc-middleware.gemspec ./
|
|
102
|
+
COPY lib/jsonrpc/version.rb lib/jsonrpc/version.rb
|
|
103
|
+
|
|
104
|
+
# Why add Linux platforms: the committed Gemfile.lock was generated on macOS and
|
|
105
|
+
# historically listed only `arm64-darwin`. Bundler refuses to install for a
|
|
106
|
+
# platform the lock doesn't name, so a Linux build would fail outright. Adding
|
|
107
|
+
# x86_64-linux + aarch64-linux makes it resolve on both Intel and ARM Linux
|
|
108
|
+
# (cloud runners and Docker on Apple silicon). These platforms are now also
|
|
109
|
+
# committed to the repo's lockfile, so in normal builds this command is a no-op
|
|
110
|
+
# — it stays here defensively so the image still builds from an older checkout.
|
|
111
|
+
#
|
|
112
|
+
# Gems install into the base image's GEM_HOME (/usr/local/bundle), which lives
|
|
113
|
+
# OUTSIDE /app. That is deliberate and important: the devcontainer bind-mounts
|
|
114
|
+
# the host repo over /app at runtime, which would shadow anything installed under
|
|
115
|
+
# /app — but gems in /usr/local/bundle survive the mount, so the container is
|
|
116
|
+
# ready to run the toolchain the instant it starts.
|
|
117
|
+
RUN bundle lock --add-platform x86_64-linux aarch64-linux \
|
|
118
|
+
&& bundle install
|
|
119
|
+
|
|
120
|
+
# --- Source layer ------------------------------------------------------------
|
|
121
|
+
# Bake a full copy of the repo so the image is usable standalone (e.g. plain
|
|
122
|
+
# `docker run ... bundle exec rspec`). When used as a devcontainer, the live
|
|
123
|
+
# bind-mount overlays this copy with the contributor's working tree.
|
|
124
|
+
COPY . .
|
|
125
|
+
|
|
126
|
+
# Hand ownership of both the workspace and the gem cache to the non-root user.
|
|
127
|
+
# Chowning /usr/local/bundle is essential, not cosmetic: gems were installed as
|
|
128
|
+
# root above, and without this the `dev` user could not install additional gems
|
|
129
|
+
# at runtime (e.g. `bundle exec rake examples:bundle_install`) without sudo.
|
|
130
|
+
RUN chown -R dev:dev /app /usr/local/bundle
|
|
131
|
+
USER dev
|
|
132
|
+
|
|
133
|
+
# Documentation only — EXPOSE does not publish ports. It records the ports the
|
|
134
|
+
# example apps use, so an agent that boots one to manually verify behaviour knows
|
|
135
|
+
# where to look: rack/sinatra examples listen on 9292, the Rails example on 3000.
|
|
136
|
+
EXPOSE 9292 3000
|
|
137
|
+
|
|
138
|
+
# Deliberately no HEALTHCHECK: this container runs dev commands, it does not serve
|
|
139
|
+
# traffic, so there is no endpoint or process to health-check.
|
|
140
|
+
|
|
141
|
+
# Default to an interactive shell for ad-hoc development. The devcontainer
|
|
142
|
+
# overrides this with its own keep-alive command, so this CMD only matters for
|
|
143
|
+
# direct `docker run` usage.
|
|
144
|
+
CMD ["bash"]
|
data/README.md
CHANGED
|
@@ -47,19 +47,13 @@ A Rack middleware implementing the JSON-RPC 2.0 protocol that integrates easily
|
|
|
47
47
|
|
|
48
48
|
The gem integrates seamlessly into your Rack-based application:
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Rack["Rack"]:4
|
|
58
|
-
HTTP["HTTP"]:4
|
|
59
|
-
|
|
60
|
-
classDef middlewareStyle fill:#ff6b6b,stroke:#d63031,stroke-width:2px,color:#fff
|
|
61
|
-
class Middleware middlewareStyle
|
|
62
|
-
```
|
|
50
|
+
<p align="center">
|
|
51
|
+
<picture>
|
|
52
|
+
<source media="(prefers-color-scheme: dark)" srcset="./.github/images/architecture-diagram-dark.svg">
|
|
53
|
+
<source media="(prefers-color-scheme: light)" srcset="./.github/images/architecture-diagram-light.svg">
|
|
54
|
+
<img alt="Architecture Diagram" src="./.github/images/architecture-diagram-light.svg" width="800" height="400" style="max-width: 100%;">
|
|
55
|
+
</picture>
|
|
56
|
+
</p>
|
|
63
57
|
|
|
64
58
|
## 📦 Installation
|
|
65
59
|
|
|
@@ -97,7 +91,7 @@ JSONRPC.configure do
|
|
|
97
91
|
end
|
|
98
92
|
|
|
99
93
|
rule(:addends) do
|
|
100
|
-
key.failure('must contain at least
|
|
94
|
+
key.failure('must contain at least two addends') if value.size < 2
|
|
101
95
|
end
|
|
102
96
|
end
|
|
103
97
|
end
|
|
@@ -185,11 +179,9 @@ rake rubocop # Run RuboCop
|
|
|
185
179
|
rake rubocop:autocorrect # Autocorrect RuboCop offenses (only when it's safe)
|
|
186
180
|
rake rubocop:autocorrect_all # Autocorrect RuboCop offenses (safe and unsafe)
|
|
187
181
|
rake spec # Run RSpec code examples
|
|
188
|
-
rake verify_measurements # Verify that yardstick coverage is at least 100%
|
|
189
182
|
rake yard # Generate YARD Documentation
|
|
190
183
|
rake yard:format # Format YARD documentation
|
|
191
|
-
rake yard:
|
|
192
|
-
rake yardstick_measure # Measure docs in lib/**/*.rb with yardstick
|
|
184
|
+
rake yard:lint # Lint YARD documentation
|
|
193
185
|
```
|
|
194
186
|
|
|
195
187
|
### 🧪 Type checking
|
data/Rakefile
CHANGED
|
@@ -6,25 +6,18 @@ require 'bundler/gem_tasks'
|
|
|
6
6
|
require 'rspec/core/rake_task'
|
|
7
7
|
require 'rubocop/rake_task'
|
|
8
8
|
require 'yaml'
|
|
9
|
+
require 'yard'
|
|
9
10
|
require 'yard/rake/yardoc_task'
|
|
10
|
-
require 'yard-junk/rake'
|
|
11
|
-
require 'yardstick/rake/measurement'
|
|
12
|
-
require 'yardstick/rake/verify'
|
|
13
|
-
|
|
14
|
-
yardstick_options = YAML.load_file('.yardstick.yml')
|
|
15
11
|
|
|
16
12
|
Bundler::Audit::Task.new
|
|
17
13
|
RSpec::Core::RakeTask.new(:spec)
|
|
18
14
|
RuboCop::RakeTask.new
|
|
19
15
|
YARD::Rake::YardocTask.new
|
|
20
|
-
YardJunk::Rake.define_task
|
|
21
|
-
Yardstick::Rake::Measurement.new(:yardstick_measure, yardstick_options)
|
|
22
|
-
Yardstick::Rake::Verify.new(:verify_measurements, yardstick_options)
|
|
23
16
|
|
|
24
17
|
task default: %i[spec rubocop]
|
|
25
18
|
|
|
26
19
|
# Remove the report on rake clobber
|
|
27
|
-
CLEAN.include('
|
|
20
|
+
CLEAN.include('doc', '.yardoc', 'tmp')
|
|
28
21
|
|
|
29
22
|
# Delete these files and folders when running rake clobber.
|
|
30
23
|
CLOBBER.include('coverage', '.rspec_status')
|
|
@@ -33,11 +26,10 @@ desc 'Run spec with coverage'
|
|
|
33
26
|
task :coverage do
|
|
34
27
|
ENV['COVERAGE'] = 'true'
|
|
35
28
|
Rake::Task['spec'].execute
|
|
36
|
-
`open coverage/index.html`
|
|
37
29
|
end
|
|
38
30
|
|
|
39
31
|
desc 'Test, lint and perform security and documentation audits'
|
|
40
|
-
task qa: %w[spec rubocop yard:
|
|
32
|
+
task qa: %w[spec rubocop yard:lint bundle:audit]
|
|
41
33
|
|
|
42
34
|
namespace :yard do
|
|
43
35
|
desc 'Format YARD documentation'
|
|
@@ -95,6 +87,11 @@ namespace :yard do
|
|
|
95
87
|
puts
|
|
96
88
|
puts 'Done!'
|
|
97
89
|
end
|
|
90
|
+
|
|
91
|
+
desc 'Lint YARD documentation'
|
|
92
|
+
task :lint do
|
|
93
|
+
system 'bundle exec yard-lint lib/'
|
|
94
|
+
end
|
|
98
95
|
end
|
|
99
96
|
|
|
100
97
|
namespace :examples do
|
|
@@ -151,4 +148,58 @@ namespace :examples do
|
|
|
151
148
|
exit 1
|
|
152
149
|
end
|
|
153
150
|
end
|
|
151
|
+
|
|
152
|
+
desc 'Run bundle update on all example folders'
|
|
153
|
+
task :bundle_update do
|
|
154
|
+
examples_dir = File.join(Dir.pwd, 'examples')
|
|
155
|
+
|
|
156
|
+
unless Dir.exist?(examples_dir)
|
|
157
|
+
puts 'Examples directory not found'
|
|
158
|
+
exit 1
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
example_folders = Dir.glob(File.join(examples_dir, '*')).select { |path| Dir.exist?(path) }
|
|
162
|
+
|
|
163
|
+
if example_folders.empty?
|
|
164
|
+
puts 'No example folders found'
|
|
165
|
+
return
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
puts "Found #{example_folders.length} example folders:"
|
|
169
|
+
example_folders.each { |folder| puts " - #{File.basename(folder)}" }
|
|
170
|
+
puts
|
|
171
|
+
|
|
172
|
+
failed_folders = []
|
|
173
|
+
|
|
174
|
+
example_folders.each do |folder|
|
|
175
|
+
gemfile_path = File.join(folder, 'Gemfile')
|
|
176
|
+
|
|
177
|
+
unless File.exist?(gemfile_path)
|
|
178
|
+
puts "Skipping #{File.basename(folder)} - no Gemfile found"
|
|
179
|
+
next
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
puts "Running bundle update in #{File.basename(folder)}..."
|
|
183
|
+
|
|
184
|
+
Dir.chdir(folder) do
|
|
185
|
+
system('bundle update --all')
|
|
186
|
+
|
|
187
|
+
if $CHILD_STATUS.success?
|
|
188
|
+
puts " ✓ Successfully updated gems in #{File.basename(folder)}"
|
|
189
|
+
else
|
|
190
|
+
failed_folders << File.basename(folder)
|
|
191
|
+
puts " ✗ Failed to bundle update in #{File.basename(folder)}"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
puts
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
if failed_folders.empty?
|
|
199
|
+
puts 'All example folders processed successfully!'
|
|
200
|
+
else
|
|
201
|
+
puts "Failed to process #{failed_folders.length} folders: #{failed_folders.join(", ")}"
|
|
202
|
+
exit 1
|
|
203
|
+
end
|
|
204
|
+
end
|
|
154
205
|
end
|
data/examples/procedures.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
JSONRPC.configure do |config|
|
|
4
|
+
# Logger instance (default: Logger.new($stdout, progname: 'JSONRPC'))
|
|
5
|
+
config.logger = Logger.new($stdout, progname: 'JSONRPC')
|
|
4
6
|
config.log_internal_errors = true # Log internal error backtraces (default: true)
|
|
5
7
|
config.log_request_validation_errors = true # Log JSON-RPC request validation errors (default: false)
|
|
6
8
|
config.render_internal_errors = true # Render detailed internal error information in responses (default: true)
|
|
@@ -14,7 +16,7 @@ JSONRPC.configure do |config|
|
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
rule(:addends) do
|
|
17
|
-
key.failure('must contain at least
|
|
19
|
+
key.failure('must contain at least two addends') if value.size < 2
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
|
data/examples/rack/Gemfile.lock
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ../..
|
|
3
3
|
specs:
|
|
4
|
-
jsonrpc-middleware (0.
|
|
4
|
+
jsonrpc-middleware (0.6.0)
|
|
5
5
|
dry-struct (~> 1.8)
|
|
6
6
|
dry-validation (~> 1.11)
|
|
7
|
-
multi_json (~> 1.
|
|
7
|
+
multi_json (~> 1.20)
|
|
8
8
|
zeitwerk (~> 2.7)
|
|
9
9
|
|
|
10
10
|
GEM
|
|
@@ -54,7 +54,7 @@ GEM
|
|
|
54
54
|
zeitwerk (~> 2.6)
|
|
55
55
|
ice_nine (0.11.2)
|
|
56
56
|
logger (1.7.0)
|
|
57
|
-
multi_json (1.
|
|
57
|
+
multi_json (1.21.1)
|
|
58
58
|
nio4r (2.7.4)
|
|
59
59
|
puma (6.6.0)
|
|
60
60
|
nio4r (~> 2.0)
|
|
@@ -74,4 +74,4 @@ DEPENDENCIES
|
|
|
74
74
|
rackup
|
|
75
75
|
|
|
76
76
|
BUNDLED WITH
|
|
77
|
-
|
|
77
|
+
4.0.8
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ../..
|
|
3
3
|
specs:
|
|
4
|
-
jsonrpc-middleware (0.
|
|
4
|
+
jsonrpc-middleware (0.6.0)
|
|
5
5
|
dry-struct (~> 1.8)
|
|
6
6
|
dry-validation (~> 1.11)
|
|
7
|
-
multi_json (~> 1.
|
|
7
|
+
multi_json (~> 1.20)
|
|
8
8
|
zeitwerk (~> 2.7)
|
|
9
9
|
|
|
10
10
|
GEM
|
|
@@ -54,7 +54,7 @@ GEM
|
|
|
54
54
|
zeitwerk (~> 2.6)
|
|
55
55
|
ice_nine (0.11.2)
|
|
56
56
|
logger (1.7.0)
|
|
57
|
-
multi_json (1.
|
|
57
|
+
multi_json (1.21.1)
|
|
58
58
|
nio4r (2.7.4)
|
|
59
59
|
puma (6.6.0)
|
|
60
60
|
nio4r (~> 2.0)
|
|
@@ -74,4 +74,4 @@ DEPENDENCIES
|
|
|
74
74
|
rackup
|
|
75
75
|
|
|
76
76
|
BUNDLED WITH
|
|
77
|
-
|
|
77
|
+
4.0.8
|