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.
Files changed (79) 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 +19 -0
  15. data/CLAUDE.md +2 -113
  16. data/Dockerfile +144 -0
  17. data/README.md +9 -17
  18. data/Rakefile +62 -11
  19. data/examples/procedures.rb +3 -1
  20. data/examples/rack/Gemfile.lock +4 -4
  21. data/examples/rack-echo/Gemfile.lock +4 -4
  22. data/examples/rails/Gemfile.lock +12 -5
  23. data/examples/rails/config/initializers/jsonrpc.rb +1 -1
  24. data/examples/rails-routing-dsl/config.ru +5 -5
  25. data/examples/rails-single-file/config.ru +1 -1
  26. data/examples/rails-single-file-routing/config.ru +1 -1
  27. data/examples/sinatra-classic/Gemfile.lock +11 -4
  28. data/examples/sinatra-modular/Gemfile.lock +11 -4
  29. data/lib/jsonrpc/batch_request.rb +8 -11
  30. data/lib/jsonrpc/batch_response.rb +6 -8
  31. data/lib/jsonrpc/configuration.rb +30 -4
  32. data/lib/jsonrpc/error.rb +7 -8
  33. data/lib/jsonrpc/errors/internal_error.rb +2 -0
  34. data/lib/jsonrpc/errors/invalid_params_error.rb +2 -0
  35. data/lib/jsonrpc/errors/invalid_request_error.rb +2 -0
  36. data/lib/jsonrpc/errors/method_not_found_error.rb +2 -0
  37. data/lib/jsonrpc/errors/parse_error.rb +2 -0
  38. data/lib/jsonrpc/helpers.rb +6 -0
  39. data/lib/jsonrpc/middleware.rb +12 -11
  40. data/lib/jsonrpc/notification.rb +7 -8
  41. data/lib/jsonrpc/parser.rb +13 -12
  42. data/lib/jsonrpc/railtie/batch_constraint.rb +1 -0
  43. data/lib/jsonrpc/railtie/mapper_extension.rb +2 -2
  44. data/lib/jsonrpc/railtie/method_constraint.rb +9 -0
  45. data/lib/jsonrpc/railtie/routes_dsl.rb +10 -15
  46. data/lib/jsonrpc/railtie.rb +2 -0
  47. data/lib/jsonrpc/response.rb +2 -2
  48. data/lib/jsonrpc/types.rb +1 -1
  49. data/lib/jsonrpc/validator.rb +14 -4
  50. data/lib/jsonrpc/version.rb +1 -1
  51. data/lib/jsonrpc.rb +3 -0
  52. data/rbs_collection.lock.yaml +476 -0
  53. data/rbs_collection.yaml +21 -0
  54. data/sig/jsonrpc/batch_request.rbs +17 -0
  55. data/sig/jsonrpc/batch_response.rbs +17 -0
  56. data/sig/jsonrpc/configuration.rbs +18 -0
  57. data/sig/jsonrpc/error.rbs +17 -0
  58. data/sig/jsonrpc/errors/internal_error.rbs +5 -0
  59. data/sig/jsonrpc/errors/invalid_params_error.rbs +5 -0
  60. data/sig/jsonrpc/errors/invalid_request_error.rbs +5 -0
  61. data/sig/jsonrpc/errors/method_not_found_error.rbs +5 -0
  62. data/sig/jsonrpc/errors/parse_error.rbs +5 -0
  63. data/sig/jsonrpc/middleware.rbs +20 -3
  64. data/sig/jsonrpc/notification.rbs +15 -0
  65. data/sig/jsonrpc/parser.rbs +7 -1
  66. data/sig/jsonrpc/request.rbs +18 -0
  67. data/sig/jsonrpc/response.rbs +19 -0
  68. data/sig/jsonrpc/validator.rbs +8 -0
  69. data/sig/jsonrpc.rbs +3 -156
  70. data/sig/multi_json.rbs +17 -0
  71. data/sig/type_definitions.rbs +11 -0
  72. data/sig/zeitwerk.rbs +10 -0
  73. metadata +34 -12
  74. data/.claude/commands/document.md +0 -105
  75. data/.claude/commands/gemfile/update.md +0 -52
  76. data/.claude/commands/test.md +0 -561
  77. data/.claude/docs/yard.md +0 -602
  78. data/.claude/settings.local.json +0 -15
  79. data/.yardstick.yml +0 -22
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- jsonrpc-middleware (0.5.0)
4
+ jsonrpc-middleware (0.6.0)
5
5
  dry-struct (~> 1.8)
