hakuban 0.7.0 → 0.8.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/bin/hakuban-engine +10 -10
- data/bin/{hakuban-observer → hakuban-observe} +20 -27
- data/lib/hakuban/contract.rb +62 -110
- data/lib/hakuban/descriptor.rb +15 -20
- data/lib/hakuban/engine.rb +73 -29
- data/lib/hakuban/exchange.rb +73 -31
- data/lib/hakuban/ffi-object.rb +46 -55
- data/lib/hakuban/ffi.rb +76 -169
- data/lib/hakuban/logger.rb +2 -2
- data/lib/hakuban/object_state.rb +13 -14
- data/lib/hakuban/object_state_sink.rb +17 -25
- data/lib/hakuban/object_state_stream.rb +15 -15
- data/lib/hakuban/refinements.rb +23 -0
- data/lib/hakuban/stream.rb +160 -90
- data/lib/hakuban/tokio-websocket-connector.rb +12 -5
- data/lib/hakuban/version.rb +1 -1
- data/lib/hakuban.rb +1 -1
- metadata +16 -15
data/lib/hakuban/object_state.rb
CHANGED
@@ -9,6 +9,7 @@ module Hakuban
|
|
9
9
|
class WrongFormatError < Exception; end
|
10
10
|
|
11
11
|
def initialize(data, version: nil, format: nil, synchronized_us_ago: 0, &block)
|
12
|
+
super()
|
12
13
|
if version.nil?
|
13
14
|
timestamp = Time.new
|
14
15
|
version = [0, timestamp.to_i, timestamp.nsec]
|
@@ -18,14 +19,19 @@ module Hakuban
|
|
18
19
|
@synchronized_us_ago = synchronized_us_ago
|
19
20
|
@data = data
|
20
21
|
@pointer = false
|
21
|
-
|
22
|
-
|
22
|
+
Thread.handle_interrupt(Object => :never) {
|
23
|
+
self.do_and_drop_or_return(&block)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize_from_ffi_pointer(pointer)
|
28
|
+
super(pointer, :hakuban_object_state_drop, :hakuban_object_state_clone)
|
23
29
|
end
|
24
30
|
|
25
31
|
|
32
|
+
|
26
33
|
def with_pointer
|
27
|
-
@
|
28
|
-
raise PointerAlreadyDropped if dropped?
|
34
|
+
@pointer_mutex.synchronize {
|
29
35
|
if @pointer == false
|
30
36
|
Hakuban::hakuban_initialize
|
31
37
|
version_pointer = ::FFI::MemoryPointer.new(:int64, @version.size)
|
@@ -41,13 +47,6 @@ module Hakuban
|
|
41
47
|
}
|
42
48
|
end
|
43
49
|
|
44
|
-
|
45
|
-
private_class_method def self.from_ffi_pointer(pointer)
|
46
|
-
new_instance = allocate
|
47
|
-
new_instance.send(:initialize_pointer, pointer, :hakuban_object_state_drop, :hakuban_object_state_clone)
|
48
|
-
new_instance
|
49
|
-
end
|
50
|
-
|
51
50
|
def data
|
52
51
|
return @data if defined? @data
|
53
52
|
array = with_pointer { |pointer| FFI::hakuban_object_state_data(pointer) }
|
@@ -94,7 +93,7 @@ module Hakuban
|
|
94
93
|
end
|
95
94
|
|
96
95
|
def json_serialize
|
97
|
-
ObjectState.new(
|
96
|
+
ObjectState.new(data.to_json, version: version, format: format+["JSON"], synchronized_us_ago: synchronized_us_ago)
|
98
97
|
end
|
99
98
|
|
100
99
|
def inspect
|
@@ -102,5 +101,5 @@ module Hakuban
|
|
102
101
|
end
|
103
102
|
|
104
103
|
end
|
105
|
-
|
106
|
-
end
|
104
|
+
|
105
|
+
end
|
@@ -8,54 +8,46 @@ module Hakuban
|
|
8
8
|
|
9
9
|
private_class_method :new
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
|
12
|
+
def initialize_from_ffi_pointer(pointer)
|
13
|
+
super(pointer, :hakuban_object_state_sink_drop, nil)
|
13
14
|
end
|
14
15
|
|
15
16
|
|
16
17
|
include Stream
|
17
18
|
|
18
19
|
|
19
|
-
def next(&block)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def current(&block)
|
27
|
-
return nil if ! pointer = with_pointer { |pointer| FFI::hakuban_object_state_sink_current(pointer) }.unwrap
|
28
|
-
ObjectStateSinkParams.send(:new, pointer).do_and_drop_or_return(&block)
|
29
|
-
rescue FFIObject::PointerAlreadyDropped
|
20
|
+
def next(drop_at_the_end_of_block=true, &block)
|
21
|
+
process_item(
|
22
|
+
lambda { |pointer| FFI::hakuban_object_state_sink_next(pointer) },
|
23
|
+
lambda { |pointer| FFIObject::from_ffi_pointer(ObjectStateSinkParams, pointer) },
|
24
|
+
&block
|
25
|
+
)
|
30
26
|
end
|
31
27
|
|
32
28
|
|
33
29
|
def send(object_state)
|
34
|
-
|
35
|
-
object_state.with_pointer { |object_state_pointer| FFI::hakuban_object_state_sink_send(pointer, object_state_pointer) }
|
36
|
-
|
37
|
-
|
30
|
+
process_item(
|
31
|
+
lambda { |pointer| object_state.with_pointer { |object_state_pointer| FFI::hakuban_object_state_sink_send(pointer, object_state_pointer) } },
|
32
|
+
lambda { |_pointer| nil },
|
33
|
+
&lambda {}
|
34
|
+
)
|
38
35
|
end
|
39
36
|
|
40
37
|
|
41
38
|
def descriptor
|
42
|
-
Hakuban::
|
39
|
+
Hakuban::FFIObject.from_ffi_pointer(Hakuban::ObjectDescriptor, with_pointer { |pointer| FFI::hakuban_object_state_sink_descriptor(pointer) })
|
43
40
|
end
|
44
41
|
|
45
42
|
|
46
|
-
def desynchronize
|
47
|
-
with_pointer { |pointer| FFI::hakuban_object_state_sink_desynchronize(pointer) }
|
48
|
-
rescue FFIObject::PointerAlreadyDropped
|
49
|
-
end
|
50
|
-
|
51
43
|
end
|
52
44
|
|
53
45
|
class ObjectStateSinkParams < FFIObject
|
54
46
|
|
55
47
|
private_class_method :new
|
56
48
|
|
57
|
-
def
|
58
|
-
|
49
|
+
def initialize_from_ffi_pointer(pointer)
|
50
|
+
super(pointer, :hakuban_object_state_sink_params_drop, :hakuban_object_state_sink_params_clone)
|
59
51
|
end
|
60
52
|
|
61
53
|
end
|
@@ -1,37 +1,37 @@
|
|
1
|
+
require 'hakuban/ffi.rb'
|
1
2
|
require 'hakuban/ffi-object.rb'
|
2
3
|
require 'hakuban/stream.rb'
|
3
|
-
|
4
|
+
require 'hakuban/refinements.rb'
|
4
5
|
|
5
6
|
module Hakuban
|
6
7
|
|
8
|
+
|
7
9
|
class ObjectStateStream < FFIObject
|
8
10
|
|
9
11
|
private_class_method :new
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
|
14
|
+
def initialize_from_ffi_pointer(pointer)
|
15
|
+
super(pointer, :hakuban_object_state_stream_drop, nil)
|
13
16
|
end
|
14
17
|
|
15
18
|
|
19
|
+
|
16
20
|
include Stream
|
17
21
|
|
18
22
|
|
23
|
+
|
19
24
|
def next(&block)
|
20
|
-
|
21
|
-
|
22
|
-
|
25
|
+
process_item(
|
26
|
+
lambda { |pointer| FFI::hakuban_object_state_stream_next(pointer) },
|
27
|
+
lambda { |pointer| FFIObject::from_ffi_pointer(ObjectState, pointer) },
|
28
|
+
&block
|
29
|
+
)
|
23
30
|
end
|
24
|
-
|
25
|
-
|
26
|
-
def current(&block)
|
27
|
-
return nil if ! pointer = with_pointer { |pointer| FFI::hakuban_object_state_stream_current(pointer) }.unwrap
|
28
|
-
ObjectState.send(:from_ffi_pointer, pointer).do_and_drop_or_return(&block)
|
29
|
-
rescue FFIObject::PointerAlreadyDropped
|
30
|
-
end
|
31
|
-
|
32
31
|
|
32
|
+
|
33
33
|
def descriptor
|
34
|
-
Hakuban::
|
34
|
+
Hakuban::FFIObject.from_ffi_pointer(Hakuban::ObjectDescriptor, with_pointer { |pointer| FFI::hakuban_object_state_stream_descriptor(pointer) })
|
35
35
|
end
|
36
36
|
|
37
37
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ThreadExt
|
2
|
+
|
3
|
+
refine Thread do
|
4
|
+
|
5
|
+
def join_with_warning(timeout=60)
|
6
|
+
loop {
|
7
|
+
self.join(60)
|
8
|
+
return self.value if !self.status
|
9
|
+
$stderr.puts "Thread doesn't want to die: \n"+item_thread.backtrace.inspect+"\nat:\n"+caller.join("\n")
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def Thread.process_interrupts
|
15
|
+
Thread.handle_interrupt(Object => :immediate) {
|
16
|
+
Thread.pass
|
17
|
+
}
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/hakuban/stream.rb
CHANGED
@@ -1,119 +1,189 @@
|
|
1
|
+
require 'hakuban/refinements'
|
2
|
+
|
3
|
+
#TODO: bring back Async variants
|
1
4
|
|
2
5
|
module Hakuban
|
3
6
|
|
4
7
|
module Stream
|
5
8
|
|
6
|
-
|
7
|
-
|
9
|
+
using ThreadExt
|
10
|
+
|
11
|
+
class NextItemInterrupt < Exception
|
12
|
+
def ==(other)
|
13
|
+
other.object_id == self.object_id
|
14
|
+
end
|
8
15
|
end
|
9
16
|
|
10
|
-
class KilledOnNext < Exception; end
|
11
|
-
|
12
|
-
#TODO: meh
|
13
|
-
class StreamEnumerator
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
# next may be called in CLI context, so, every return branch should process interrupts explicitly
|
19
|
+
private def process_item(future_constructor, item_constructor, &block)
|
20
|
+
Thread.handle_interrupt(Object => :never) do
|
21
|
+
future_pointer = begin
|
22
|
+
with_pointer(&future_constructor)
|
23
|
+
rescue FFIObject::PointerAlreadyDropped
|
24
|
+
Thread.process_interrupts
|
25
|
+
return nil
|
26
|
+
end
|
27
|
+
|
28
|
+
result, error = Hakuban::FFI::FFIFuture.await(future_pointer)
|
29
|
+
item = if pointer = result.unwrap
|
30
|
+
item_constructor.call(pointer)
|
31
|
+
end
|
18
32
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
33
|
+
raise error if error
|
34
|
+
return item if block.nil?
|
22
35
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
36
|
+
if item.nil?
|
37
|
+
Thread.process_interrupts
|
38
|
+
nil
|
39
|
+
else
|
40
|
+
Thread.handle_interrupt(Object => :immediate) {
|
41
|
+
block.call(item)
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
ensure
|
46
|
+
item.drop if !!block and !!item
|
29
47
|
end
|
48
|
+
end
|
30
49
|
|
31
|
-
|
32
|
-
|
50
|
+
def each(*args, **kwargs, &block)
|
51
|
+
for_each(*args, **kwargs, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def for_each(*args, **kwargs, &block)
|
55
|
+
if kwargs[:interrupt_on_next]
|
56
|
+
for_each_till_next(*args, **kwargs, &block)
|
57
|
+
else
|
58
|
+
while self.next { |new_item| sync_call(new_item, args, kwargs, block); true }; end
|
33
59
|
end
|
60
|
+
end
|
34
61
|
|
35
|
-
def call_or_return(&block)
|
36
|
-
if block
|
37
|
-
Thread.handle_interrupt(Object => :never) {
|
38
|
-
begin
|
39
|
-
to_kill = nil
|
40
|
-
Thread.handle_interrupt(Object => :immediate) {
|
41
|
-
while new_item = @stream.next
|
42
|
-
case @async
|
43
|
-
when false
|
44
|
-
sync_call(new_item, block)
|
45
|
-
when :async
|
46
|
-
if @kill_on_next
|
47
|
-
if to_kill
|
48
|
-
to_kill.stop
|
49
|
-
to_kill.wait
|
50
|
-
end
|
51
|
-
end
|
52
|
-
to_kill = Async(new_item) { |_task, item|
|
53
|
-
sync_call(item, block)
|
54
|
-
}
|
55
|
-
when :thread
|
56
|
-
if @kill_on_next
|
57
|
-
if to_kill
|
58
|
-
to_kill.raise KilledOnNext.new
|
59
|
-
to_kill.value
|
60
|
-
end
|
61
|
-
end
|
62
|
-
to_kill = Thread.new(new_item) { |item|
|
63
|
-
sync_call(item, block)
|
64
|
-
}
|
65
|
-
end
|
66
|
-
end
|
67
62
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
63
|
+
def for_each_till_next(*args, **kwargs, &block)
|
64
|
+
for_each_in_thread(*args,**kwargs.merge(kill_previous_on_next: true), &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def for_each_concurrent(*args, **kwargs, &block)
|
69
|
+
for_each_in_thread(*args, **kwargs, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def for_each_in_thread(*args, **kwargs, &block)
|
74
|
+
Thread.handle_interrupt(Object => :never) {
|
75
|
+
|
76
|
+
# we poll the stream in a separate thread because we sometimes want to interrupt the polling thread from an item-handling thread, and, without the wrapper-thread, we could end up raising exception in a random place of code, if Async is being used. maybe?
|
77
|
+
stream_polling_thread = Thread.new {
|
78
|
+
begin
|
79
|
+
item_threads = ObjectSpace::WeakMap.new
|
80
|
+
exception_to_rescue = NextItemInterrupt.new
|
81
|
+
|
82
|
+
while new_item = self.next
|
83
|
+
if kwargs[:kill_previous_on_next]
|
84
|
+
item_threads.keys.each { |item_thread|
|
85
|
+
item_thread.raise(exception_to_rescue)
|
86
|
+
item_thread.join
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
item_threads[Thread.new(new_item, Thread.current) do |item, parent_thread|
|
91
|
+
sync_call(item, args, kwargs, block)
|
92
|
+
rescue Object => error
|
93
|
+
if error != exception_to_rescue
|
94
|
+
if kwargs[:propagate_exceptions].nil? or kwargs[:propagate_exceptions]
|
95
|
+
parent_thread.raise(error)
|
96
|
+
else
|
97
|
+
raise
|
83
98
|
end
|
84
99
|
end
|
85
|
-
|
86
|
-
|
87
|
-
|
100
|
+
ensure
|
101
|
+
item_threads.delete Thread.current
|
102
|
+
end] = true
|
88
103
|
end
|
89
|
-
}
|
90
|
-
else
|
91
|
-
self
|
92
|
-
end
|
93
|
-
end
|
94
104
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
rescue KilledOnNext
|
100
|
-
end
|
101
|
-
else
|
102
|
-
Thread.handle_interrupt(Object => :never) {
|
103
|
-
begin
|
104
|
-
Thread.handle_interrupt(Object => :immediate) {
|
105
|
-
block.call(item, *@args, **@kwargs)
|
105
|
+
if kwargs[:kill_previous_on_next]
|
106
|
+
item_threads.keys.each { |item_thread|
|
107
|
+
item_thread.raise(exception_to_rescue)
|
108
|
+
item_thread.join
|
106
109
|
}
|
107
|
-
rescue KilledOnNext
|
108
|
-
ensure
|
109
|
-
item.drop
|
110
110
|
end
|
111
|
+
|
112
|
+
Thread.handle_interrupt(Object => :immediate) {
|
113
|
+
item_threads.keys.each { |item_thread|
|
114
|
+
item_thread.join
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
nil
|
119
|
+
rescue Object => error
|
120
|
+
exception_to_rescue = error
|
121
|
+
item_threads.keys.each { |item_thread| item_thread.raise(error) }
|
122
|
+
error
|
123
|
+
ensure
|
124
|
+
item_threads.keys.each { |item_thread|
|
125
|
+
begin
|
126
|
+
item_thread.join_with_warning(60)
|
127
|
+
rescue Object => error
|
128
|
+
# it's probably better to not re-raise here, so this one doesn't obscure the original. but lets at least print it out.
|
129
|
+
$stderr.puts "Item thread exception: \n"+error.inspect
|
130
|
+
end
|
131
|
+
}
|
132
|
+
end
|
133
|
+
}
|
134
|
+
|
135
|
+
begin
|
136
|
+
Thread.handle_interrupt(Object => :immediate) {
|
137
|
+
value = stream_polling_thread.join.value
|
138
|
+
raise value if !value.nil?
|
111
139
|
}
|
140
|
+
rescue Object => error
|
141
|
+
stream_polling_thread.raise(error)
|
142
|
+
raise
|
143
|
+
ensure
|
144
|
+
begin
|
145
|
+
stream_polling_thread.join_with_warning(60)
|
146
|
+
rescue Object => error
|
147
|
+
# it's probably better to not re-raise here, so this one doesn't obscure the original. but lets at least print it out.
|
148
|
+
$stderr.puts "Item thread exception: \n"+error.inspect
|
149
|
+
end
|
112
150
|
end
|
113
|
-
end
|
114
151
|
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
## blocks can't be sanely passed to ractors :(
|
157
|
+
# def for_each_in_ractor(*args, **kwargs, &block)
|
158
|
+
# Thread.handle_interrupt(Object => :never) {
|
159
|
+
# begin
|
160
|
+
# Thread.handle_interrupt(Object => :immediate) {
|
161
|
+
# while new_item = self.next
|
162
|
+
# Ractor.new(new_item, args, kwargs, runner ) { |item, args, kwargs, runner|
|
163
|
+
# sync_call(item, args, kwargs, runner)
|
164
|
+
# }
|
165
|
+
# end
|
166
|
+
# }
|
167
|
+
# ensure
|
168
|
+
# #TODO: should we kill still-running sub-tasks here, or await? or maybe kill on exception, and await otherwise?
|
169
|
+
# end
|
170
|
+
# }
|
171
|
+
# end
|
172
|
+
|
173
|
+
|
174
|
+
private def sync_call(item, args, kwargs, block)
|
175
|
+
Thread.handle_interrupt(Object => :never) {
|
176
|
+
begin
|
177
|
+
Thread.handle_interrupt(Object => :immediate) {
|
178
|
+
block.call(item, *args, **kwargs)
|
179
|
+
}
|
180
|
+
ensure
|
181
|
+
item.drop
|
182
|
+
end
|
183
|
+
}
|
115
184
|
end
|
116
185
|
|
186
|
+
|
117
187
|
end
|
118
188
|
|
119
189
|
end
|
@@ -21,12 +21,19 @@ module Hakuban
|
|
21
21
|
|
22
22
|
class WebsocketConnector < FFIObject
|
23
23
|
|
24
|
-
def initialize(
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
def initialize(exchange, url, &block)
|
25
|
+
super()
|
26
|
+
Thread.handle_interrupt(Object => :never) {
|
27
|
+
pointer = exchange.with_pointer { |exchange_pointer| FFI::hakuban_tokio_websocket_connector_new(Tokio.pointer, exchange_pointer, url) }.unwrap
|
28
|
+
initialize_pointer(pointer, :hakuban_tokio_websocket_connector_drop, nil)
|
29
|
+
self.do_and_drop_or_return(&block)
|
30
|
+
}
|
28
31
|
end
|
29
32
|
|
30
33
|
end
|
31
34
|
|
32
|
-
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.default_name
|
38
|
+
"#{Socket.gethostname}:#{File.basename(caller_locations(0..1)[1].path)}:#{$$}"
|
39
|
+
end
|
data/lib/hakuban/version.rb
CHANGED
data/lib/hakuban.rb
CHANGED
@@ -11,7 +11,7 @@ end
|
|
11
11
|
|
12
12
|
require_relative 'hakuban/contract.rb'
|
13
13
|
require_relative 'hakuban/descriptor.rb'
|
14
|
-
require_relative "hakuban/engine"
|
14
|
+
require_relative "hakuban/engine.rb"
|
15
15
|
require_relative 'hakuban/exchange.rb'
|
16
16
|
require_relative 'hakuban/ffi-object.rb'
|
17
17
|
require_relative 'hakuban/logger.rb'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hakuban
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yunta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-03-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -16,75 +16,75 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '3.
|
19
|
+
version: '3.13'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '3.
|
26
|
+
version: '3.13'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: simplecov
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
33
|
+
version: '0.22'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0.
|
40
|
+
version: '0.22'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: ffi
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
47
|
+
version: '1.17'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
54
|
+
version: '1.17'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: json
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '2.
|
61
|
+
version: '2.9'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '2.
|
68
|
+
version: '2.9'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: slop
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '4.
|
75
|
+
version: '4.10'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '4.
|
82
|
+
version: '4.10'
|
83
83
|
description: Ruby binding for convenient data-object sharing library - Hakuban.
|
84
84
|
email:
|
85
85
|
- maciej.blomberg@mikoton.com
|
86
86
|
executables:
|
87
|
-
- hakuban-
|
87
|
+
- hakuban-observe
|
88
88
|
- hakuban-engine
|
89
89
|
extensions: []
|
90
90
|
extra_rdoc_files: []
|
@@ -93,7 +93,7 @@ files:
|
|
93
93
|
- README.md
|
94
94
|
- Rakefile
|
95
95
|
- bin/hakuban-engine
|
96
|
-
- bin/hakuban-
|
96
|
+
- bin/hakuban-observe
|
97
97
|
- lib/hakuban.rb
|
98
98
|
- lib/hakuban/contract.rb
|
99
99
|
- lib/hakuban/descriptor.rb
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- lib/hakuban/object_state.rb
|
106
106
|
- lib/hakuban/object_state_sink.rb
|
107
107
|
- lib/hakuban/object_state_stream.rb
|
108
|
+
- lib/hakuban/refinements.rb
|
108
109
|
- lib/hakuban/stream.rb
|
109
110
|
- lib/hakuban/tokio-websocket-connector.rb
|
110
111
|
- lib/hakuban/version.rb
|
@@ -127,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
128
|
- !ruby/object:Gem::Version
|
128
129
|
version: '0'
|
129
130
|
requirements: []
|
130
|
-
rubygems_version: 3.
|
131
|
+
rubygems_version: 3.5.22
|
131
132
|
signing_key:
|
132
133
|
specification_version: 4
|
133
134
|
summary: Ruby binding for Hakuban library
|