skyfall 0.2.0 → 0.2.1
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/CHANGELOG.md +7 -0
- data/lib/skyfall/car_archive.rb +3 -0
- data/lib/skyfall/messages/commit_message.rb +23 -0
- data/lib/skyfall/messages/handle_message.rb +7 -0
- data/lib/skyfall/messages/info_message.rb +22 -0
- data/lib/skyfall/messages/tombstone_message.rb +4 -0
- data/lib/skyfall/messages/unknown_message.rb +4 -0
- data/lib/skyfall/messages/websocket_message.rb +90 -0
- data/lib/skyfall/operation.rb +24 -13
- data/lib/skyfall/stream.rb +1 -1
- data/lib/skyfall/version.rb +1 -1
- data/lib/skyfall.rb +1 -1
- metadata +8 -3
- data/lib/skyfall/websocket_message.rb +0 -77
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a508caa504715c30f9d9ff68dbf419c8b88ad00e5fbc6315ae6a3abb624e167
|
4
|
+
data.tar.gz: 9010ece05b7f1cce0a2573ecfa75965d1dba2b77cd7926eaa8fc510f8e2ae7e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9fc0370411496e76a4caaa7b2795e999b2fbf3445e21a02e3b9135e4c7fc5e3766d7ea3a94e145becf3ab96fd4d494b0f7d8a04a09eee03698a97125d44238d
|
7
|
+
data.tar.gz: b302a5f7eefc30509a6b721809105bd9439f6919b31ab73878d132fa8a67062d0d77c17d375d3d433cb77bbba63127a823d1817f5d1a71f16c2b6601dca7d493
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## [0.2.1] - 2023-08-19
|
2
|
+
|
3
|
+
- optimized `WebsocketMessage` parsing performance - lazy parsing of most properties (message decoding should be over 50% faster on average)
|
4
|
+
- added separate subclasses of `WebsocketMessage` for different message types
|
5
|
+
- added support for `#handle`, `#info` and `#tombstone` message types
|
6
|
+
- `UnknownMessage` is returned for unrecognized message types
|
7
|
+
|
1
8
|
## [0.2.0] - 2023-07-24
|
2
9
|
|
3
10
|
- switched the websocket library from `websocket-client-simple` to `faye-websocket`, which should make event parsing up to ~30× faster (!)
|
data/lib/skyfall/car_archive.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../car_archive'
|
2
|
+
require_relative '../cid'
|
3
|
+
require_relative '../operation'
|
4
|
+
|
5
|
+
module Skyfall
|
6
|
+
class CommitMessage < WebsocketMessage
|
7
|
+
def commit
|
8
|
+
@commit ||= @data_object['commit'] && CID.from_cbor_tag(@data_object['commit'])
|
9
|
+
end
|
10
|
+
|
11
|
+
def prev
|
12
|
+
@prev ||= @data_object['prev'] && CID.from_cbor_tag(@data_object['prev'])
|
13
|
+
end
|
14
|
+
|
15
|
+
def blocks
|
16
|
+
@blocks ||= CarArchive.new(@data_object['blocks'])
|
17
|
+
end
|
18
|
+
|
19
|
+
def operations
|
20
|
+
@operations ||= @data_object['ops'].map { |op| Operation.new(self, op) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Skyfall
|
2
|
+
class InfoMessage < WebsocketMessage
|
3
|
+
attr_reader :name, :message
|
4
|
+
|
5
|
+
OUTDATED_CURSOR = "OutdatedCursor"
|
6
|
+
|
7
|
+
def initialize(type_object, data_object)
|
8
|
+
super
|
9
|
+
|
10
|
+
@name = @data_object['name']
|
11
|
+
@message = @data_object['message']
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
(@name || "InfoMessage") + (@message ? ": #{@message}" : "")
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspectable_variables
|
19
|
+
super - [:@did, :@seq]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require_relative '../errors'
|
2
|
+
require_relative '../extensions'
|
3
|
+
|
4
|
+
require 'cbor'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
module Skyfall
|
8
|
+
class WebsocketMessage
|
9
|
+
using Skyfall::Extensions
|
10
|
+
|
11
|
+
require_relative 'commit_message'
|
12
|
+
require_relative 'handle_message'
|
13
|
+
require_relative 'info_message'
|
14
|
+
require_relative 'tombstone_message'
|
15
|
+
require_relative 'unknown_message'
|
16
|
+
|
17
|
+
attr_reader :type_object, :data_object
|
18
|
+
attr_reader :type, :did, :seq
|
19
|
+
|
20
|
+
alias repo did
|
21
|
+
|
22
|
+
def self.new(data)
|
23
|
+
type_object, data_object = decode_cbor_objects(data)
|
24
|
+
|
25
|
+
message_class = case type_object['t']
|
26
|
+
when '#commit' then CommitMessage
|
27
|
+
when '#handle' then HandleMessage
|
28
|
+
when '#info' then InfoMessage
|
29
|
+
when '#tombstone' then TombstoneMessage
|
30
|
+
else UnknownMessage
|
31
|
+
end
|
32
|
+
|
33
|
+
message = message_class.allocate
|
34
|
+
message.send(:initialize, type_object, data_object)
|
35
|
+
message
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(type_object, data_object)
|
39
|
+
@type_object = type_object
|
40
|
+
@data_object = data_object
|
41
|
+
|
42
|
+
@type = @type_object['t'][1..-1].to_sym
|
43
|
+
@did = @data_object['repo'] || @data_object['did']
|
44
|
+
@seq = @data_object['seq']
|
45
|
+
end
|
46
|
+
|
47
|
+
def operations
|
48
|
+
[]
|
49
|
+
end
|
50
|
+
|
51
|
+
def time
|
52
|
+
@time ||= @data_object['time'] && Time.parse(@data_object['time'])
|
53
|
+
end
|
54
|
+
|
55
|
+
def inspectable_variables
|
56
|
+
instance_variables - [:@type_object, :@data_object, :@blocks]
|
57
|
+
end
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
vars = inspectable_variables.map { |v| "#{v}=#{instance_variable_get(v).inspect}" }.join(", ")
|
61
|
+
"#<#{self.class}:0x#{object_id} #{vars}>"
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def self.decode_cbor_objects(data)
|
67
|
+
objects = CBOR.decode_sequence(data)
|
68
|
+
|
69
|
+
if objects.length < 2
|
70
|
+
raise DecodeError.new("Malformed message: #{objects.inspect}")
|
71
|
+
elsif objects.length > 2
|
72
|
+
raise DecodeError.new("Invalid number of objects: #{objects.length}")
|
73
|
+
end
|
74
|
+
|
75
|
+
type, data = objects
|
76
|
+
|
77
|
+
if data['error']
|
78
|
+
raise SubscriptionError.new(data['error'], data['message'])
|
79
|
+
end
|
80
|
+
|
81
|
+
raise DecodeError.new("Invalid object type: #{type}") unless type.is_a?(Hash)
|
82
|
+
raise UnsupportedError.new("Unexpected CBOR object: #{type}") unless type['op'] == 1
|
83
|
+
raise DecodeError.new("Missing data: #{type} #{objects.inspect}") unless type['op'] && type['t']
|
84
|
+
raise DecodeError.new("Invalid message type: #{type['t']}") unless type['t'].start_with?('#')
|
85
|
+
raise DecodeError.new("Invalid object type: #{data}") unless data.is_a?(Hash)
|
86
|
+
|
87
|
+
[type, data]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/skyfall/operation.rb
CHANGED
@@ -2,30 +2,41 @@ require_relative 'collection'
|
|
2
2
|
|
3
3
|
module Skyfall
|
4
4
|
class Operation
|
5
|
-
|
5
|
+
def initialize(message, json)
|
6
|
+
@message = message
|
7
|
+
@json = json
|
8
|
+
end
|
6
9
|
|
7
|
-
def
|
8
|
-
@repo
|
9
|
-
@path = path
|
10
|
-
@action = action.to_sym
|
11
|
-
@cid = cid
|
12
|
-
@record = record
|
10
|
+
def repo
|
11
|
+
@message.repo
|
13
12
|
end
|
14
13
|
|
15
|
-
def
|
16
|
-
@
|
14
|
+
def path
|
15
|
+
@json['path']
|
17
16
|
end
|
18
17
|
|
19
|
-
def
|
20
|
-
|
18
|
+
def action
|
19
|
+
@json['action'].to_sym
|
21
20
|
end
|
22
21
|
|
23
22
|
def collection
|
24
|
-
path.split('/')[0]
|
23
|
+
@json['path'].split('/')[0]
|
25
24
|
end
|
26
25
|
|
27
26
|
def rkey
|
28
|
-
path.split('/')[1]
|
27
|
+
@json['path'].split('/')[1]
|
28
|
+
end
|
29
|
+
|
30
|
+
def uri
|
31
|
+
"at://#{repo}/#{path}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def cid
|
35
|
+
@cid ||= @json['cid'] && CID.from_cbor_tag(@json['cid'])
|
36
|
+
end
|
37
|
+
|
38
|
+
def raw_record
|
39
|
+
@raw_record ||= cid && @message.blocks.section_with_cid(cid)
|
29
40
|
end
|
30
41
|
|
31
42
|
def type
|
data/lib/skyfall/stream.rb
CHANGED
data/lib/skyfall/version.rb
CHANGED
data/lib/skyfall.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skyfall
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kuba Suder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base32
|
@@ -86,10 +86,15 @@ files:
|
|
86
86
|
- lib/skyfall/collection.rb
|
87
87
|
- lib/skyfall/errors.rb
|
88
88
|
- lib/skyfall/extensions.rb
|
89
|
+
- lib/skyfall/messages/commit_message.rb
|
90
|
+
- lib/skyfall/messages/handle_message.rb
|
91
|
+
- lib/skyfall/messages/info_message.rb
|
92
|
+
- lib/skyfall/messages/tombstone_message.rb
|
93
|
+
- lib/skyfall/messages/unknown_message.rb
|
94
|
+
- lib/skyfall/messages/websocket_message.rb
|
89
95
|
- lib/skyfall/operation.rb
|
90
96
|
- lib/skyfall/stream.rb
|
91
97
|
- lib/skyfall/version.rb
|
92
|
-
- lib/skyfall/websocket_message.rb
|
93
98
|
- sig/skyfall.rbs
|
94
99
|
homepage: https://github.com/mackuba/skyfall
|
95
100
|
licenses:
|
@@ -1,77 +0,0 @@
|
|
1
|
-
require_relative 'car_archive'
|
2
|
-
require_relative 'cid'
|
3
|
-
require_relative 'errors'
|
4
|
-
require_relative 'extensions'
|
5
|
-
require_relative 'operation'
|
6
|
-
|
7
|
-
require 'cbor'
|
8
|
-
require 'time'
|
9
|
-
|
10
|
-
module Skyfall
|
11
|
-
class WebsocketMessage
|
12
|
-
using Skyfall::Extensions
|
13
|
-
|
14
|
-
attr_reader :type_object, :data_object
|
15
|
-
attr_reader :type, :repo, :time, :seq, :commit, :prev, :blocks, :operations
|
16
|
-
|
17
|
-
def initialize(data)
|
18
|
-
@type_object, @data_object = decode_cbor_objects(data)
|
19
|
-
|
20
|
-
@type = @type_object['t'][1..-1].to_sym
|
21
|
-
@operations = []
|
22
|
-
|
23
|
-
@repo = @data_object['repo']
|
24
|
-
@time = Time.parse(@data_object['time'])
|
25
|
-
@seq = @data_object['seq']
|
26
|
-
|
27
|
-
return unless @type == :commit
|
28
|
-
|
29
|
-
@commit = @data_object['commit'] && CID.from_cbor_tag(@data_object['commit'])
|
30
|
-
@prev = @data_object['prev'] && CID.from_cbor_tag(@data_object['prev'])
|
31
|
-
|
32
|
-
@blocks = CarArchive.new(@data_object['blocks'])
|
33
|
-
|
34
|
-
@operations = @data_object['ops'].map { |op|
|
35
|
-
cid = op['cid'] && CID.from_cbor_tag(op['cid'])
|
36
|
-
path = op['path']
|
37
|
-
action = op['action']
|
38
|
-
record = cid && @blocks.sections.detect { |s| s.cid == cid }.body
|
39
|
-
|
40
|
-
Operation.new(@repo, path, action, cid, record)
|
41
|
-
}
|
42
|
-
end
|
43
|
-
|
44
|
-
def inspect
|
45
|
-
keys = instance_variables - [:@type_object, :@data_object, :@blocks]
|
46
|
-
vars = keys.map { |v| "#{v}=#{instance_variable_get(v).inspect}" }.join(", ")
|
47
|
-
"#<#{self.class}:0x#{object_id} #{vars}>"
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def decode_cbor_objects(data)
|
53
|
-
objects = CBOR.decode_sequence(data)
|
54
|
-
|
55
|
-
if objects.length < 2
|
56
|
-
raise DecodeError.new("Malformed message: #{objects.inspect}")
|
57
|
-
elsif objects.length > 2
|
58
|
-
raise DecodeError.new("Invalid number of objects: #{objects.length}")
|
59
|
-
end
|
60
|
-
|
61
|
-
type_object, data_object = objects
|
62
|
-
|
63
|
-
if data_object['error']
|
64
|
-
raise SubscriptionError.new(data_object['error'], data_object['message'])
|
65
|
-
end
|
66
|
-
|
67
|
-
raise DecodeError.new("Invalid object type: #{type_object}") unless type_object.is_a?(Hash)
|
68
|
-
raise UnsupportedError.new("Unexpected CBOR object: #{type_object}") unless type_object['op'] == 1
|
69
|
-
raise DecodeError.new("Missing data: #{type_object} #{objects.inspect}") unless type_object['op'] && type_object['t']
|
70
|
-
raise DecodeError.new("Invalid message type: #{type_object['t']}") unless type_object['t'].start_with?('#')
|
71
|
-
|
72
|
-
raise DecodeError.new("Invalid object type: #{data_object}") unless data_object.is_a?(Hash)
|
73
|
-
|
74
|
-
[type_object, data_object]
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|