6
6
  dry-validation (~> 1.11)
7
- multi_json (~> 1.17)
7
+ multi_json (~> 1.20)
8
8
  zeitwerk (~> 2.7)
9
9
 
10
10
  GEM
@@ -156,8 +156,11 @@ GEM
156
156
  net-smtp
157
157
  marcel (1.0.4)
158
158
  mini_mime (1.1.5)
159
- minitest (5.25.5)
160
- multi_json (1.17.0)
159
+ mini_portile2 (2.8.9)
160
+ minitest (6.0.2)
161
+ drb (~> 2.0)
162
+ prism (~> 1.5)
163
+ multi_json (1.21.1)
161
164
  net-imap (0.5.9)
162
165
  date
163
166
  net-protocol
@@ -168,11 +171,15 @@ GEM
168
171
  net-smtp (0.5.1)
169
172
  net-protocol
170
173
  nio4r (2.7.4)
174
+ nokogiri (1.18.8)
175
+ mini_portile2 (~> 2.8.2)
176
+ racc (~> 1.4)
171
177
  nokogiri (1.18.8-arm64-darwin)
172
178
  racc (~> 1.4)
173
179
  pp (0.6.2)
174
180
  prettyprint
175
181
  prettyprint (0.2.0)
182
+ prism (1.9.0)
176
183
  psych (5.2.6)
177
184
  date
178
185
  stringio
@@ -246,4 +253,4 @@ DEPENDENCIES
246
253
  rails (~> 8.0.2)
247
254
 
248
255
  BUNDLED WITH
249
- 2.7.0
256
+ 4.0.8
@@ -16,7 +16,7 @@ require_relative '../../../procedures'
16
16
  # end
17
17
  #
18
18
  # rule(:addends) do
19
- # key.failure('must contain at least one addend') if value.empty?
19
+ # key.failure('must contain at least two addends') if value.size < 2
20
20
  # end
21
21
  # end
22
22
  #
@@ -65,7 +65,7 @@ class App < Rails::Application
65
65
  end
66
66
 
67
67
  # Controller for main system operations
68
- class MainController < ActionController::Base
68
+ class MainController < ApplicationController
69
69
  def on
70
70
  render jsonrpc: { device: 'main_system', status: 'on' }
71
71
  end
@@ -76,7 +76,7 @@ class MainController < ActionController::Base
76
76
  end
77
77
 
78
78
  # Controller for lights operations
79
- class LightsController < ActionController::Base
79
+ class LightsController < ApplicationController
80
80
  def on
81
81
  render jsonrpc: { device: 'lights', status: 'on' }
82
82
  end
@@ -87,7 +87,7 @@ class LightsController < ActionController::Base
87
87
  end
88
88
 
89
89
  # Controller for climate operations
90
- class ClimateController < ActionController::Base
90
+ class ClimateController < ApplicationController
91
91
  def on
92
92
  render jsonrpc: { device: 'climate_system', status: 'on' }
93
93
  end
@@ -98,7 +98,7 @@ class ClimateController < ActionController::Base
98
98
  end
99
99
 
100
100
  # Controller for climate fan operations
101
- class FanController < ActionController::Base
101
+ class FanController < ApplicationController
102
102
  def on
103
103
  render jsonrpc: { device: 'fan', status: 'on' }
104
104
  end
@@ -109,7 +109,7 @@ class FanController < ActionController::Base
109
109
  end
110
110
 
111
111
  # Controller for batch operations
112
- class BatchController < ActionController::Base
112
+ class BatchController < ApplicationController
113
113
  def handle
114
114
  # Process each request in the batch and collect results
115
115
  results = jsonrpc_batch.process_each do |request_or_notification|
@@ -40,7 +40,7 @@ class App < Rails::Application
40
40
  end
41
41
 
42
42
  # Define the JSONRPC controller
43
- class JsonrpcController < ActionController::Base
43
+ class JsonrpcController < ApplicationController
44
44
  def handle
