calimero 0.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 70d04b0fe2325e620f6ec464863c5ad0d631c4ab3fac3c64e0833588de23adf1
4
+ data.tar.gz: 5771da472ba8150018bda106145e13e60707962a7b20d0a3c6be3c601f92dbdd
5
+ SHA512:
6
+ metadata.gz: be4b26a632b5d9c73647d15e08622d64195ff9b665d979b7f3656a2c9f79ad9ffe48b2c04f8828587e9940746a84404aa0f2a9ae72866fd5fccc0e5ec125fb78
7
+ data.tar.gz: f1824a22ed7201565be9f76f2c8e1ff218755874ee1ff185c562180a2b47ba262629eafcb109014ad849c4bb485bfcaa2e6c34c40cb5e9291c8b85dc05c086a1
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ * Kirill Abramov <septengineering@pm.me>
data/CHANGES.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## 0.2.0 - 2025-03-18
9
+
10
+ * Implement `Config` to manage config of Calimero nodes.
11
+ * Implement `Ed25519Keypair` to support identity keypairs and other keys used in Calimero.
12
+ * Implement authentication using Calimero identity key for interaction with the JSONRPC dev endpoint.
13
+ * Add tests for `Config` and `Ed25519Keypair` classes.
14
+ * Improve smoke tests.
15
+ * Update README with more examples.
16
+
17
+ ## 0.1.0 - 2025-03-11
18
+
19
+ ## 0.0.0 - 2025-03-08
20
+
data/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # Calimero Network for Ruby
2
+
3
+ [![License](https://img.shields.io/badge/license-Public%20Domain-blue.svg)](https://unlicense.org)
4
+ [![Compatibility](https://img.shields.io/badge/ruby-3.0%2B-blue)](https://rubygems.org/gems/calimero)
5
+ [![Package](https://img.shields.io/gem/v/calimero)](https://rubygems.org/gems/calimero)
6
+ [![Documentation](https://img.shields.io/badge/rubydoc-latest-blue)](https://rubydoc.info/gems/calimero)
7
+
8
+ **Calimero.rb** is a [Ruby] client library for the [Calimero Network].
9
+
10
+ > [!TIP]
11
+ > 🚧 _We are building in public. This is presently under heavy construction._
12
+
13
+ ## ✨ Features
14
+
15
+ - Implemented natively in Ruby with minimal dependencies, ensuring low overhead and efficient performance.
16
+ - Implements a `JsonRpcClient` for sending queries and updates to the applications in Calimero nodes.
17
+ - Handles write and read calls to Calimero network applications.
18
+ - Handles config management of Calimero nodes.
19
+ - Manages authentication workflow using Ed25519-keypair.
20
+ - 🚧 Manages authentication workflow using token acquisitions and refresh.
21
+ - 🚧Implements a `WsSubscriptionsClient` for subscribing to real-time updates from the Calimero nodes.
22
+ - 🚧 Supports interaction with Calimero Admin and Calimero Node APIs.
23
+ - Adheres to the Ruby API Guidelines in its [naming conventions].
24
+ - 100% free and unencumbered public domain software.
25
+
26
+ ## 🛠️ Prerequisites
27
+
28
+ - [Ruby] 3.0+
29
+
30
+ ## ⬇️ Installation
31
+
32
+ ### Installation via RubyGems
33
+
34
+ ```bash
35
+ gem install calimero
36
+ ```
37
+
38
+ ## 👉 Examples
39
+
40
+ ### Importing the library
41
+
42
+ ```ruby
43
+ require 'calimero'
44
+ ```
45
+
46
+ ### Loading the Calimero config
47
+
48
+ You can load a Calimero config file the following way:
49
+
50
+ ```ruby
51
+ require 'calimero'
52
+
53
+ config_path = "/path/to/your/calimero/config.toml"
54
+ config = Calimero::load_config(config_path)
55
+ ```
56
+
57
+ If you would like to utilize the default Calimero config folder:
58
+ ```ruby
59
+ require 'calimero'
60
+
61
+ config_path = "#{Calimero::default_config_folder}/node1/config.toml"
62
+ config = Calimero::load_config(config_path)
63
+ ```
64
+
65
+ ### Importing `Ed25519Keypair` from the config and signing an arbitrary message with it
66
+
67
+ ```ruby
68
+ require 'calimero'
69
+
70
+ config_path = "#{Calimero::default_config_folder}/node1/config.toml"
71
+ config = Calimero::load_config(config_path)
72
+
73
+ message = "Hello, Calimero"
74
+ signature = config.keypair.sign(message)
75
+ ```
76
+
77
+ ### Importing `Ed25519Keypair` from base58-encoded protobuf message and signing an arbitrary message with it
78
+
79
+ ```ruby
80
+ require 'calimero'
81
+
82
+ # The keypair should be base58-encoded protobuf message (using `libp2p_identity::Keypair`)
83
+ keypair_base58_protobuf = "<YOUR_BASE58_ENCODED_ED25519_KEYPAIR>"
84
+ keypair = Ed25519Keypair.new(keypair_base58_protobuf)
85
+ message = "Hello, Calimero"
86
+ signature = keypair.sign(message)
87
+ ```
88
+
89
+ ### Executing arbitrary method in Calimero Application with authentication using dev JSONRPC endpoint
90
+
91
+ ```ruby
92
+ require 'calimero'
93
+ require 'base58'
94
+
95
+ client = JsonRpcClient.new('http://localhost:2428', '/jsonrpc/dev')
96
+ params = RpcQueryParams.new('your_application_context_id', 'some_method', { 'some': 'args' }, 'executor_public_key')
97
+
98
+ config_path = "#{Calimero::default_config_folder}/node1/config.toml"
99
+ config = Calimero::load_config(config_path)
100
+
101
+ timestamp = Time.now.utc.to_i.to_s
102
+ signature = config.keypair.sign(timestamp)
103
+ signature_b58 = Base58.binary_to_base58(signature, :bitcoin)
104
+
105
+ headers = {
106
+ 'Content-Type' => 'application/json',
107
+ 'X-Signature' => signature_b58,
108
+ 'X-Timestamp' => timestamp
109
+ }
110
+ request_config = RequestConfig.new(timeout: 1000, headers: headers)
111
+ result = client.execute(query_params, request_config)
112
+ if result.error
113
+ puts "Error: #{result.error}"
114
+ else
115
+ puts "Result: #{result.result}"
116
+ end
117
+ ```
118
+
119
+ ### Executing arbitrary method in Calimero Application
120
+
121
+ ```ruby
122
+ require 'calimero'
123
+
124
+ client = JsonRpcClient.new('http://localhost:2428', '/jsonrpc')
125
+ params = RpcQueryParams.new('your_application_context_id', 'some_method', { 'some': 'args' }, 'executor_public_key')
126
+ bearer_auth_token = "some bearer auth token"
127
+ headers = {
128
+ 'Content-Type' => 'application/json',
129
+ 'Authorization' => "Bearer #{bearer_auth_token}"
130
+ }
131
+ request_config = RequestConfig.new(timeout: 1000, headers: headers)
132
+ result = client.execute(params, request_config)
133
+ if result.error
134
+ puts "Error: #{result.error}"
135
+ else
136
+ puts "Result: #{result.result}"
137
+ end
138
+ ```
139
+
140
+ ### Fetching all posts from OnlyPeers application
141
+
142
+ You can query all the posts in the given [OnlyPeers] demo application, by using the following example:
143
+ ```sh
144
+ CONTEXT_ID=<ONLYPEERS_CONTEXT_ID> EXECUTOR_PUBLIC_KEY=<YOUR_EXECUTOR_PUBLIC_KEY> ruby examples/onlypeers_get_all_posts.rb
145
+ ```
146
+
147
+ That example also contains an example on how to use the `Config` and `Ed25519Keypair` to authenticate your requests to the Calimero node.
148
+
149
+ ## 📚 Reference
150
+
151
+ https://rubydoc.info/gems/calimero
152
+
153
+ ## 👨‍💻 Development
154
+
155
+ ```bash
156
+ git clone https://github.com/dryruby/calimero.rb.git
157
+ ```
158
+
159
+ - - -
160
+
161
+ [![Share on Twitter](https://img.shields.io/badge/share%20on-twitter-03A9F4?logo=twitter)](https://x.com/share?url=https://github.com/dryruby/calimero.rb&text=calimero.rb)
162
+ [![Share on Reddit](https://img.shields.io/badge/share%20on-reddit-red?logo=reddit)](https://reddit.com/submit?url=https://github.com/dryruby/calimero.rb&title=calimero.rb)
163
+ [![Share on Hacker News](https://img.shields.io/badge/share%20on-hacker%20news-orange?logo=ycombinator)](https://news.ycombinator.com/submitlink?u=https://github.com/dryruby/calimero.rb&t=calimero.rb)
164
+ [![Share on Facebook](https://img.shields.io/badge/share%20on-facebook-1976D2?logo=facebook)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/dryruby/calimero.rb)
165
+
166
+ [Calimero Network]: https://calimero.network/
167
+ [Ruby]: https://ruby-lang.org
168
+ [OnlyPeers]: https://calimero-network.github.io/tutorials/awesome-projects/only-peers/
data/UNLICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <https://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,48 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ require 'toml-rb'
4
+ require_relative '../types/keypair'
5
+
6
+ class ConfigError < StandardError; end
7
+
8
+ # Configuration class that holds a Keypair and is extensible for future for other fields
9
+ class Config
10
+ attr_reader :keypair
11
+
12
+ # Initialize with a TOML file path
13
+ def initialize(file_path)
14
+ @config_data = load_toml(file_path)
15
+ keypair_value = @config_data.dig('identity', 'keypair')
16
+ raise ConfigError, "'keypair' not found in [identity] section" unless keypair_value
17
+ @keypair = Ed25519Keypair.new(keypair_value)
18
+ end
19
+
20
+ # Allow dynamic access to raw config data
21
+ def [](key)
22
+ @config_data[key]
23
+ end
24
+
25
+ # Extend config with additional fields in the future
26
+ def method_missing(method_name, *args, &block)
27
+ if @config_data.key?(method_name.to_s)
28
+ @config_data[method_name.to_s]
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def respond_to_missing?(method_name, include_private = false)
35
+ @config_data.key?(method_name.to_s) || super
36
+ end
37
+
38
+ def load_toml(file_path)
39
+ TomlRB.load_file(file_path)
40
+ rescue Errno::ENOENT
41
+ raise ConfigError, "Config file '#{file_path}' not found"
42
+ rescue TomlRB::ParseError => e
43
+ raise ConfigError, "Failed to parse TOML file: #{e.message}"
44
+ end
45
+
46
+ private :load_toml
47
+ end
48
+
@@ -0,0 +1,3 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ require_relative 'config/config'
@@ -0,0 +1,154 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'json'
4
+
5
+ require_relative 'types/rpc'
6
+
7
+ module HTTPStatusCodes
8
+ HTTPOK = 200
9
+ HTTPBadRequest = 400
10
+ HTTPInternalServerError = 500
11
+ end
12
+
13
+ class JsonRpcClient < RpcClient
14
+ attr_reader :path, :base_url, :default_timeout
15
+
16
+ def initialize(base_url, path, default_timeout = 1000)
17
+ @base_url = base_url
18
+ @path = path
19
+ @default_timeout = default_timeout
20
+ end
21
+
22
+ def execute(params, config = RequestConfig.new(timeout: default_timeout))
23
+ request('execute', params, config)
24
+ end
25
+
26
+ def request(method, params, config = RequestConfig.new(timeout: default_timeout))
27
+ request_id = get_random_request_id
28
+ data = {
29
+ jsonrpc: '2.0',
30
+ id: request_id,
31
+ method: method,
32
+ params: params.instance_variables.each_with_object({}) { |var, hash| hash[var.to_s.delete('@')] = params.instance_variable_get(var) }
33
+ }
34
+
35
+ uri = URI.parse("#{@base_url}#{@path}")
36
+ http = Net::HTTP.new(uri.host, uri.port)
37
+ http.use_ssl = uri.scheme == 'https'
38
+ http.open_timeout = config.timeout || @default_timeout
39
+ http.read_timeout = config.timeout || @default_timeout
40
+ headers = {'Content-Type' => 'application/json'}.merge(config.headers ? config.headers : {})
41
+
42
+ begin
43
+ response = http.post(uri.path, data.to_json, headers)
44
+ parsed_response = JSON.parse(response.body)
45
+
46
+ if response.is_a?(Net::HTTPOK)
47
+ if parsed_response['id'] != request_id
48
+ return RpcResult.new(result: nil, error: {
49
+ code: HTTPStatusCodes::HTTPBadRequest,
50
+ id: parsed_response['id'],
51
+ jsonrpc: parsed_response['jsonrpc'],
52
+ error: {
53
+ name: 'MissmatchedRequestIdError',
54
+ cause: {
55
+ name: 'MissmatchedRequestIdError',
56
+ info: {
57
+ message: "Missmatched RequestId expected #{request_id}, got #{parsed_response['id']}"
58
+ }
59
+ }
60
+ }
61
+ })
62
+ end
63
+
64
+ error_data = parsed_response['error']
65
+ #TODO figure out if there are still weird use cases where error_data['data']['data'] might not be a Hash, but a String
66
+ if error_data
67
+ error_cause_name = if error_data['data'].is_a?(Hash)
68
+ error_data.dig('data', 'type')
69
+ else
70
+ error_data['type']
71
+ end
72
+ error_message = if error_data['data'].is_a?(Hash) && error_data['data']['data'].is_a?(Hash)
73
+ error_data.dig('data', 'data', 'type')
74
+ else
75
+ error_data['data']
76
+ end
77
+ return RpcResult.new(result: nil, error: {
78
+ code: HTTPStatusCodes::HTTPBadRequest,
79
+ id: parsed_response['id'],
80
+ jsonrpc: parsed_response['jsonrpc'],
81
+ error: {
82
+ name: error_data['type'],
83
+ cause: {
84
+ name: error_cause_name,
85
+ info: {
86
+ message: error_message
87
+ }
88
+ }
89
+ }
90
+ })
91
+ end
92
+
93
+ return RpcResult.new(result: parsed_response['result'], error: nil)
94
+ else
95
+ error_data = parsed_response['error']
96
+ error_message = if error_data['data'].is_a?(Hash) && error_data['data']['data'].is_a?(Hash)
97
+ error_data.dig('data', 'data', 'type')
98
+ else
99
+ error_data['data']
100
+ end
101
+ return RpcResult.new(result: nil, error: {
102
+ id: parsed_response['id'],
103
+ jsonrpc: parsed_response['jsonrpc'],
104
+ code: response.code.to_i,
105
+ error: {
106
+ name: 'InvalidRequestError',
107
+ cause: {
108
+ name: 'InvalidRequestError',
109
+ info: {
110
+ message: error_message
111
+ }
112
+ }
113
+ }
114
+ })
115
+ end
116
+ rescue JSON::ParserError
117
+ return RpcResult.new(result: nil, error: {
118
+ id: request_id,
119
+ jsonrpc: '2.0',
120
+ code: HTTPStatusCodes::HTTPInternalServerError,
121
+ error: {
122
+ name: 'InvalidJsonResponseError',
123
+ cause: {
124
+ name: 'InvalidJsonResponseError',
125
+ info: {
126
+ message: "Invalid JSON response from server."
127
+ }
128
+ }
129
+ }
130
+ })
131
+ rescue StandardError => e
132
+ return RpcResult.new(result: nil, error: {
133
+ id: request_id,
134
+ jsonrpc: '2.0',
135
+ code: HTTPStatusCodes::HTTPInternalServerError,
136
+ error: {
137
+ name: 'UnknownServerError',
138
+ cause: {
139
+ name: 'UnknownServerError',
140
+ info: {
141
+ message: e.message
142
+ }
143
+ }
144
+ }
145
+ })
146
+ end
147
+ end
148
+
149
+ def get_random_request_id
150
+ rand(2**32)
151
+ end
152
+
153
+ private :request, :get_random_request_id
154
+ end
@@ -0,0 +1,4 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ # ContextId is a string.
4
+ ContextId = String
@@ -0,0 +1,74 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ require 'base58'
4
+ require 'ed25519'
5
+
6
+ class KeypairError < StandardError; end
7
+
8
+ class Ed25519Keypair
9
+ attr_reader :private_key, :public_key, :stored_public_key
10
+
11
+ # Expected protobuf prefix for Ed25519 keypair (type: 1, data length: 64)
12
+ # The implementation is used for compatibility with Ed25519 keypair from
13
+ # [libp2p_identity/keypair](https://github.com/libp2p/rust-libp2p/blob/88f7875ad1a3e240aa2d9b9fb6f6c5354f1a62eb/identity/src/keypair.rs#L262)
14
+ PROTOBUF_PREFIX = "\x08\x01\x12\x40".freeze
15
+
16
+ # Initialize with a Base58-encoded keypair string
17
+ def initialize(base58_keypair)
18
+ raise KeypairError, "Base58 keypair cannot be nil" if base58_keypair.nil?
19
+ @key_bytes = decode_base58(base58_keypair)
20
+ validate_keypair_length
21
+ validate_protobuf_prefix
22
+ extract_keys
23
+ initialize_signing_key
24
+ end
25
+
26
+ # Sign a message (as raw bytes)
27
+ def sign(message)
28
+ @signing_key.sign(message)
29
+ end
30
+
31
+ # Verify a signature against a message
32
+ def verify(signature, message)
33
+ @verify_key.verify(signature, message)
34
+ true
35
+ rescue Ed25519::VerifyError
36
+ false
37
+ end
38
+
39
+ def decode_base58(base58_keypair)
40
+ Base58.base58_to_binary(base58_keypair, :bitcoin)
41
+ rescue StandardError => e
42
+ raise KeypairError, "Failed to decode Base58 keypair: #{e.message}"
43
+ end
44
+
45
+ def validate_keypair_length
46
+ return if @key_bytes.length == 68
47
+ raise KeypairError, "Unexpected keypair length: #{@key_bytes.length} bytes (expected 68)"
48
+ end
49
+
50
+ def validate_protobuf_prefix
51
+ prefix = @key_bytes[0..3]
52
+ unless prefix == PROTOBUF_PREFIX
53
+ raise KeypairError, "Invalid protobuf prefix: #{prefix.unpack1('H*')} (expected #{PROTOBUF_PREFIX.unpack1('H*')})"
54
+ end
55
+ end
56
+
57
+ def extract_keys
58
+ @private_key = @key_bytes[4..35] # 32-byte private key
59
+ @stored_public_key = @key_bytes[36..67] # 32-byte stored public key
60
+ unless @private_key.length == 32 && @stored_public_key.length == 32
61
+ raise KeypairError, "Invalid key lengths: private #{@private_key.length}, stored public #{@stored_public_key.length} (expected 32 each)"
62
+ end
63
+ end
64
+
65
+ def initialize_signing_key
66
+ @signing_key = Ed25519::SigningKey.new(@private_key)
67
+ @verify_key = @signing_key.verify_key
68
+ @public_key = @verify_key.to_bytes
69
+ rescue ArgumentError => e
70
+ raise KeypairError, "Invalid Ed25519 private key: #{e.message}"
71
+ end
72
+
73
+ private :decode_base58, :validate_keypair_length, :extract_keys, :initialize_signing_key
74
+ end
@@ -0,0 +1,102 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ require_relative 'context'
4
+ require_relative 'rpc_request'
5
+
6
+ class RpcError < StandardError
7
+ attr_reader :id, :jsonrpc, :code, :error_info
8
+
9
+ def initialize(id, jsonrpc, code, error_info)
10
+ @id = id
11
+ @jsonrpc = jsonrpc
12
+ @code = code
13
+ @error_info = error_info
14
+ super(error_info[:message])
15
+ end
16
+ end
17
+
18
+ class RpcErrorInfo < StandardError
19
+ attr_reader :name, :cause
20
+
21
+ def initialize(name, cause)
22
+ @name = name
23
+ @cause = cause
24
+ super(cause[:message])
25
+ end
26
+ end
27
+
28
+ class RpcCauseInfo < StandardError
29
+ attr_reader :name, :info
30
+
31
+ def initialize(name, info)
32
+ @name = name
33
+ @info = info
34
+ super(info[:message])
35
+ end
36
+ end
37
+
38
+ class RpcClient
39
+ def execute(params, config = {})
40
+ raise NotImplementedError, "Subclasses must implement execute"
41
+ end
42
+ end
43
+
44
+ class RequestConfig
45
+ attr_accessor :timeout, :headers
46
+
47
+ def initialize(timeout: nil, headers: nil)
48
+ @timeout = timeout
49
+ @headers = headers
50
+ end
51
+
52
+ def to_h
53
+ {
54
+ timeout: @timeout,
55
+ headers: @headers
56
+ }
57
+ end
58
+ end
59
+
60
+ class RpcResult
61
+ attr_accessor :result, :error
62
+
63
+ def initialize(result: nil, error: nil)
64
+ @result = result
65
+ @error = error
66
+ end
67
+
68
+ def to_h
69
+ {
70
+ result: @result,
71
+ error: @error,
72
+ }
73
+ end
74
+ end
75
+
76
+ class RpcQueryParams
77
+ attr_accessor :contextId, :method, :argsJson, :executorPublicKey
78
+
79
+ def initialize(contextId, method, argsJson, executorPublicKey)
80
+ @contextId = contextId
81
+ @method = method
82
+ @argsJson = argsJson
83
+ @executorPublicKey = executorPublicKey
84
+ end
85
+
86
+ def to_h
87
+ {
88
+ contextId: @contextId,
89
+ method: @method,
90
+ argsJson: @argsJson,
91
+ executorPublicKey: @executorPublicKey
92
+ }
93
+ end
94
+ end
95
+
96
+ class RpcQueryResponse
97
+ attr_accessor :output
98
+
99
+ def initialize(output: nil)
100
+ @output = output
101
+ end
102
+ end
@@ -0,0 +1,7 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ module RpcRequestId
4
+ def self.valid?(value)
5
+ value.is_a?(String) || value.is_a?(Numeric)
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ require_relative 'types/rpc'
4
+ require_relative 'types/keypair'
@@ -0,0 +1 @@
1
+ # This is free and unencumbered software released into the public domain.
data/lib/calimero.rb ADDED
@@ -0,0 +1,30 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ module Calimero; end
4
+
5
+ require_relative 'calimero/config'
6
+ require_relative 'calimero/jsonrpc'
7
+ require_relative 'calimero/types'
8
+ require_relative 'calimero/version'
9
+
10
+ module Calimero
11
+ ##
12
+ # @return [Calimero::default_rpc_url]
13
+ def self.default_rpc_url
14
+ @rpc_url ||= "http://127.0.0.1:2428"
15
+ end
16
+
17
+ ##
18
+ # @return [Calimero::default_config_path]
19
+ def self.default_config_folder
20
+ @config_path ||= "#{Dir.home}/.calimero"
21
+ end
22
+
23
+ ##
24
+ # @return [Calimero::load_config]
25
+ # Utility method to load config from TOML file
26
+ def self.load_config(file_path)
27
+ Config.new(file_path)
28
+ end
29
+ end
30
+
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: calimero
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Kirill Abramov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-03-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: tempfile
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.3.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.3.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: net-http
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.4.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.4.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: uri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.10.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.10.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: json
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.10'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.10'
97
+ - !ruby/object:Gem::Dependency
98
+ name: toml-rb
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 3.0.1
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 3.0.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: ed25519
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.3.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.3.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: base58
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.2.3
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.2.3
139
+ description: A Ruby client library for the Calimero Network.
140
+ email: septengineering@pm.me
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - AUTHORS
146
+ - CHANGES.md
147
+ - README.md
148
+ - UNLICENSE
149
+ - VERSION
150
+ - lib/calimero.rb
151
+ - lib/calimero/config.rb
152
+ - lib/calimero/config/config.rb
153
+ - lib/calimero/jsonrpc.rb
154
+ - lib/calimero/types.rb
155
+ - lib/calimero/types/context.rb
156
+ - lib/calimero/types/keypair.rb
157
+ - lib/calimero/types/rpc.rb
158
+ - lib/calimero/types/rpc_request.rb
159
+ - lib/calimero/version.rb
160
+ homepage: https://github.com/dryruby/calimero.rb
161
+ licenses:
162
+ - Unlicense
163
+ metadata:
164
+ bug_tracker_uri: https://github.com/dryruby/calimero.rb/issues
165
+ changelog_uri: https://github.com/dryruby/calimero.rb/blob/master/CHANGES.md
166
+ documentation_uri: https://rubydoc.info/gems/calimero
167
+ homepage_uri: https://github.com/dryruby/calimero.rb
168
+ source_code_uri: https://github.com/dryruby/calimero.rb
169
+ post_install_message:
170
+ rdoc_options: []
171
+ require_paths:
172
+ - lib
173
+ required_ruby_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '3.0'
178
+ required_rubygems_version: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ requirements: []
184
+ rubygems_version: 3.0.3.1
185
+ signing_key:
186
+ specification_version: 4
187
+ summary: 'Calimero.rb: Calimero Network for Ruby'
188
+ test_files: []