clamo 0.8.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97084b2761bd6af3549fd2444f981da0a23ce6f1ebbc740f4c2fb237e90ee1fe
4
- data.tar.gz: dde0cc4f7187bc58aeb2d1be4169f3cc6331d4008c7a10539317cb80e2a68480
3
+ metadata.gz: e1a61a818aab987fe6d621d2f3bf642fd7993044933b0b258f2a515d896bb7a5
4
+ data.tar.gz: b68b313c737400cda3d739902d1d2d498a2d28344cf9cc0afc5c8b481850bcb5
5
5
  SHA512:
6
- metadata.gz: 18a87542a9340025ff18f60594ae32e941bca85f4f268c2dd99ca207dc2c4296c74f2809531e7e43034f27219b4d43fec4f380f61def8e37af6477fa979c273e
7
- data.tar.gz: 976d81d554ff643ddfcbbaa5185094fda802657b5adee586d60e7c31e7aee27b0f6f141263f23dc8ebbc4d7b0a288beb3e1dc78a41b4209c18c266f2c64931a8
6
+ metadata.gz: b2a1ac30f5f40c035d683d54a36c0164ea24dc56b8ca9af694de499862732faf22cc572029e6bfac1f05413c4d4cf56a737b9b29af96b870fcf23eee5305e756
7
+ data.tar.gz: ccb5a26f16ab0492e3d77e27eeed648e26b6fdcb9bda59c37a264cc3e419ba45a77bede078863e9ea18dface1f9f5c0be13a1629e6b37d4a8fb2da2f8cc291b4
data/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [0.9.0] - 2026-03-14
8
+
9
+ ### Changed
10
+
11
+ - **Breaking:** All response and request builder hashes now use string keys (`"jsonrpc"`, `"result"`, `"id"`, `"error"`) instead of symbol keys. This makes the entire pipeline consistent — `JSON.parse` produces string keys, `normalize_request_keys` uses string keys, and now responses match. Callers using the lower-level `parsed_dispatch_to_object` or `unparsed_dispatch_to_object` must update hash access from `response[:result]` to `response["result"]`. The `handle` method (JSON string in/out) is unaffected.
12
+ - `after_dispatch` hook now receives the actual return value for notifications. Previously always passed `nil`; now passes the value returned by the dispatched method.
13
+
14
+ ### Fixed
15
+
16
+ - **Security:** `method_known?` no longer exposes inherited `Module` methods (`define_method`, `class_eval`, `const_set`, `freeze`, `include`, and 55 others) on module-based service objects. The previous `public_methods(false)` implementation included these; the new `public_method_defined?` check restricts dispatch to methods explicitly defined on the service object.
17
+
18
+ ### Internal
19
+
20
+ - `method_known?` replaced array-allocating `public_methods(false).map(&:to_sym).include?` with zero-allocation `public_method_defined?` lookups. Handles both module targets (singleton class) and class instance targets (class + singleton class).
21
+
7
22
  ## [0.8.0] - 2026-03-14
8
23
 
9
24
  ### Added
data/README.md CHANGED
@@ -43,7 +43,7 @@ response = Clamo::Server.unparsed_dispatch_to_object(
43
43
  request: '{"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": 1}',
44
44
  object: MyService
45
45
  )
46
- # => {jsonrpc: "2.0", result: 3, id: 1}
46
+ # => {"jsonrpc" => "2.0", "result" => 3, "id" => 1}
47
47
 
48
48
  # From a pre-parsed hash
