jsonrpc-middleware 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/commands/gemfile/update.md +52 -0
- data/.claude/settings.local.json +6 -2
- data/CHANGELOG.md +90 -1
- data/README.md +3 -0
- data/Rakefile +57 -0
- data/examples/README.md +9 -0
- data/examples/procedures.rb +1 -5
- data/examples/rack/Gemfile.lock +10 -1
- data/examples/rack-echo/Gemfile.lock +10 -1
- data/examples/rails/Gemfile.lock +10 -22
- data/examples/rails/config/initializers/jsonrpc.rb +1 -5
- data/examples/rails-routing-dsl/README.md +199 -0
- data/examples/rails-routing-dsl/config.ru +146 -0
- data/examples/rails-single-file-routing/README.md +39 -4
- data/examples/rails-single-file-routing/config.ru +18 -5
- data/examples/sinatra-classic/Gemfile.lock +3 -2
- data/examples/sinatra-modular/Gemfile.lock +3 -2
- data/lib/jsonrpc/batch_request.rb +1 -14
- data/lib/jsonrpc/batch_response.rb +1 -1
- data/lib/jsonrpc/configuration.rb +25 -3
- data/lib/jsonrpc/error.rb +1 -1
- data/lib/jsonrpc/middleware.rb +3 -2
- data/lib/jsonrpc/notification.rb +1 -1
- data/lib/jsonrpc/parser.rb +9 -7
- data/lib/jsonrpc/railtie/batch_constraint.rb +25 -0
- data/lib/jsonrpc/railtie/mapper_extension.rb +34 -0
- data/lib/jsonrpc/railtie/method_constraint.rb +1 -1
- data/lib/jsonrpc/railtie/routes_dsl.rb +147 -0
- data/lib/jsonrpc/railtie.rb +9 -2
- data/lib/jsonrpc/request.rb +12 -84
- data/lib/jsonrpc/response.rb +10 -59
- data/lib/jsonrpc/types.rb +13 -0
- data/lib/jsonrpc/version.rb +1 -1
- data/lib/jsonrpc.rb +2 -0
- metadata +38 -3
- /data/{.aiexclude → .aiignore} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf6393e7fe408f08dc933ebb6fc73a2541829eab966df8acedd7103a2e4e2c01
|
4
|
+
data.tar.gz: f1f17e444ea3a84cfd69e46316e595eaa00fc0a01e9566e313111dc0e47142b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 103c23a06d7d49ad68ed3d7249f24cf7a7c52cf82d72a9a0af68b1da4d2ec1869e8e80ef0207be9d4aaf5e85caf7433094f3f1e4dc52e812b4a5cf6cf074f735
|
7
|
+
data.tar.gz: eb0dcf3da91d3176de5507c1a9c604ffefdc59bfbd4476a0355d74fc85f301674b89f8b2a743b9eca4168dc3b410a43de3a056079674d64e7651fc7c2c69ada1
|
@@ -0,0 +1,52 @@
|
|
1
|
+
---
|
2
|
+
allowed-tools: Bash(bundle :*), Bash(git :*), Read, Edit, MultiEdit, Glob
|
3
|
+
description: Update Gemfile dependencies to latest minor versions
|
4
|
+
argument-hint: [gemfile] [commit]
|
5
|
+
---
|
6
|
+
|
7
|
+
Update the dependencies in the specified Gemfile (or ./Gemfile if no path provided) to their latest minor versions while
|
8
|
+
preserving major version constraints. Only update MAJOR.MINOR versions, never PATCH versions unless explicitly needed.
|
9
|
+
|
10
|
+
Steps:
|
11
|
+
1. Read the Gemfile at the specified path (or ./Gemfile if $ARGUMENTS is empty)
|
12
|
+
2. Read the corresponding Gemfile.lock to get current resolved versions
|
13
|
+
3. Run `bundle outdated --only-explicit` to check for available minor updates of explicitly declared gems
|
14
|
+
4. For each gem in Gemfile, check if Gemfile.lock has a newer minor version than the current Gemfile constraint allows
|
15
|
+
5. Update gem version constraints to match the minor version from Gemfile.lock or latest available, whichever is newer (MAJOR.MINOR format)
|
16
|
+
6. Use pessimistic version constraints (~> MAJOR.MINOR) to prevent automatic patch updates
|
17
|
+
7. Preserve any existing version operators but ensure they follow minor-only update strategy
|
18
|
+
8. Run `bundle update` to apply the changes
|
19
|
+
7. Skip step 8, 9 and 10 if --commit flag is not provided
|
20
|
+
8. Stage Gemfile (only if not gitignored)
|
21
|
+
9. Verify if Gemfile.lock is tracked and not gitignored. If both conditions are met, stage it for commit.
|
22
|
+
10. Create a git commit with message 'Update development dependencies' and a description listing all updated gems with their old and new versions like:
|
23
|
+
|
24
|
+
<commit-message>
|
25
|
+
Updated gems:
|
26
|
+
- rubocop: 1.75.2 → 1.78.0
|
27
|
+
- rubocop-yard: 0.10.0 → 1.0.0
|
28
|
+
</commit-message>
|
29
|
+
|
30
|
+
11. If any dependencies were updated, respond only with the update message. And if the user has chose to commit,
|
31
|
+
include the update commit message. Otherwise, respond only with the no op message.
|
32
|
+
|
33
|
+
<update-message>
|
34
|
+
Updated gems:
|
35
|
+
- rbs: 3.8 → 3.9
|
36
|
+
- rubocop: 1.78 → 1.80
|
37
|
+
- rubocop-rspec: 3.6 → 3.7
|
38
|
+
|
39
|
+
<update-commit-message>The changes have been committed with the message "Update development dependencies".</update-commit-message>
|
40
|
+
</update-message>
|
41
|
+
|
42
|
+
<no-op-message>All dependencies are up to date.</no-op-message>
|
43
|
+
|
44
|
+
Key bundle outdated flags used:
|
45
|
+
- `--only-explicit`: Only show gems explicitly listed in Gemfile (not dependencies)
|
46
|
+
- No `--local` flag to ensure remote gem sources are checked for latest versions
|
47
|
+
|
48
|
+
Arguments:
|
49
|
+
- `gemfile`: Gemfile path (defaults to ./Gemfile if not provided)
|
50
|
+
- `--commit`: Create a git commit after updating dependencies with message 'Update development dependencies' and a description listing all updated gems with their old and new versions
|
51
|
+
|
52
|
+
Gemfile path: ${ARGUMENTS:-./Gemfile}
|
data/.claude/settings.local.json
CHANGED
@@ -4,8 +4,12 @@
|
|
4
4
|
"Bash(bundle exec rspec:*)",
|
5
5
|
"Bash(bundle exec rubocop:*)",
|
6
6
|
"Bash(bundle exec rake:*)",
|
7
|
-
"Bash(mkdir:*)"
|
7
|
+
"Bash(mkdir:*)",
|
8
|
+
"Bash(bin/console:*)",
|
9
|
+
"Bash(bundle exec ruby:*)",
|
10
|
+
"Bash(git:*)",
|
11
|
+
"Bash(grep:*)"
|
8
12
|
]
|
9
13
|
},
|
10
14
|
"enableAllProjectMcpServers": false
|
11
|
-
}
|
15
|
+
}
|
data/CHANGELOG.md
CHANGED
@@ -5,10 +5,97 @@ 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.6.0] - 2025-09-17
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- MultiJson support for improved JSON performance and flexibility
|
12
|
+
- Configuration option to select JSON adapter (`JSONRPC.configuration.json_adapter`)
|
13
|
+
- Enhanced error handling with adapter and input preview details
|
14
|
+
- Better performance through optimized JSON parsing
|
15
|
+
- Enhanced examples
|
16
|
+
- Batch JSON-RPC request handling in Rails single-file routing example
|
17
|
+
- Smart home control API example using Rails routing DSL
|
18
|
+
- Updated documentation with batch request usage examples
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
- Replaced JSON gem with MultiJson throughout the codebase
|
22
|
+
- All JSON parsing now uses MultiJson for better adapter support
|
23
|
+
- Updated documentation to mention optimized JSON handling
|
24
|
+
- Refactored Request and Response classes to use dry-struct
|
25
|
+
- Simplified class definitions with automatic type checking
|
26
|
+
- Reduced code complexity while maintaining functionality
|
27
|
+
- Enhanced Rails integration
|
28
|
+
- Improved example applications with better error handling
|
29
|
+
- Updated Gemfile.lock files across all examples
|
30
|
+
|
31
|
+
## [0.5.0] - 2025-07-22
|
32
|
+
|
33
|
+
### Added
|
34
|
+
- Rails routing DSL for elegant JSON-RPC method mapping with support for namespaces and batch handling:
|
35
|
+
```ruby
|
36
|
+
# In routes.rb
|
37
|
+
jsonrpc '/' do
|
38
|
+
# Handle batch requests
|
39
|
+
batch to: 'batch#handle'
|
40
|
+
|
41
|
+
method 'on', to: 'main#on'
|
42
|
+
method 'off', to: 'main#off'
|
43
|
+
|
44
|
+
namespace 'lights' do
|
45
|
+
method 'on', to: 'lights#on' # becomes lights.on
|
46
|
+
method 'off', to: 'lights#off' # becomes lights.off
|
47
|
+
end
|
48
|
+
|
49
|
+
namespace 'climate' do
|
50
|
+
method 'on', to: 'climate#on' # becomes climate.on
|
51
|
+
method 'off', to: 'climate#off' # becomes climate.off
|
52
|
+
|
53
|
+
namespace 'fan' do
|
54
|
+
method 'on', to: 'fan#on' # becomes climate.fan.on
|
55
|
+
method 'off', to: 'fan#off' # becomes climate.fan.off
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
- `JSONRPC::BatchConstraint` for routing JSON-RPC batch requests to dedicated controllers:
|
61
|
+
```ruby
|
62
|
+
# Handle batch requests with custom constraint
|
63
|
+
post '/api', to: 'api#handle_batch', constraints: JSONRPC::BatchConstraint.new
|
64
|
+
|
65
|
+
# Or use the DSL (recommended)
|
66
|
+
jsonrpc '/api' do
|
67
|
+
batch to: 'api#handle_batch'
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
### Changed
|
72
|
+
- Procedure registration now supports optional validation blocks (defaults to empty contract):
|
73
|
+
```ruby
|
74
|
+
# Before: Always required validation block (even if empty)
|
75
|
+
procedure('ping') do
|
76
|
+
params do
|
77
|
+
# No params needed
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# After: Optional validation block
|
82
|
+
procedure('ping') # Uses empty contract by default
|
83
|
+
|
84
|
+
# Still works with validation when needed
|
85
|
+
procedure('add') do
|
86
|
+
params do
|
87
|
+
required(:a).value(:integer)
|
88
|
+
required(:b).value(:integer)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
```
|
92
|
+
- Simplified example configurations by removing unnecessary empty validation blocks
|
93
|
+
- Enhanced Rails integration with automatic DSL registration via Railtie
|
94
|
+
|
8
95
|
## [0.4.0] - 2025-07-18
|
9
96
|
|
10
97
|
### Added
|
11
|
-
- JSONRPC::BatchRequest#process_each method for simplified batch processing
|
98
|
+
- `JSONRPC::BatchRequest#process_each` method for simplified batch processing
|
12
99
|
|
13
100
|
## [0.3.0] - 2025-07-17
|
14
101
|
|
@@ -71,6 +158,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
71
158
|
- Helper methods for request and response processing
|
72
159
|
- Examples for basic and advanced usage scenarios
|
73
160
|
|
161
|
+
[0.6.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.5.0...v0.6.0
|
162
|
+
[0.5.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.4.0...v0.5.0
|
74
163
|
[0.4.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.3.0...v0.4.0
|
75
164
|
[0.3.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.2.0...v0.3.0
|
76
165
|
[0.2.0]: https://github.com/wilsonsilva/jsonrpc-middleware/compare/v0.1.0...v0.2.0
|
data/README.md
CHANGED
@@ -37,9 +37,11 @@ A Rack middleware implementing the JSON-RPC 2.0 protocol that integrates easily
|
|
37
37
|
- **Spec-compliant**: Fully implements the [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification)
|
38
38
|
- **Rack middleware integration**: Seamlessly integrates with Rack applications (Rails, Sinatra, Hanami, etc)
|
39
39
|
- **Support for all request types**: Handles single requests, notifications, and batch requests
|
40
|
+
- **Rails routing DSL**: Elegant routing DSL for Rails applications with support for namespaces and batch handling
|
40
41
|
- **Error handling**: Comprehensive error handling with standard JSON-RPC error responses
|
41
42
|
- **Request validation**: Define request parameter specifications and validations
|
42
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
|
43
45
|
|
44
46
|
## 🏗️ Architecture
|
45
47
|
|
@@ -174,6 +176,7 @@ rake bundle:audit:update # Updates the bundler-audit vulnerability database
|
|
174
176
|
rake clean # Remove any temporary products
|
175
177
|
rake clobber # Remove any generated files
|
176
178
|
rake coverage # Run spec with coverage
|
179
|
+
rake examples:bundle_install # Run bundle install on all example folders (useful after updating the gem version)
|
177
180
|
rake install # Build and install jsonrpc-middleware.gem into system gems
|
178
181
|
rake install:local # Build and install jsonrpc-middleware.gem into system gems without network access
|
179
182
|
rake qa # Test, lint and perform security and documentation audits
|
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'English' # For $CHILD_STATUS
|
3
4
|
require 'bundler/audit/task'
|
4
5
|
require 'bundler/gem_tasks'
|
5
6
|
require 'rspec/core/rake_task'
|
@@ -95,3 +96,59 @@ namespace :yard do
|
|
95
96
|
puts 'Done!'
|
96
97
|
end
|
97
98
|
end
|
99
|
+
|
100
|
+
namespace :examples do
|
101
|
+
desc 'Run bundle install on all example folders'
|
102
|
+
task :bundle_install do
|
103
|
+
examples_dir = File.join(Dir.pwd, 'examples')
|
104
|
+
|
105
|
+
unless Dir.exist?(examples_dir)
|
106
|
+
puts 'Examples directory not found'
|
107
|
+
exit 1
|
108
|
+
end
|
109
|
+
|
110
|
+
example_folders = Dir.glob(File.join(examples_dir, '*')).select { |path| Dir.exist?(path) }
|
111
|
+
|
112
|
+
if example_folders.empty?
|
113
|
+
puts 'No example folders found'
|
114
|
+
return
|
115
|
+
end
|
116
|
+
|
117
|
+
puts "Found #{example_folders.length} example folders:"
|
118
|
+
example_folders.each { |folder| puts " - #{File.basename(folder)}" }
|
119
|
+
puts
|
120
|
+
|
121
|
+
failed_folders = []
|
122
|
+
|
123
|
+
example_folders.each do |folder|
|
124
|
+
gemfile_path = File.join(folder, 'Gemfile')
|
125
|
+
|
126
|
+
unless File.exist?(gemfile_path)
|
127
|
+
puts "Skipping #{File.basename(folder)} - no Gemfile found"
|
128
|
+
next
|
129
|
+
end
|
130
|
+
|
131
|
+
puts "Running bundle install in #{File.basename(folder)}..."
|
132
|
+
|
133
|
+
Dir.chdir(folder) do
|
134
|
+
system('bundle install')
|
135
|
+
|
136
|
+
if $CHILD_STATUS.success?
|
137
|
+
puts " ✓ Successfully installed gems in #{File.basename(folder)}"
|
138
|
+
else
|
139
|
+
failed_folders << File.basename(folder)
|
140
|
+
puts " ✗ Failed to bundle install in #{File.basename(folder)}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
puts
|
145
|
+
end
|
146
|
+
|
147
|
+
if failed_folders.empty?
|
148
|
+
puts 'All example folders processed successfully!'
|
149
|
+
else
|
150
|
+
puts "Failed to process #{failed_folders.length} folders: #{failed_folders.join(", ")}"
|
151
|
+
exit 1
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/examples/README.md
CHANGED
@@ -10,6 +10,7 @@ This directory contains example implementations of JSON-RPC servers using the js
|
|
10
10
|
- [**rails**](./rails/) - Calculator server using Rails
|
11
11
|
- [**rails-single-file**](./rails-single-file/) - Echo server using Rails with bundler/inline
|
12
12
|
- [**rails-single-file-routing**](./rails-single-file-routing/) - Echo server using Rails with method-specific routing
|
13
|
+
- [**rails-routing-dsl**](./rails-routing-dsl/) - Smart home control server showcasing Rails JSON-RPC routing DSL
|
13
14
|
- [**sinatra-classic**](./sinatra-classic/) - Calculator server using classic Sinatra
|
14
15
|
- [**sinatra-modular**](./sinatra-modular/) - Calculator server using modular Sinatra
|
15
16
|
|
@@ -27,6 +28,14 @@ The echo examples implement:
|
|
27
28
|
|
28
29
|
- `echo` - Returns the input message
|
29
30
|
|
31
|
+
The rails-routing-dsl example implements a smart home control API:
|
32
|
+
|
33
|
+
- `on` / `off` - Control main home automation system
|
34
|
+
- `lights.on` / `lights.off` - Control lights
|
35
|
+
- `climate.on` / `climate.off` - Control climate system
|
36
|
+
- `climate.fan.on` / `climate.fan.off` - Control climate fan
|
37
|
+
- Batch request support for multiple operations
|
38
|
+
|
30
39
|
## Running Examples
|
31
40
|
|
32
41
|
Each example directory contains its own README with specific instructions. Generally:
|
data/examples/procedures.rb
CHANGED
data/examples/rack/Gemfile.lock
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
|
-
jsonrpc-middleware (0.
|
4
|
+
jsonrpc-middleware (0.5.0)
|
5
|
+
dry-struct (~> 1.8)
|
5
6
|
dry-validation (~> 1.11)
|
7
|
+
multi_json (~> 1.17)
|
6
8
|
zeitwerk (~> 2.7)
|
7
9
|
|
8
10
|
GEM
|
@@ -32,6 +34,11 @@ GEM
|
|
32
34
|
dry-logic (~> 1.5)
|
33
35
|
dry-types (~> 1.8)
|
34
36
|
zeitwerk (~> 2.6)
|
37
|
+
dry-struct (1.8.0)
|
38
|
+
dry-core (~> 1.1)
|
39
|
+
dry-types (~> 1.8, >= 1.8.2)
|
40
|
+
ice_nine (~> 0.11)
|
41
|
+
zeitwerk (~> 2.6)
|
35
42
|
dry-types (1.8.3)
|
36
43
|
bigdecimal (~> 3.0)
|
37
44
|
concurrent-ruby (~> 1.0)
|
@@ -45,7 +52,9 @@ GEM
|
|
45
52
|
dry-initializer (~> 3.2)
|
46
53
|
dry-schema (~> 1.14)
|
47
54
|
zeitwerk (~> 2.6)
|
55
|
+
ice_nine (0.11.2)
|
48
56
|
logger (1.7.0)
|
57
|
+
multi_json (1.17.0)
|
49
58
|
nio4r (2.7.4)
|
50
59
|
puma (6.6.0)
|
51
60
|
nio4r (~> 2.0)
|
@@ -1,8 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
|
-
jsonrpc-middleware (0.
|
4
|
+
jsonrpc-middleware (0.5.0)
|
5
|
+
dry-struct (~> 1.8)
|
5
6
|
dry-validation (~> 1.11)
|
7
|
+
multi_json (~> 1.17)
|
6
8
|
zeitwerk (~> 2.7)
|
7
9
|
|
8
10
|
GEM
|
@@ -32,6 +34,11 @@ GEM
|
|
32
34
|
dry-logic (~> 1.5)
|
33
35
|
dry-types (~> 1.8)
|
34
36
|
zeitwerk (~> 2.6)
|
37
|
+
dry-struct (1.8.0)
|
38
|
+
dry-core (~> 1.1)
|
39
|
+
dry-types (~> 1.8, >= 1.8.2)
|
40
|
+
ice_nine (~> 0.11)
|
41
|
+
zeitwerk (~> 2.6)
|
35
42
|
dry-types (1.8.3)
|
36
43
|
bigdecimal (~> 3.0)
|
37
44
|
concurrent-ruby (~> 1.0)
|
@@ -45,7 +52,9 @@ GEM
|
|
45
52
|
dry-initializer (~> 3.2)
|
46
53
|
dry-schema (~> 1.14)
|
47
54
|
zeitwerk (~> 2.6)
|
55
|
+
ice_nine (0.11.2)
|
48
56
|
logger (1.7.0)
|
57
|
+
multi_json (1.17.0)
|
49
58
|
nio4r (2.7.4)
|
50
59
|
puma (6.6.0)
|
51
60
|
nio4r (~> 2.0)
|
data/examples/rails/Gemfile.lock
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
|
-
jsonrpc-middleware (0.
|
4
|
+
jsonrpc-middleware (0.5.0)
|
5
|
+
dry-struct (~> 1.8)
|
5
6
|
dry-validation (~> 1.11)
|
7
|
+
multi_json (~> 1.17)
|
6
8
|
zeitwerk (~> 2.7)
|
7
9
|
|
8
10
|
GEM
|
@@ -113,6 +115,11 @@ GEM
|
|
113
115
|
dry-logic (~> 1.5)
|
114
116
|
dry-types (~> 1.8)
|
115
117
|
zeitwerk (~> 2.6)
|
118
|
+
dry-struct (1.8.0)
|
119
|
+
dry-core (~> 1.1)
|
120
|
+
dry-types (~> 1.8, >= 1.8.2)
|
121
|
+
ice_nine (~> 0.11)
|
122
|
+
zeitwerk (~> 2.6)
|
116
123
|
dry-types (1.8.3)
|
117
124
|
bigdecimal (~> 3.0)
|
118
125
|
concurrent-ruby (~> 1.0)
|
@@ -132,6 +139,7 @@ GEM
|
|
132
139
|
activesupport (>= 6.1)
|
133
140
|
i18n (1.14.7)
|
134
141
|
concurrent-ruby (~> 1.0)
|
142
|
+
ice_nine (0.11.2)
|
135
143
|
io-console (0.8.1)
|
136
144
|
irb (1.15.2)
|
137
145
|
pp (>= 0.6.0)
|
@@ -149,6 +157,7 @@ GEM
|
|
149
157
|
marcel (1.0.4)
|
150
158
|
mini_mime (1.1.5)
|
151
159
|
minitest (5.25.5)
|
160
|
+
multi_json (1.17.0)
|
152
161
|
net-imap (0.5.9)
|
153
162
|
date
|
154
163
|
net-protocol
|
@@ -159,22 +168,8 @@ GEM
|
|
159
168
|
net-smtp (0.5.1)
|
160
169
|
net-protocol
|
161
170
|
nio4r (2.7.4)
|
162
|
-
nokogiri (1.18.8-aarch64-linux-gnu)
|
163
|
-
racc (~> 1.4)
|
164
|
-
nokogiri (1.18.8-aarch64-linux-musl)
|
165
|
-
racc (~> 1.4)
|
166
|
-
nokogiri (1.18.8-arm-linux-gnu)
|
167
|
-
racc (~> 1.4)
|
168
|
-
nokogiri (1.18.8-arm-linux-musl)
|
169
|
-
racc (~> 1.4)
|
170
171
|
nokogiri (1.18.8-arm64-darwin)
|
171
172
|
racc (~> 1.4)
|
172
|
-
nokogiri (1.18.8-x86_64-darwin)
|
173
|
-
racc (~> 1.4)
|
174
|
-
nokogiri (1.18.8-x86_64-linux-gnu)
|
175
|
-
racc (~> 1.4)
|
176
|
-
nokogiri (1.18.8-x86_64-linux-musl)
|
177
|
-
racc (~> 1.4)
|
178
173
|
pp (0.6.2)
|
179
174
|
prettyprint
|
180
175
|
prettyprint (0.2.0)
|
@@ -242,14 +237,7 @@ GEM
|
|
242
237
|
zeitwerk (2.7.3)
|
243
238
|
|
244
239
|
PLATFORMS
|
245
|
-
aarch64-linux-gnu
|
246
|
-
aarch64-linux-musl
|
247
|
-
arm-linux-gnu
|
248
|
-
arm-linux-musl
|
249
240
|
arm64-darwin
|
250
|
-
x86_64-darwin
|
251
|
-
x86_64-linux-gnu
|
252
|
-
x86_64-linux-musl
|
253
241
|
|
254
242
|
DEPENDENCIES
|
255
243
|
debug
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# Rails JSON-RPC Routing DSL
|
2
|
+
|
3
|
+
Demonstrates using the Rails routing DSL extension to route JSON-RPC methods to different controller actions for a
|
4
|
+
smart home control system.
|
5
|
+
|
6
|
+
## Highlights
|
7
|
+
|
8
|
+
Uses the `jsonrpc` routing DSL to map JSON-RPC methods to Rails controller actions with clean, readable syntax:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class App < Rails::Application
|
12
|
+
# ...
|
13
|
+
routes.append do
|
14
|
+
jsonrpc '/' do
|
15
|
+
# Handle batch requests with a dedicated controller
|
16
|
+
batch to: 'batch#handle'
|
17
|
+
|
18
|
+
method 'on', to: 'main#on'
|
19
|
+
method 'off', to: 'main#off'
|
20
|
+
|
21
|
+
namespace 'lights' do
|
22
|
+
method 'on', to: 'lights#on' # becomes lights.on
|
23
|
+
method 'off', to: 'lights#off' # becomes lights.off
|
24
|
+
end
|
25
|
+
|
26
|
+
namespace 'climate' do
|
27
|
+
method 'on', to: 'climate#on' # becomes climate.on
|
28
|
+
method 'off', to: 'climate#off' # becomes climate.off
|
29
|
+
|
30
|
+
namespace 'fan' do
|
31
|
+
method 'on', to: 'fan#on' # becomes climate.fan.on
|
32
|
+
method 'off', to: 'fan#off' # becomes climate.fan.off
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class MainController < ActionController::Base
|
40
|
+
def on
|
41
|
+
render jsonrpc: { device: 'main_system', status: 'on' }
|
42
|
+
end
|
43
|
+
|
44
|
+
def off
|
45
|
+
render jsonrpc: { device: 'main_system', status: 'off' }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class LightsController < ActionController::Base
|
50
|
+
def on
|
51
|
+
render jsonrpc: { device: 'lights', status: 'on' }
|
52
|
+
end
|
53
|
+
|
54
|
+
def off
|
55
|
+
render jsonrpc: { device: 'lights', status: 'off' }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class ClimateController < ActionController::Base
|
60
|
+
def on
|
61
|
+
render jsonrpc: { device: 'climate_system', status: 'on' }
|
62
|
+
end
|
63
|
+
|
64
|
+
def off
|
65
|
+
render jsonrpc: { device: 'climate_system', status: 'off' }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class FanController < ActionController::Base
|
70
|
+
def on
|
71
|
+
render jsonrpc: { device: 'fan', status: 'on' }
|
72
|
+
end
|
73
|
+
|
74
|
+
def off
|
75
|
+
render jsonrpc: { device: 'fan', status: 'off' }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class BatchController < ActionController::Base
|
80
|
+
def handle
|
81
|
+
# Process each request in the batch and collect results
|
82
|
+
results = jsonrpc_batch.process_each do |request_or_notification|
|
83
|
+
case request_or_notification.method
|
84
|
+
when 'on'
|
85
|
+
{ device: 'main_system', status: 'on' }
|
86
|
+
when 'off'
|
87
|
+
{ device: 'main_system', status: 'off' }
|
88
|
+
when 'lights.on'
|
89
|
+
{ device: 'lights', status: 'on' }
|
90
|
+
when 'lights.off'
|
91
|
+
{ device: 'lights', status: 'off' }
|
92
|
+
# ... handle other methods
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
render jsonrpc: results
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
## Running
|
102
|
+
|
103
|
+
```sh
|
104
|
+
bundle exec rackup
|
105
|
+
```
|
106
|
+
|
107
|
+
## API
|
108
|
+
|
109
|
+
The server implements smart home controls with these procedures:
|
110
|
+
|
111
|
+
**Root Methods:**
|
112
|
+
- `on` - Turn home automation system on
|
113
|
+
- `off` - Turn home automation system off
|
114
|
+
|
115
|
+
**Lights Namespace:**
|
116
|
+
- `lights.on` - Turn lights on
|
117
|
+
- `lights.off` - Turn lights off
|
118
|
+
|
119
|
+
**Climate Namespace:**
|
120
|
+
- `climate.on` - Turn climate system on
|
121
|
+
- `climate.off` - Turn climate system off
|
122
|
+
|
123
|
+
**Climate Fan Namespace:**
|
124
|
+
- `climate.fan.on` - Turn fan on
|
125
|
+
- `climate.fan.off` - Turn fan off
|
126
|
+
|
127
|
+
**Batch Processing:**
|
128
|
+
- Batch requests are automatically routed to the `BatchController#handle` action
|
129
|
+
- The controller uses `jsonrpc_batch.process_each` to handle each request in the batch
|
130
|
+
- Responses are collected and returned as an array
|
131
|
+
|
132
|
+
## Example Requests
|
133
|
+
|
134
|
+
Turn on the home automation system:
|
135
|
+
```sh
|
136
|
+
curl -X POST http://localhost:9292 \
|
137
|
+
-H "Content-Type: application/json" \
|
138
|
+
-d '{"jsonrpc": "2.0", "method": "on", "params": {}, "id": 1}'
|
139
|
+
```
|
140
|
+
|
141
|
+
Turn off the home automation system:
|
142
|
+
```sh
|
143
|
+
curl -X POST http://localhost:9292 \
|
144
|
+
-H "Content-Type: application/json" \
|
145
|
+
-d '{"jsonrpc": "2.0", "method": "off", "params": {}, "id": 2}'
|
146
|
+
```
|
147
|
+
|
148
|
+
Turn on lights:
|
149
|
+
```sh
|
150
|
+
curl -X POST http://localhost:9292 \
|
151
|
+
-H "Content-Type: application/json" \
|
152
|
+
-d '{"jsonrpc": "2.0", "method": "lights.on", "params": {}, "id": 3}'
|
153
|
+
```
|
154
|
+
|
155
|
+
Turn off the lights:
|
156
|
+
```sh
|
157
|
+
curl -X POST http://localhost:9292 \
|
158
|
+
-H "Content-Type: application/json" \
|
159
|
+
-d '{"jsonrpc": "2.0", "method": "lights.off", "params": {}, "id": 4}'
|
160
|
+
```
|
161
|
+
|
162
|
+
Turn on the climate system:
|
163
|
+
```sh
|
164
|
+
curl -X POST http://localhost:9292 \
|
165
|
+
-H "Content-Type: application/json" \
|
166
|
+
-d '{"jsonrpc": "2.0", "method": "climate.on", "params": {}, "id": 5}'
|
167
|
+
```
|
168
|
+
|
169
|
+
Turn off the climate system:
|
170
|
+
```sh
|
171
|
+
curl -X POST http://localhost:9292 \
|
172
|
+
-H "Content-Type: application/json" \
|
173
|
+
-d '{"jsonrpc": "2.0", "method": "climate.off", "params": {}, "id": 6}'
|
174
|
+
```
|
175
|
+
|
176
|
+
Turn on fan:
|
177
|
+
```sh
|
178
|
+
curl -X POST http://localhost:9292 \
|
179
|
+
-H "Content-Type: application/json" \
|
180
|
+
-d '{"jsonrpc": "2.0", "method": "climate.fan.on", "params": {}, "id": 7}'
|
181
|
+
```
|
182
|
+
|
183
|
+
Turn off fan:
|
184
|
+
```sh
|
185
|
+
curl -X POST http://localhost:9292 \
|
186
|
+
-H "Content-Type: application/json" \
|
187
|
+
-d '{"jsonrpc": "2.0", "method": "climate.fan.off", "params": {}, "id": 8}'
|
188
|
+
```
|
189
|
+
|
190
|
+
Batch request for evening routine:
|
191
|
+
```sh
|
192
|
+
curl -X POST http://localhost:9292 \
|
193
|
+
-H "Content-Type: application/json" \
|
194
|
+
-d '[
|
195
|
+
{"jsonrpc": "2.0", "method": "off", "params": {}, "id": 9},
|
196
|
+
{"jsonrpc": "2.0", "method": "lights.off", "params": {}, "id": 10},
|
197
|
+
{"jsonrpc": "2.0", "method": "climate.off", "params": {}, "id": 11}
|
198
|
+
]'
|
199
|
+
```
|