45
45
  if jsonrpc_request?
46
46
  result = handle_single(jsonrpc_request)
@@ -47,7 +47,7 @@ class App < Rails::Application
47
47
  end
48
48
 
49
49
  # Define the JSONRPC controller
50
- class JsonrpcController < ActionController::Base
50
+ class JsonrpcController < ApplicationController
51
51
  def echo
52
52
  render jsonrpc: jsonrpc_request.params
53
53
  end
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- jsonrpc-middleware (0.5.0)
4
+ jsonrpc-middleware (0.6.0)
5
+ dry-struct (~> 1.8)
5
6
  dry-validation (~> 1.11)
6
- multi_json (~> 1.17)
7
+ multi_json (~> 1.20)
7
8
  zeitwerk (~> 2.7)
8
9
 
9
10
  GEM
@@ -34,6 +35,11 @@ GEM
34
35
  dry-logic (~> 1.5)
35
36
  dry-types (~> 1.8)
36
37
  zeitwerk (~> 2.6)
38
+ dry-struct (1.8.0)
39
+ dry-core (~> 1.1)
40
+ dry-types (~> 1.8, >= 1.8.2)
41
+ ice_nine (~> 0.11)
42
+ zeitwerk (~> 2.6)
37
43
  dry-types (1.8.3)
38
44
  bigdecimal (~> 3.0)
39
45
  concurrent-ruby (~> 1.0)
@@ -47,8 +53,9 @@ GEM
47
53
  dry-initializer (~> 3.2)
48
54
  dry-schema (~> 1.14)
49
55
  zeitwerk (~> 2.6)
56
+ ice_nine (0.11.2)
50
57
  logger (1.7.0)
51
- multi_json (1.17.0)
58
+ multi_json (1.21.1)
52
59
  mustermann (3.0.3)
53
60
  ruby2_keywords (~> 0.0.1)
54
61
  nio4r (2.7.4)
@@ -93,4 +100,4 @@ DEPENDENCIES
93
100
  sinatra-contrib
94
101
 
95
102
  BUNDLED WITH
96
- 2.7.0
103
+ 4.0.8
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- jsonrpc-middleware (0.5.0)
4
+ jsonrpc-middleware (0.6.0)
5
+ dry-struct (~> 1.8)
5
6
  dry-validation (~> 1.11)
6
- multi_json (~> 1.17)
7
+ multi_json (~> 1.20)
7
8
  zeitwerk (~> 2.7)
8
9
 
9
10
  GEM
@@ -34,6 +35,11 @@ GEM
34
35
  dry-logic (~> 1.5)
35
36
  dry-types (~> 1.8)
36
37
  zeitwerk (~> 2.6)
38
+ dry-struct (1.8.0)
39
+ dry-core (~> 1.1)
40
+ dry-types (~> 1.8, >= 1.8.2)
41
+ ice_nine (~> 0.11)
42
+ zeitwerk (~> 2.6)
37
43
  dry-types (1.8.3)
38
44
  bigdecimal (~> 3.0)
39
45
  concurrent-ruby (~> 1.0)
@@ -47,8 +53,9 @@ GEM
47
53
  dry-initializer (~> 3.2)
48
54
  dry-schema (~> 1.14)
49
55
  zeitwerk (~> 2.6)
56
+ ice_nine (0.11.2)
50
57
  logger (1.7.0)
51
- multi_json (1.17.0)
58
+ multi_json (1.21.1)
52
59
  mustermann (3.0.3)
53
60
  ruby2_keywords (~> 0.0.1)
54
61
  nio4r (2.7.4)
@@ -93,4 +100,4 @@ DEPENDENCIES
93
100
  sinatra-contrib
94
101
 
95
102
  BUNDLED WITH
96
- 2.7.0
103
+ 4.0.8
@@ -6,6 +6,8 @@ module JSONRPC
6
6
  # A batch request is an Array filled with Request objects to send several requests at once.
7
7
  # The Server should respond with an Array containing the corresponding Response objects.
8
8
  #
9
+ # @api public
10
+ #
9
11
  # @example Create a batch request with multiple requests
10
12
  # batch = JSONRPC::BatchRequest.new([
