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.
@@ -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
- @mutex = Mutex.new
22
- self.do_and_drop_or_return(&block)
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
- @mutex.synchronize {
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(JSON.dump(data), version: version, format: format+["JSON"], synchronized_us_ago: synchronized_us_ago)
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
- def initialize(pointer)
12
- initialize_pointer(pointer, :hakuban_object_state_sink_drop, nil)
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
- return nil if ! pointer = FFI::FFIFutureReturningPointer.create_and_await(self) { |pointer| FFI::hakuban_object_state_sink_next(pointer) }.unwrap
21
- ObjectStateSinkParams.send(:new, pointer).do_and_drop_or_return(&block)
22
- rescue FFIObject::PointerAlreadyDropped
23
- end
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
- FFI::FFIFutureReturningNothing.create_and_await(self) { |pointer|
35
- object_state.with_pointer { |object_state_pointer| FFI::hakuban_object_state_sink_send(pointer, object_state_pointer) }
36
- }.unwrap
37
- rescue FFIObject::PointerAlreadyDropped
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::ObjectDescriptor.send(:from_ffi_pointer, with_pointer { |pointer| FFI::hakuban_object_state_sink_descriptor(pointer) })
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 initialize(pointer)
58
- initialize_pointer(pointer, :hakuban_object_state_sink_params_drop, :hakuban_object_state_sink_params_clone)
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
- def initialize(pointer)
12
- initialize_pointer(pointer, :hakuban_object_state_stream_drop, nil)
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
- return nil if ! pointer = FFI::FFIFutureReturningPointer.create_and_await(self) { |pointer| FFI::hakuban_object_state_stream_next(pointer) }.unwrap
21
- ObjectState.send(:from_ffi_pointer, pointer).do_and_drop_or_return(&block)
22
- rescue FFIObject::PointerAlreadyDropped
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::ObjectDescriptor.send(:from_ffi_pointer, with_pointer { |pointer| FFI::hakuban_object_state_stream_descriptor(pointer) })
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
@@ -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
- def each(*args,**kwargs,&block)
7
- StreamEnumerator.new(self,false,false,false,args,kwargs).call_or_return(&block)
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
- def initialize(stream, without_implicit_drop, async, kill_on_next, args, kwargs)
16
- @stream, @without_implicit_drop, @async, @kill_on_next, @args, @kwargs = stream, without_implicit_drop, async, kill_on_next, args, kwargs
17
- end
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
- def async(*args,**kwargs,&block)
20
- StreamEnumerator.new(@stream, @without_implicit_drop, :async, @kill_on_next, @args+args, @kwargs.merge(kwargs)).call_or_return(&block)
21
- end
33
+ raise error if error
34
+ return item if block.nil?
22
35
 
23
- def thread(*args,**kwargs,&block)
24
- StreamEnumerator.new(@stream, @without_implicit_drop, :thread, @kill_on_next, @args+args, @kwargs.merge(kwargs)).call_or_return(&block)
25
- end
26
-
27
- def without_implicit_drop(*args,**kwargs,&block)
28
- StreamEnumerator.new(@stream, true, @async, @kill_on_next, @args+args, @kwargs.merge(kwargs)).call_or_return(&block)
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
- def kill_on_next(*args,**kwargs,&block)
32
- StreamEnumerator.new(@stream, @without_implicit_drop, @async, true, @args+args, @kwargs.merge(kwargs)).call_or_return(&block)
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
- case @async
69
- when false
70
- when :async
71
- if @kill_on_next
72
- if to_kill
73
- to_kill.stop
74
- to_kill.wait
75
- end
76
- end
77
- when :thread
78
- if @kill_on_next
79
- if to_kill
80
- to_kill.raise KilledOnNext.new
81
- to_kill.value
82
- end
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
- ensure
87
- #TODO: should we kill still-running sub-tasks here, or await? or maybe kill on exception, and await otherwise?
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
- private def sync_call(item, block)
96
- if @without_implicit_drop
97
- begin
98
- block.call(item, *@args, **@kwargs)
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(local_exchange, url, &block)
25
- pointer = local_exchange.with_pointer { |local_exchange_pointer| FFI::hakuban_tokio_websocket_connector_new(Tokio.pointer, local_exchange_pointer, url) }.unwrap
26
- initialize_pointer(pointer, :hakuban_tokio_websocket_connector_drop, nil)
27
- self.do_and_drop_or_return(&block)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hakuban
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.1"
5
5
  end
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.7.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: 2023-01-08 00:00:00.000000000 Z
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.10'
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.10'
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.21'
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.21'
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.15'
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.15'
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.5'
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.5'
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.9'
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.9'
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-observer
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-observer
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.3.26
131
+ rubygems_version: 3.5.22
131
132
  signing_key:
132
133
  specification_version: 4
133
134
  summary: Ruby binding for Hakuban library