jsonrpc-middleware 0.5.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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.aiignore +6 -1
  3. data/.claude/agents/entire-search.md +25 -0
  4. data/.claude/agents/rbs-specialist.md +89 -0
  5. data/.claude/settings.json +84 -0
  6. data/.devcontainer/devcontainer.json +17 -0
  7. data/.dockerignore +16 -0
  8. data/.entire/.gitignore +5 -0
  9. data/.entire/settings.json +4 -0
  10. data/.rubocop.yml +26 -1
  11. data/.tool-versions +1 -1
  12. data/.yard-lint.yml +283 -0
  13. data/AGENTS.md +142 -0
  14. data/CHANGELOG.md +43 -0
  15. data/CLAUDE.md +2 -113
  16. data/Dockerfile +144 -0
  17. data/README.md +10 -17
  18. data/Rakefile +78 -26
  19. data/examples/README.md +9 -0
  20. data/examples/procedures.rb +3 -1
  21. data/examples/rack/Gemfile.lock +11 -2
  22. data/examples/rack-echo/Gemfile.lock +11 -2
  23. data/examples/rails/Gemfile.lock +18 -23
  24. data/examples/rails/config/initializers/jsonrpc.rb +1 -1
  25. data/examples/rails-routing-dsl/config.ru +5 -5
  26. data/examples/rails-single-file/config.ru +1 -1
  27. data/examples/rails-single-file-routing/README.md +38 -3
  28. data/examples/rails-single-file-routing/config.ru +18 -1
  29. data/examples/sinatra-classic/Gemfile.lock +11 -3
  30. data/examples/sinatra-modular/Gemfile.lock +11 -3
  31. data/lib/jsonrpc/batch_request.rb +9 -12
  32. data/lib/jsonrpc/batch_response.rb +7 -9
  33. data/lib/jsonrpc/configuration.rb +43 -4
  34. data/lib/jsonrpc/error.rb +8 -9
  35. data/lib/jsonrpc/errors/internal_error.rb +2 -0
  36. data/lib/jsonrpc/errors/invalid_params_error.rb +2 -0
  37. data/lib/jsonrpc/errors/invalid_request_error.rb +2 -0
  38. data/lib/jsonrpc/errors/method_not_found_error.rb +2 -0
  39. data/lib/jsonrpc/errors/parse_error.rb +2 -0
  40. data/lib/jsonrpc/helpers.rb +6 -0
  41. data/lib/jsonrpc/middleware.rb +15 -13
  42. data/lib/jsonrpc/notification.rb +8 -9
  43. data/lib/jsonrpc/parser.rb +22 -19
  44. data/lib/jsonrpc/railtie/batch_constraint.rb +1 -0
  45. data/lib/jsonrpc/railtie/mapper_extension.rb +2 -2
  46. data/lib/jsonrpc/railtie/method_constraint.rb +9 -0
  47. data/lib/jsonrpc/railtie/routes_dsl.rb +10 -15
  48. data/lib/jsonrpc/railtie.rb +4 -2
  49. data/lib/jsonrpc/request.rb +12 -84
  50. data/lib/jsonrpc/response.rb +11 -60
  51. data/lib/jsonrpc/types.rb +13 -0
  52. data/lib/jsonrpc/validator.rb +14 -4
  53. data/lib/jsonrpc/version.rb +1 -1
  54. data/lib/jsonrpc.rb +5 -0
  55. data/rbs_collection.lock.yaml +476 -0
  56. data/rbs_collection.yaml +21 -0
  57. data/sig/jsonrpc/batch_request.rbs +17 -0
  58. data/sig/jsonrpc/batch_response.rbs +17 -0
  59. data/sig/jsonrpc/configuration.rbs +18 -0
  60. data/sig/jsonrpc/error.rbs +17 -0
  61. data/sig/jsonrpc/errors/internal_error.rbs +5 -0
  62. data/sig/jsonrpc/errors/invalid_params_error.rbs +5 -0
  63. data/sig/jsonrpc/errors/invalid_request_error.rbs +5 -0
  64. data/sig/jsonrpc/errors/method_not_found_error.rbs +5 -0
  65. data/sig/jsonrpc/errors/parse_error.rbs +5 -0
  66. data/sig/jsonrpc/middleware.rbs +20 -3
  67. data/sig/jsonrpc/notification.rbs +15 -0
  68. data/sig/jsonrpc/parser.rbs +7 -1
  69. data/sig/jsonrpc/request.rbs +18 -0
  70. data/sig/jsonrpc/response.rbs +19 -0
  71. data/sig/jsonrpc/validator.rbs +8 -0
  72. data/sig/jsonrpc.rbs +3 -156
  73. data/sig/multi_json.rbs +17 -0
  74. data/sig/type_definitions.rbs +11 -0
  75. data/sig/zeitwerk.rbs +10 -0
  76. metadata +61 -9
  77. data/.claude/commands/document.md +0 -105
  78. data/.claude/commands/test.md +0 -561
  79. data/.claude/docs/yard.md +0 -602
  80. data/.claude/settings.local.json +0 -11
  81. 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,47 @@ 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