11
13
  # JSONRPC::Request.new(method: "sum", params: [1, 2, 4], id: "1"),
@@ -39,14 +41,13 @@ module JSONRPC
39
41
  # ]
40
42
  # batch = JSONRPC::BatchRequest.new(requests)
41
43
  #
42
- # @param requests [Array<JSONRPC::Request, JSONRPC::Notification, JSONRPC::Error>] an array of request objects
43
- # or errors
44
44
  # @raise [ArgumentError] if requests is not an Array
45
- #
46
45
  # @raise [ArgumentError] if requests is empty
47
- #
48
46
  # @raise [ArgumentError] if any request is not a valid Request, Notification, or Error
49
47
  #
48
+ # @param requests [Array<JSONRPC::Request, JSONRPC::Notification, JSONRPC::Error>] an array of request objects
49
+ # or errors
50
+ #
50
51
  def initialize(requests)
51
52
  validate_requests(requests)
52
53
  @requests = requests
@@ -109,9 +110,7 @@ module JSONRPC
109
110
  #
110
111
  # @return [Integer] the number of requests in the batch
111
112
  #
112
- def size
113
- requests.size
114
- end
113
+ def size = requests.size
115
114
 
116
115
  # Alias for size method providing Array-like interface
117
116
  #
@@ -162,14 +161,12 @@ module JSONRPC
162
161
  #
163
162
  # @api private
164
163
  #
165
- # @param requests [Array] the array of requests
166
- #
167
164
  # @raise [ArgumentError] if requests is not an Array
168
- #
169
165
  # @raise [ArgumentError] if requests is empty
170
- #
171
166
  # @raise [ArgumentError] if any request is not a valid Request, Notification, or Error
172
167
  #
168
+ # @param requests [Array] the array of requests
169
+ #
173
170
  # @return [void]
174
171
  #
175
172
  def validate_requests(requests)
@@ -7,6 +7,8 @@ module JSONRPC
7
7
  # a Batch Request. The Server should respond with one Response for each Request
8
8
  # (except for Notifications which don't receive responses).
9
9
  #
10
+ # @api public
11
+ #
10
12
  # @example Create a batch response
11
13
  # batch = JSONRPC::BatchResponse.new([
12
14
  # JSONRPC::Response.new(result: 7, id: "1"),
@@ -39,14 +41,12 @@ module JSONRPC
39
41
  # ]
40
42
  # batch = JSONRPC::BatchResponse.new(responses)
41
43
  #
42
- # @param responses [Array<JSONRPC::Response>] an array of response objects
43
- #
44
44
  # @raise [ArgumentError] if responses is not an Array
45
- #
46
45
  # @raise [ArgumentError] if responses is empty
47
- #
48
46
  # @raise [ArgumentError] if any response is not a valid Response
49
47
  #
48
+ # @param responses [Array<JSONRPC::Response>] an array of response objects
49
+ #
50
50
  def initialize(responses)
51
51
  validate_responses(responses)
52
52
  @responses = responses
@@ -119,14 +119,12 @@ module JSONRPC
119
119
  #
120
120
  # @api private
121
121
  #
122
- # @param responses [Array] the array of responses
123
- #
124
122
  # @raise [ArgumentError] if responses is not an Array
125
- #
126
123
  # @raise [ArgumentError] if responses is empty
127
- #
128
124
  # @raise [ArgumentError] if any response is not a valid Response
129
125
  #
126
+ # @param responses [Array] the array of responses
127
+ #
130
128
  # @return [void]
131
129
  #
132
130
  def validate_responses(responses)
@@ -20,32 +20,52 @@ module JSONRPC
20
20
  #
21
21
  # @api public
22
22
  #
23
- # @!method allow_positional_arguments
23
+ # @!attribute [r] allow_positional_arguments
24
24
  # Indicates if the procedure accepts positional arguments
25
+ #
25
26
  # @api public
26
27
  #
27
28
  # @example
28
29
  # procedure.allow_positional_arguments # => true
30
+ #
29
31
  # @return [Boolean] whether the procedure accepts positional arguments
30
32
  #
31
- # @!method contract
33
+ # @!attribute [r] contract
32
34
  # The validation contract for procedure parameters
35
+ #
33
36
  # @api public
34
37
  #
35
38
  # @example
