axonbase-sdk 0.1.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 +7 -0
- data/lib/axonbase/client.rb +142 -0
- data/lib/axonbase/errors.rb +34 -0
- data/lib/axonbase/migration.rb +108 -0
- data/lib/axonbase/saga_participant_transaction.rb +58 -0
- data/lib/axonbase/saga_transaction.rb +70 -0
- data/lib/axonbase/transport.rb +28 -0
- data/lib/axonbase.rb +14 -0
- metadata +65 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f2af53f84385eb0aa349e48950a5ab6e91d8368a918028127f9cd254ec030b14
|
|
4
|
+
data.tar.gz: db4a5d54e6cedf4a7af3cac044dc40c4e352ada002186d2e69b51f561fb8593d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 37c471b91d5be964be66b5267cc747209a68aad3ca61568a42422633f3c6cd9eb5ea03e5d81d00646a8cd24eade0dc190dcef74100b4146766d2fe4e7ef9a780
|
|
7
|
+
data.tar.gz: bf67a5d343902ed310a1485a43b36b9c710a1e057aa2595d18ce9ba1b3a6741e04dc8d04063f88a7ffdb9ad6c0b9c4a1d6867814fa3536c9a4701bfe5309915d
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AxonBase
|
|
4
|
+
class Client
|
|
5
|
+
attr_reader :namespace, :database, :hello
|
|
6
|
+
|
|
7
|
+
def self.connect(url, ssl_context: nil)
|
|
8
|
+
new(WebSocketTransport.new(url, ssl_context: ssl_context))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(transport)
|
|
12
|
+
@transport = transport
|
|
13
|
+
@next_id = 1
|
|
14
|
+
@live_handlers = {}
|
|
15
|
+
frame = decode(@transport.receive)
|
|
16
|
+
raise Error, "expected hello as first WebSocket frame" unless frame["hello"].is_a?(Hash)
|
|
17
|
+
@hello = frame["hello"]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def use(namespace, database)
|
|
21
|
+
result = call("use", [namespace, database])
|
|
22
|
+
@namespace = result.fetch("namespace", namespace)
|
|
23
|
+
@database = result.fetch("database", database)
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def signin(user, pass, access = nil)
|
|
28
|
+
credentials = { "user" => user, "pass" => pass }
|
|
29
|
+
credentials["access"] = access unless access.nil?
|
|
30
|
+
@token = call("signin", [credentials])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def authenticate(token)
|
|
34
|
+
call("authenticate", [token])
|
|
35
|
+
@token = token
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def query(sql, vars = nil)
|
|
40
|
+
call("query", vars.nil? ? [sql] : [sql, vars])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def select(target, where = nil); call("select", where.nil? ? [target] : [target, where]); end
|
|
44
|
+
def create(target, data); call("create", [target, data]); end
|
|
45
|
+
def insert(table, data); call("insert", [table, data]); end
|
|
46
|
+
def update(target, data); call("update", [target, data]); end
|
|
47
|
+
def upsert(target, data); call("upsert", [target, data]); end
|
|
48
|
+
def delete(target, where = nil); call("delete", where.nil? ? [target] : [target, where]); end
|
|
49
|
+
def ping; call("ping", []); end
|
|
50
|
+
def version; call("version", []); end
|
|
51
|
+
def close; @transport.close; end
|
|
52
|
+
|
|
53
|
+
def relate(from, kind, to, data = {})
|
|
54
|
+
call("relate", [from, kind, to, data])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def let_var(name, value)
|
|
58
|
+
clean = name.start_with?("$") ? name[1..] : name
|
|
59
|
+
call("let", [clean, value])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def unset(name)
|
|
63
|
+
clean = name.start_with?("$") ? name[1..] : name
|
|
64
|
+
call("unset", [clean])
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def begin; call("begin", []); end
|
|
68
|
+
def commit; call("commit", []); end
|
|
69
|
+
def cancel; call("cancel", []); end
|
|
70
|
+
|
|
71
|
+
def kv_get(ns, db, key); call("kv_get", [ns, db, key]); end
|
|
72
|
+
def kv_set(ns, db, key, value, ttl = nil); call("kv_set", [ns, db, key, value, ttl]); end
|
|
73
|
+
def kv_del(ns, db, key); call("kv_del", [ns, db, key]); end
|
|
74
|
+
def kv_scan(ns, db, prefix); call("kv_scan", [ns, db, prefix]); end
|
|
75
|
+
|
|
76
|
+
def live(table, handler, diff = false)
|
|
77
|
+
id = call("live", [table, diff])
|
|
78
|
+
@live_handlers[id] = handler
|
|
79
|
+
id
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def kill(id)
|
|
83
|
+
ok = call("kill", [id])
|
|
84
|
+
@live_handlers.delete(id) if ok
|
|
85
|
+
ok
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def certificate_begin(store)
|
|
89
|
+
result = call("certificate.begin", [{ "store" => store }])
|
|
90
|
+
{
|
|
91
|
+
"id" => result["id"],
|
|
92
|
+
"challenge" => result["challenge"],
|
|
93
|
+
"expires_at" => result["expires_at"],
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def certificate_complete(completion_hash)
|
|
98
|
+
call("certificate.complete", [completion_hash])
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def call(method, params)
|
|
104
|
+
id = @next_id
|
|
105
|
+
@next_id += 1
|
|
106
|
+
@transport.send(JSON.generate("id" => id, "method" => method, "params" => params, "version" => PROTOCOL_VERSION))
|
|
107
|
+
loop do
|
|
108
|
+
response = decode(@transport.receive)
|
|
109
|
+
if response.key?("notification")
|
|
110
|
+
n = response["notification"]
|
|
111
|
+
handler = @live_handlers[n["id"]]
|
|
112
|
+
handler&.call(n["id"], n["action"], n["result"])
|
|
113
|
+
next
|
|
114
|
+
end
|
|
115
|
+
next if response["id"] != id
|
|
116
|
+
raise rpc_error(response["error"]) if response.key?("error")
|
|
117
|
+
return response["result"]
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def decode(frame)
|
|
122
|
+
JSON.parse(frame)
|
|
123
|
+
rescue JSON::ParserError
|
|
124
|
+
raise Error, "invalid JSON-RPC frame"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def rpc_error(error)
|
|
128
|
+
code, message = error.fetch("code", 0), error.fetch("message", "RPC error")
|
|
129
|
+
case code
|
|
130
|
+
when -32_002 then AuthError.new(message, code)
|
|
131
|
+
when -32_009 then ConflictError.new(message, code)
|
|
132
|
+
when -32_010 then NotLeaderError.new(message, error.fetch("leader", ""), error.fetch("leader_address", ""))
|
|
133
|
+
when -32_011 then NoQuorumError.new(message, code)
|
|
134
|
+
when -32_029 then RateLimitError.new(message, code)
|
|
135
|
+
when -32_600
|
|
136
|
+
return ProtocolMismatchError.new(message, error.fetch("protocol", 0), error.fetch("version", 0)) if error["kind"] == "PROTOCOL_MISMATCH"
|
|
137
|
+
Error.new(message, code)
|
|
138
|
+
else Error.new(message, code)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AxonBase
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
attr_reader :code
|
|
6
|
+
def initialize(message, code = nil)
|
|
7
|
+
super(message)
|
|
8
|
+
@code = code
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class AuthError < Error; end
|
|
13
|
+
class ConflictError < Error; end
|
|
14
|
+
class NoQuorumError < Error; end
|
|
15
|
+
class RateLimitError < Error; end
|
|
16
|
+
|
|
17
|
+
class NotLeaderError < Error
|
|
18
|
+
attr_reader :leader, :leader_address
|
|
19
|
+
def initialize(message, leader = "", leader_address = "")
|
|
20
|
+
super(message, -32_010)
|
|
21
|
+
@leader = leader
|
|
22
|
+
@leader_address = leader_address
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class ProtocolMismatchError < Error
|
|
27
|
+
attr_reader :protocol, :server_version
|
|
28
|
+
def initialize(message, protocol, server_version)
|
|
29
|
+
super(message, -32_600)
|
|
30
|
+
@protocol = protocol
|
|
31
|
+
@server_version = server_version
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
5
|
+
module AxonBase
|
|
6
|
+
Migration = Struct.new(:version, :name, :path, :checksum, :sql, keyword_init: true)
|
|
7
|
+
|
|
8
|
+
class Migrator
|
|
9
|
+
DEFAULT_TABLE = "_migration"
|
|
10
|
+
|
|
11
|
+
def initialize(axon, table: DEFAULT_TABLE)
|
|
12
|
+
@axon = axon
|
|
13
|
+
@table = table
|
|
14
|
+
@before_hooks = []
|
|
15
|
+
@after_hooks = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def on_before(&hook)
|
|
19
|
+
@before_hooks << hook
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def on_after(&hook)
|
|
24
|
+
@after_hooks << hook
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ensure_table
|
|
29
|
+
@axon.query("DEFINE TABLE #{@table} SCHEMAFULL;" \
|
|
30
|
+
"DEFINE FIELD version ON TABLE #{@table} TYPE string;" \
|
|
31
|
+
"DEFINE FIELD name ON TABLE #{@table} TYPE string;" \
|
|
32
|
+
"DEFINE FIELD checksum ON TABLE #{@table} TYPE string;" \
|
|
33
|
+
"DEFINE FIELD applied_at ON TABLE #{@table} TYPE datetime DEFAULT time::now();")
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def applied
|
|
38
|
+
rows = @axon.query("SELECT version, checksum FROM #{@table} ORDER BY version ASC;")
|
|
39
|
+
return {} unless rows.is_a?(Array)
|
|
40
|
+
|
|
41
|
+
rows.each_with_object({}) do |row, result|
|
|
42
|
+
next unless row.is_a?(Hash) && row.key?("version") && row.key?("checksum")
|
|
43
|
+
|
|
44
|
+
result[row["version"].to_s] = row["checksum"].to_s
|
|
45
|
+
end
|
|
46
|
+
rescue Error
|
|
47
|
+
{}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def load(path)
|
|
51
|
+
if File.file?(path)
|
|
52
|
+
raise ArgumentError, "not an .axql file: #{path}" unless path.end_with?(".axql")
|
|
53
|
+
|
|
54
|
+
return [parse_file(path)]
|
|
55
|
+
end
|
|
56
|
+
raise ArgumentError, "migration directory not found: #{path}" unless Dir.exist?(path)
|
|
57
|
+
|
|
58
|
+
Dir.glob(File.join(path, "*.axql")).sort.map { |file| parse_file(file) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def status(path)
|
|
62
|
+
completed = applied
|
|
63
|
+
load(path).map { |migration| { migration: migration, applied: completed.key?(migration.version) } }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def up(path)
|
|
67
|
+
completed = applied
|
|
68
|
+
pending = load(path).reject do |migration|
|
|
69
|
+
checksum = completed[migration.version]
|
|
70
|
+
next false if checksum.nil?
|
|
71
|
+
|
|
72
|
+
raise "migration checksum changed: #{migration.name}" unless checksum == migration.checksum
|
|
73
|
+
|
|
74
|
+
true
|
|
75
|
+
end
|
|
76
|
+
return [] if pending.empty?
|
|
77
|
+
|
|
78
|
+
ensure_table
|
|
79
|
+
pending.each do |migration|
|
|
80
|
+
@before_hooks.each { |hook| hook.call(migration) }
|
|
81
|
+
@axon.query(migration.sql) unless migration.sql.strip.empty?
|
|
82
|
+
@axon.query(
|
|
83
|
+
"UPSERT #{@table}:v_#{migration.version} CONTENT { version: $v, name: $n, checksum: $c, applied_at: time::now() }",
|
|
84
|
+
"v" => migration.version,
|
|
85
|
+
"n" => migration.name,
|
|
86
|
+
"c" => migration.checksum
|
|
87
|
+
)
|
|
88
|
+
@after_hooks.each { |hook| hook.call(migration) }
|
|
89
|
+
end
|
|
90
|
+
pending
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def parse_file(path)
|
|
96
|
+
name = File.basename(path, ".axql")
|
|
97
|
+
Migration.new(
|
|
98
|
+
version: name.split("_", 2).first,
|
|
99
|
+
name: name,
|
|
100
|
+
path: path,
|
|
101
|
+
checksum: Digest::SHA256.file(path).hexdigest,
|
|
102
|
+
sql: File.read(path)
|
|
103
|
+
)
|
|
104
|
+
rescue Errno::ENOENT, Errno::EACCES => error
|
|
105
|
+
raise "failed to read migration: #{path}: #{error.message}"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AxonBase
|
|
4
|
+
class SagaParticipantTransaction
|
|
5
|
+
attr_reader :correlation_id
|
|
6
|
+
|
|
7
|
+
def initialize(axon, correlation_id)
|
|
8
|
+
@axon = axon
|
|
9
|
+
@correlation_id = correlation_id
|
|
10
|
+
@begun = false
|
|
11
|
+
@finished = false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def is_begun?
|
|
15
|
+
@begun
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def is_finished?
|
|
19
|
+
@finished
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def begin
|
|
23
|
+
raise Error, "Transaction already begun: #{@correlation_id}" if @begun
|
|
24
|
+
|
|
25
|
+
@axon.begin
|
|
26
|
+
@axon.let_var("saga_corr", @correlation_id)
|
|
27
|
+
@begun = true
|
|
28
|
+
rescue StandardError
|
|
29
|
+
@axon.cancel rescue nil
|
|
30
|
+
raise
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def step(axonql)
|
|
34
|
+
assert_active
|
|
35
|
+
@axon.query(axonql)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def commit
|
|
39
|
+
assert_active
|
|
40
|
+
@axon.commit
|
|
41
|
+
@finished = true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def rollback
|
|
45
|
+
return unless @begun && !@finished
|
|
46
|
+
|
|
47
|
+
@axon.cancel
|
|
48
|
+
@finished = true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def assert_active
|
|
54
|
+
raise Error, "Transaction not begun" unless @begun
|
|
55
|
+
raise Error, "Transaction already finished" if @finished
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AxonBase
|
|
4
|
+
class SagaTransaction
|
|
5
|
+
attr_reader :saga_name, :correlation_id
|
|
6
|
+
|
|
7
|
+
def initialize(axon, saga_name, correlation_id)
|
|
8
|
+
@axon = axon
|
|
9
|
+
@saga_name = saga_name
|
|
10
|
+
@correlation_id = correlation_id
|
|
11
|
+
@begun = false
|
|
12
|
+
@finished = false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def is_begun?
|
|
16
|
+
@begun
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def is_finished?
|
|
20
|
+
@finished
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def begin
|
|
24
|
+
raise Error, "Saga already begun: #{@correlation_id}" if @begun
|
|
25
|
+
|
|
26
|
+
result = @axon.query("BEGIN SAGA #{escape(@saga_name)} WITH CORRELATION '#{escape(@correlation_id)}'")
|
|
27
|
+
if result.is_a?(Hash) && result["status"] == "RUNNING"
|
|
28
|
+
@begun = true
|
|
29
|
+
return
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
raise Error, "Failed to begin saga: #{result}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def step(axonql)
|
|
36
|
+
assert_active
|
|
37
|
+
@axon.let_var("saga_corr", @correlation_id)
|
|
38
|
+
@axon.query(axonql)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def commit
|
|
42
|
+
assert_active
|
|
43
|
+
result = @axon.query("COMMIT SAGA #{escape(@saga_name)} WITH CORRELATION '#{escape(@correlation_id)}'")
|
|
44
|
+
@finished = true
|
|
45
|
+
raise Error, "Saga commit failed: #{result}" unless result.is_a?(Hash) && result["status"] == "COMMITTED"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def rollback
|
|
49
|
+
return unless @begun && !@finished
|
|
50
|
+
|
|
51
|
+
@axon.query("CANCEL SAGA #{escape(@saga_name)} WITH CORRELATION '#{escape(@correlation_id)}'")
|
|
52
|
+
@finished = true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def describe
|
|
56
|
+
@axon.query("SHOW SAGA TRANSACTION #{escape(@saga_name)} '#{escape(@correlation_id)}'")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def assert_active
|
|
62
|
+
raise Error, "Saga not begun" unless @begun
|
|
63
|
+
raise Error, "Saga already finished" if @finished
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def escape(value)
|
|
67
|
+
value.gsub("\\") { "\\\\\\\\" }.gsub("'") { "''" }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AxonBase
|
|
4
|
+
class WebSocketTransport
|
|
5
|
+
def initialize(url, ssl_context: nil)
|
|
6
|
+
require "websocket-client-simple"
|
|
7
|
+
@messages = Queue.new
|
|
8
|
+
options = ssl_context ? { tls: ssl_context } : {}
|
|
9
|
+
@socket = WebSocket::Client::Simple.connect(url, options)
|
|
10
|
+
@socket.on(:message) { |message| @messages << message.data }
|
|
11
|
+
@socket.on(:error) { |error| @messages << error }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def send(message)
|
|
15
|
+
@socket.send(message)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def receive
|
|
19
|
+
message = @messages.pop
|
|
20
|
+
raise message if message.is_a?(Exception)
|
|
21
|
+
message
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def close
|
|
25
|
+
@socket.close
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/axonbase.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "thread"
|
|
5
|
+
require_relative "axonbase/errors"
|
|
6
|
+
require_relative "axonbase/transport"
|
|
7
|
+
require_relative "axonbase/client"
|
|
8
|
+
require_relative "axonbase/migration"
|
|
9
|
+
require_relative "axonbase/saga_transaction"
|
|
10
|
+
require_relative "axonbase/saga_participant_transaction"
|
|
11
|
+
|
|
12
|
+
module AxonBase
|
|
13
|
+
PROTOCOL_VERSION = 1
|
|
14
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: axonbase-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- AxonBase
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-09-03 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: websocket-client-simple
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0.7'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0.7'
|
|
27
|
+
description:
|
|
28
|
+
email:
|
|
29
|
+
executables: []
|
|
30
|
+
extensions: []
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
files:
|
|
33
|
+
- lib/axonbase.rb
|
|
34
|
+
- lib/axonbase/client.rb
|
|
35
|
+
- lib/axonbase/errors.rb
|
|
36
|
+
- lib/axonbase/migration.rb
|
|
37
|
+
- lib/axonbase/saga_participant_transaction.rb
|
|
38
|
+
- lib/axonbase/saga_transaction.rb
|
|
39
|
+
- lib/axonbase/transport.rb
|
|
40
|
+
homepage: https://github.com/axonbase/axonbase
|
|
41
|
+
licenses:
|
|
42
|
+
- MIT
|
|
43
|
+
metadata:
|
|
44
|
+
source_code_uri: https://github.com/axonbase/axonbase
|
|
45
|
+
bug_tracker_uri: https://github.com/axonbase/axonbase/issues
|
|
46
|
+
post_install_message:
|
|
47
|
+
rdoc_options: []
|
|
48
|
+
require_paths:
|
|
49
|
+
- lib
|
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.6'
|
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0'
|
|
60
|
+
requirements: []
|
|
61
|
+
rubygems_version: 3.0.3.1
|
|
62
|
+
signing_key:
|
|
63
|
+
specification_version: 4
|
|
64
|
+
summary: AxonBase WebSocket JSON-RPC client
|
|
65
|
+
test_files: []
|