+
26
+ ## [0.6.0] - 2025-09-17
27
+
28
+ ### Added
29
+ - MultiJson support for improved JSON performance and flexibility
30
+ - Configuration option to select JSON adapter (`JSONRPC.configuration.json_adapter`)
31
+ - Enhanced error handling with adapter and input preview details
32
+ - Better performance through optimized JSON parsing
33
+ - Enhanced examples
34
+ - Batch JSON-RPC request handling in Rails single-file routing example
35
+ - Smart home control API example using Rails routing DSL
36
+ - Updated documentation with batch request usage examples
37
+
38
+ ### Changed
39
+ - Replaced JSON gem with MultiJson throughout the codebase
40
+ - All JSON parsing now uses MultiJson for better adapter support
41
+ - Updated documentation to mention optimized JSON handling
42
+ - Refactored Request and Response classes to use dry-struct
43
+ - Simplified class definitions with automatic type checking
44
+ - Reduced code complexity while maintaining functionality
45
+ - Enhanced Rails integration
46
+ - Improved example applications with better error handling
47
+ - Updated Gemfile.lock files across all examples
48
+
8
49
  ## [0.5.0] - 2025-07-22
9
50
 
10
51
  ### Added
@@ -135,6 +176,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
135
176
  - Helper methods for request and response processing
136
177
  - Examples for basic and advanced usage scenarios
137
178
 
179
+ [0.7.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.6.0...v0.7.0
180
+ [0.6.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.5.0...v0.6.0
138
181
  [0.5.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.4.0...v0.5.0
139
182
  [0.4.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.3.0...v0.4.0
140
183
  [0.3.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.2.0...v0.3.0
data/CLAUDE.md CHANGED
@@ -1,114 +1,3 @@
1
- # CLAUDE.md
1
+ # Claude Code Instructions
2
2
 
3
- This file provides guidance to Claude Code (claude.ai/code) 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
- - `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
@@ -41,24 +41,19 @@ A Rack middleware implementing the JSON-RPC 2.0 protocol that integrates easily
41
41
  - **Error handling**: Comprehensive error handling with standard JSON-RPC error responses
42
42
  - **Request validation**: Define request parameter specifications and validations
43
43
  - **Helpers**: Convenient helper methods to simplify request and response processing
44
+ - **Optimized JSON handling**: Uses MultiJSON to automatically select the fastest available JSON library
44
45
 
45
46
  ## 🏗️ Architecture
46
47
 
47
48
  The gem integrates seamlessly into your Rack-based application:
48
49
 
49
- ```mermaid
50
- block-beta
51
- columns 4
52
-
53
- App["Your app"]:4
54
- Rails:1 Sinatra:1 RackApp["Other Rack-compatible framework"]:2
55
- Middleware["JSON-RPC Middleware"]:4
56
- Rack["Rack"]:4
57
- HTTP["HTTP"]:4
58
-
59
- classDef middlewareStyle fill:#ff6b6b,stroke:#d63031,stroke-width:2px,color:#fff
60
- class Middleware middlewareStyle
61
- ```
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>
62
57
 
63
58
  ## 📦 Installation
64
59
 
@@ -96,7 +91,7 @@ JSONRPC.configure do
96
91
  end
97
92
 
98
93
  rule(:addends) do
99
- key.failure('must contain at least one addend') if value.empty?
94
+ key.failure('must contain at least two addends') if value.size < 2
100
95
  end
101
96
  end
102
97
  end
@@ -184,11 +179,9 @@ rake rubocop # Run RuboCop
184
179
  rake rubocop:autocorrect # Autocorrect RuboCop offenses (only when it's safe)
185
180
  rake rubocop:autocorrect_all # Autocorrect RuboCop offenses (safe and unsafe)
186
181
  rake spec # Run RSpec code examples
187
- rake verify_measurements # Verify that yardstick coverage is at least 100%
188
182
  rake yard # Generate YARD Documentation
189
183
  rake yard:format # Format YARD documentation
190
- rake yard:junk # Check the junk in your YARD Documentation
191
- rake yardstick_measure # Measure docs in lib/**/*.rb with yardstick
184
+ rake yard:lint # Lint YARD documentation
192
185
  ```
193
186
 
194
187
  ### 🧪 Type checking