36
39
  # procedure.contract # => #<Dry::Validation::Contract...>
40
+ #
37
41
  # @return [Dry::Validation::Contract] the validation contract for procedure parameters
38
42
  #
39
- # @!method parameter_name
43
+ # @!attribute [r] parameter_name
40
44
  # The name of the first parameter in the contract schema
45
+ #
41
46
  # @api public
42
47
  #
43
48
  # @example
44
49
  # procedure.parameter_name # => :numbers
50
+ #
45
51
  # @return [Symbol, nil] the name of the first parameter in the contract schema
46
52
  #
47
53
  Procedure = Data.define(:allow_positional_arguments, :contract, :parameter_name)
48
54
 
55
+ # The logger instance used for error and diagnostic output
56
+ #
57
+ # @api public
58
+ #
59
+ # @example Using the default logger
60
+ # config.logger # => #<Logger:...>
61
+ #
62
+ # @example Setting a custom logger
63
+ # config.logger = Logger.new('log/jsonrpc.log')
64
+ #
65
+ # @return [Logger] the logger instance
66
+ #
67
+ attr_accessor :logger
68
+
49
69
  # Whether to log detailed internal error information in the terminal
50
70
  #
51
71
  # @api public
@@ -108,6 +128,8 @@ module JSONRPC
108
128
  # @example
109
129
  # config.json_adapter = :oj
110
130
  #
131
+ # @param adapter [Symbol, nil] the JSON adapter to use
132
+ #
111
133
  # @return [Symbol, nil] the JSON adapter to use
112
134
  #
113
135
  def json_adapter=(adapter)
@@ -127,6 +149,7 @@ module JSONRPC
127
149
  # render_internal_errors: true
128
150
  # )
129
151
  #
152
+ # @param logger [Logger] the logger instance for error and diagnostic output
130
153
  # @param log_internal_errors [Boolean] whether to log detailed internal error information in the terminal
131
154
  # @param log_request_validation_errors [Boolean] whether to log validation errors during JSON-RPC request processing
132
155
  # @param rescue_internal_errors [Boolean] whether internal errors should be rescued and converted to JSON-RPC errors
@@ -136,6 +159,7 @@ module JSONRPC
136
159
  # @return [Configuration] a new configuration instance
137
160
  #
138
161
  def initialize(
162
+ logger: Logger.new($stdout, progname: 'JSONRPC'),
139
163
  log_internal_errors: true,
140
164
  log_request_validation_errors: false,
141
165
  rescue_internal_errors: true,
@@ -143,6 +167,7 @@ module JSONRPC
143
167
  validate_procedure_signatures: true
144
168
  )
145
169
  @procedures = {}
170
+ @logger = logger
146
171
  @log_internal_errors = log_internal_errors
147
172
  @log_request_validation_errors = log_request_validation_errors
148
173
  @rescue_internal_errors = rescue_internal_errors
@@ -180,8 +205,9 @@ module JSONRPC
180
205
  #
181
206
  # @param method_name [String, Symbol] the name of the procedure
182
207
  # @param allow_positional_arguments [Boolean] whether the procedure accepts positional arguments
208
+ # @param block [Proc, nil] an optional block that defines the validation contract using Dry::Validation DSL
183
209
  #
184
- # @yield [optional] A block that defines the validation contract using Dry::Validation DSL
210
+ # @yield A block that defines the validation contract using Dry::Validation DSL
185
211
  #
186
212
  # @return [Procedure] the registered procedure
187
213
  #
data/lib/jsonrpc/error.rb CHANGED
@@ -82,15 +82,14 @@ module JSONRPC
82
82
  # @example Create an error with additional data
83
83
  # error = JSONRPC::Error.new("Invalid params", code: -32602, data: { "field" => "missing" })
84
84
  #
85
+ # @raise [ArgumentError] if code is not an Integer
86
+ # @raise [ArgumentError] if message is not a String
87
+ #
85
88
  # @param message [String] short description of the error
86
89
  # @param code [Integer] a number indicating the error type
87
90
  # @param data [Hash, Array, String, Number, Boolean, nil] additional error information
88
91
  # @param request_id [String, Integer, nil] the request identifier
89
92
  #