49
49
  response = Clamo::Server.parsed_dispatch_to_object(
@@ -82,7 +82,7 @@ batch_response = Clamo::Server.unparsed_dispatch_to_object(
82
82
  )
83
83
 
84
84
  puts batch_response
85
- # => [{jsonrpc: "2.0", result: 3, id: 1}, {jsonrpc: "2.0", result: 2, id: 2}]
85
+ # => [{"jsonrpc" => "2.0", "result" => 3, "id" => 1}, {"jsonrpc" => "2.0", "result" => 2, "id" => 2}]
86
86
  ```
87
87
 
88
88
  ### Notifications
@@ -112,7 +112,7 @@ request = Clamo::JSONRPC.build_request(
112
112
  )
113
113
 
114
114
  puts request
115
- # => {jsonrpc: "2.0", method: "add", params: [1, 2], id: 1}
115
+ # => {"jsonrpc" => "2.0", "method" => "add", "params" => [1, 2], "id" => 1}
116
116
  ```
117
117
 
118
118
  ## Error Handling
data/lib/clamo/jsonrpc.rb CHANGED
@@ -35,26 +35,26 @@ module Clamo
35
35
 
36
36
  validate_params_type!(opts[:params]) if opts.key?(:params)
37
37
 
38
- { jsonrpc: "2.0", method: opts[:method] }
39
- .then { |r| opts.key?(:params) ? r.merge(params: opts[:params]) : r }
40
- .then { |r| opts.key?(:id) ? r.merge(id: opts[:id]) : r }
38
+ { "jsonrpc" => "2.0", "method" => opts[:method] }
39
+ .then { |r| opts.key?(:params) ? r.merge("params" => opts[:params]) : r }
40
+ .then { |r| opts.key?(:id) ? r.merge("id" => opts[:id]) : r }
41
41
  end
42
42
 
43
43
  def build_result_response(id:, result:)
44
- { jsonrpc: "2.0", result: result, id: id }
44
+ { "jsonrpc" => "2.0", "result" => result, "id" => id }
45
45
  end
46
46
 
47
47
  def build_error_response(**opts)
48
48
  raise ArgumentError, "error code is required" unless opts.dig(:error, :code)
49
49
  raise ArgumentError, "error message is required" unless opts.dig(:error, :message)
50
50
 
51
- { jsonrpc: "2.0",
52
- id: opts[:id],
53
- error: {
54
- code: opts.dig(:error, :code),
55
- message: opts.dig(:error, :message),
56
- data: opts.dig(:error, :data)
57
- }.reject { |k, _| k == :data && !opts[:error].key?(:data) } }
51
+ { "jsonrpc" => "2.0",
52
+ "id" => opts[:id],
53
+ "error" => {
54
+ "code" => opts.dig(:error, :code),
55
+ "message" => opts.dig(:error, :message),
56
+ "data" => opts.dig(:error, :data)
57
+ }.reject { |k, _| k == "data" && !opts[:error].key?(:data) } }
58
58
  end
59
59
 
60
60
  def build_error_response_from(descriptor:, id: nil)
data/lib/clamo/server.rb CHANGED
@@ -74,7 +74,13 @@ module Clamo
74
74
  end
75
75
 
76
76
  def method_known?(object:, method:)
77
- object.public_methods(false).map(&:to_sym).include?(method.to_sym)
77
+ name = method.to_sym
78
+ if object.is_a?(Module)
79
+ object.singleton_class.public_method_defined?(name, false)
80
+ else
81
+ object.class.public_method_defined?(name, false) ||
82
+ object.singleton_class.public_method_defined?(name, false)
83
+ end
78
84
  end
79
85
 
80
86
  def dispatch_to_ruby(object:, method:, params:)
@@ -152,8 +158,8 @@ module Clamo
152
158
  method = request["method"]
153
159
  params = request["params"]
154
160
  config.before_dispatch&.call(method, params)
155
- with_timeout(config.timeout) { block.yield(method, params) }
156
- config.after_dispatch&.call(method, params, nil)
161
+ result = with_timeout(config.timeout) { block.yield(method, params) }
162
+ config.after_dispatch&.call(method, params, result)
157
163
  nil
158
164
  rescue StandardError => e
159
165
  config.on_error&.call(e, method, params)
data/lib/clamo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Clamo
4
- VERSION = "0.8.0"
4
+ VERSION = "0.9.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clamo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andriy Tyurnikov