90
- # @raise [ArgumentError] if code is not an Integer
91
- #
92
- # @raise [ArgumentError] if message is not a String
93
- #
94
93
  def initialize(message, code:, data: nil, request_id: nil)
95
94
  super(message)
96
95
 
@@ -150,10 +149,10 @@ module JSONRPC
150
149
  #
151
150
  # @api private
152
151
  #
153
- # @param code [Integer] the error code
154
- #
155
152
  # @raise [ArgumentError] if code is not an Integer
156
153
  #
154
+ # @param code [Integer] the error code
155
+ #
157
156
  # @return [void]
158
157
  #
159
158
  def validate_code(code)
@@ -164,10 +163,10 @@ module JSONRPC
164
163
  #
165
164
  # @api private
166
165
  #
167
- # @param message [String] the error message
168
- #
169
166
  # @raise [ArgumentError] if message is not a String
170
167
  #
168
+ # @param message [String] the error message
169
+ #
171
170
  # @return [void]
172
171
  #
173
172
  def validate_message(message)
@@ -5,6 +5,8 @@ module JSONRPC
5
5
  #
6
6
  # Raised when there was an internal JSON-RPC error.
7
7
  #
8
+ # @api public
9
+ #
8
10
  # @example Create an internal error
9
11
  # error = JSONRPC::Errors::InternalError.new(data: { details: 'Unexpected server error' })
10
12
  #
@@ -5,6 +5,8 @@ module JSONRPC
5
5
  #
6
6
  # Raised when invalid method parameter(s) were provided.
7
7
  #
8
+ # @api public
9
+ #
8
10
  # @example Create an invalid params error
9
11
  # error = JSONRPC::InvalidParamsError.new(data: { details: "Expected array of integers" })
10
12
  #
@@ -5,6 +5,8 @@ module JSONRPC
5
5
  #
6
6
  # Raised when the JSON sent is not a valid Request object.
7
7
  #
8
+ # @api public
9
+ #
8
10
  # @example Create an invalid request error
9
11
  # error = JSONRPC::InvalidRequestError.new(data: { details: "Method must be a string" })
10
12
  #
@@ -5,6 +5,8 @@ module JSONRPC
5
5
  #
6
6
  # Raised when the method does not exist / is not available.
7
7
  #
8
+ # @api public
9
+ #
8
10
  # @example Create a method not found error
9
11
  # error = JSONRPC::MethodNotFound.new(data: { requested_method: "unknown_method" })
10
12
  #
@@ -6,6 +6,8 @@ module JSONRPC
6
6
  # Raised when invalid JSON was received by the server.
7
7
  # An error occurred on the server while parsing the JSON text.
8
8
  #
9
+ # @api public
10
+ #
9
11
  # @example Create a parse error
10
12
  # error = JSONRPC::ParseError.new(data: { details: "Unexpected end of input" })
11
13
  #
@@ -2,6 +2,9 @@
2
2
 
3
3
  module JSONRPC
4
4
  # Framework-agnostic helpers for JSON-RPC
5
+ #
6
+ # @api public
7
+ #
5
8
  module Helpers
6
9
  # Extends the including class with ClassMethods when module is included
7
10
  #
@@ -21,6 +24,9 @@ module JSONRPC
21
24
  end
22
25
 
23
26
  # Class methods for registering JSON-RPC procedure handlers
27
+ #
28
+ # @api public
29
+ #
24
30
  module ClassMethods
25
31
  # Registers a JSON-RPC procedure with the given method name
26
32
  #
@@ -41,13 +41,15 @@ module JSONRPC
41
41
  # @option options [String] :path ('/') The path to handle JSON-RPC requests on
42
42
  # @option options [Boolean] :rescue_internal_errors (nil) Override config rescue_internal_errors
43
43
  # @option options [Boolean] :log_internal_errors (true) Override config log_internal_errors
44
+ # @option options [Logger] :logger (nil) Override config logger
44
45
  #
45
46
  def initialize(app, options = {})
46
47
  @app = app
47
- @parser = Parser.new
48
- @validator = Validator.new
49
48
  @path = options.fetch(:path, DEFAULT_PATH)
50
49
  @config = JSONRPC.configuration
50
+ @logger = options.fetch(:logger, @config.logger)
51
+ @parser = Parser.new
52
+ @validator = Validator.new(logger: @logger)
51
53
  @log_internal_errors = options.fetch(:log_internal_errors, @config.log_internal_errors)
52
54
  @rescue_internal_errors = options.fetch(:rescue_internal_errors, @config.rescue_internal_errors)
53
55
  end
@@ -89,10 +91,10 @@ module JSONRPC
89
91
  #
90
92
  # @api private
91
93
  #
92
- # @return [Array] Rack response tuple
93
- #
94
94
  # @raise [StandardError] Catches all errors and converts to Internal Error response
95
95
  #
96
+ # @return [Array] Rack response tuple
97
+ #
96
98
  def handle_jsonrpc_request
97
99
  parsed_request = parse_request
98
100
  return parsed_request if parsed_request.is_a?(Array) # Early return for parse errors
@@ -122,12 +124,11 @@ module JSONRPC
122
124
  #
123
125
  # @api private
124
126
  #
125
- # @return [Request, Notification, BatchRequest, Array] Parsed request or error response
126
- #
127
127
  # @raise [ParseError] When JSON parsing fails
128
- #
129
128
  # @raise [InvalidRequestError] When request structure is invalid
130
129
  #
130
+ # @return [Request, Notification, BatchRequest, Array] Parsed request or error response
131
+ #
131
132
  def parse_request
132
133
  body = read_request_body
133
134
  parsed = @parser.parse(body)
@@ -259,7 +260,7 @@ module JSONRPC
259
260
  # @return [Array<Request, Notification>] Array of valid requests
260
261
  #
261
262
  def collect_valid_requests(batch_request)
262
- batch_request.requests.reject { |item| item.is_a?(Error) }
263
+ batch_request.requests.grep_v(Error)
263
264
  end
264
265
 
265
266
  # Builds ordered array of responses maintaining request order for batch processing
@@ -378,7 +379,7 @@ module JSONRPC
378
379
  body_content
379
380
  end
380
381
 
381
- # Logs internal errors to stdout with full backtrace
382
+ # Logs internal errors with full backtrace using the configured logger
382
383
  #
383
384
  # @api private
384
385
  #
@@ -390,8 +391,8 @@ module JSONRPC
390
391
  # @return [void]
391
392
  #
392
393
  def log_internal_error(error)
393
- puts "Internal error: #{error.message}"
394
- puts error.backtrace.join("\n")
394
+ @logger.error("Internal error: #{error.message}")
395
+ @logger.error(error.backtrace.join("\n"))
395
396
  end
396
397
  end
397
398
  end
@@ -62,13 +62,12 @@ module JSONRPC
62
62
  # @example Create a notification with named parameters
63
63
  # JSONRPC::Notification.new(method: "log", params: { level: "info", message: "Hello" })
64
64
  #
65
- # @param method [String] the name of the method to be invoked
66
- # @param params [Hash, Array, nil] the parameters to be used during method invocation
67
- #
68
65
  # @raise [ArgumentError] if method is not a String or is reserved
69
- #
70
66
  # @raise [ArgumentError] if params is not a Hash, Array, or nil
71
67
  #
68
+ # @param method [String] the name of the method to be invoked
69
+ # @param params [Hash, Array, nil] the parameters to be used during method invocation
70
+ #
72
71
  def initialize(method:, params: nil)
73
72
  @jsonrpc = '2.0'
74
73
 
@@ -117,10 +116,10 @@ module JSONRPC
117
116
  #
118
117
  # @api private
119
118
  #
120
- # @param method [String] the method name
121
- #
122
119
  # @raise [ArgumentError] if method is not a String or is reserved
123
120
  #
121
+ # @param method [String] the method name
122
+ #
124
123
  # @return [void]
125
124
  #
126
125
  def validate_method(method)
@@ -135,10 +134,10 @@ module JSONRPC
135
134
  #
136
135
  # @api private
137
136
  #
138
- # @param params [Hash, Array, nil] the parameters
139
- #
140
137
  # @raise [ArgumentError] if params is not a Hash, Array, or nil
141
138
  #
139
+ # @param params [Hash, Array, nil] the parameters
140
+ #
142
141
  # @return [void]
143
142
  #
144
143
  def validate_params(params)