hakuban 0.5.0 → 0.6.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.
@@ -0,0 +1,259 @@
1
+ require 'hakuban/hakuban'
2
+ require 'ostruct'
3
+
4
+ #TODO: prevent usage of old object?
5
+ #TODO: destructors
6
+
7
+ module Hakuban
8
+
9
+ class ObjectManager
10
+
11
+ attr_reader :contract
12
+
13
+ class Event < OpenStruct; end
14
+
15
+ def initialize(contract, object_class, block)
16
+ @contract = contract
17
+ @objects = {}
18
+ @running_handlers = {}
19
+ @event_queue = Queue.new
20
+
21
+ # This callback gets called from a separate thread, with no async reactor running.
22
+ # So, we only use it to forward actions to the main thread.
23
+ @ffi_callback = proc { |descriptor, action|
24
+ @event_queue << Event.new(action: action, descriptor: descriptor)
25
+ }
26
+ @ffi_events = @contract.events
27
+ @ffi_events.callback_register(&@ffi_callback)
28
+
29
+ @async = async_run {
30
+ while event = @event_queue.shift
31
+ case event.action
32
+ when :insert
33
+ raise if @objects[event.descriptor]
34
+ @objects[event.descriptor] = object_class.new(@contract, event.descriptor)
35
+ @event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if block
36
+ when :change
37
+ raise if not @objects[event.descriptor]
38
+ @objects[event.descriptor].do_change(:change)
39
+ when :remove
40
+ raise if not @objects[event.descriptor]
41
+ @objects[event.descriptor].do_change(:remove)
42
+ @objects.delete(event.descriptor)
43
+ when :handler_start
44
+ if (object = @objects[event.descriptor]) and !object.handler_already_run and !@running_handlers[event.descriptor]
45
+ descriptor_for_lambda = event.descriptor
46
+ @running_handlers[event.descriptor] = async_run {
47
+ object.run(block)
48
+ @event_queue << Event.new(action: :handler_finished, descriptor: descriptor_for_lambda) if @event_queue
49
+ }
50
+ end
51
+ when :handler_finished
52
+ raise if not @running_handlers[event.descriptor]
53
+ @running_handlers.delete(event.descriptor)
54
+ @event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if @objects[event.descriptor]
55
+ when :drop
56
+ @ffi_events.callback_unregister
57
+ @objects.values.each { |object| object.do_change(:remove) }
58
+ while @running_handlers.size > 0
59
+ event = @event_queue.shift
60
+ @running_handlers.delete(event.descriptor) if event.action == :handler_finished
61
+ end
62
+ @event_queue.clear
63
+ @objects.clear
64
+ @contract.drop
65
+ @contract, @event_queue = nil, nil
66
+ break
67
+ end
68
+ end
69
+ }
70
+ end
71
+
72
+ def objects; @objects; end
73
+ def object; @objects.values.first; end
74
+
75
+ def drop
76
+ drop_nonblock
77
+ async_join(@async)
78
+ end
79
+
80
+ def drop_nonblock
81
+ if @contract
82
+ @event_queue << Event.new(action: :drop)
83
+ end
84
+ end
85
+ end
86
+
87
+
88
+
89
+ class ManagedObject
90
+
91
+ attr_reader :descriptor, :async, :handler_already_run
92
+
93
+
94
+ def initialize(contract, descriptor)
95
+ @contract,@descriptor = contract, descriptor
96
+ @changes = Queue.new
97
+ @handler_already_run = false
98
+ end
99
+
100
+
101
+ def run(handler)
102
+ @handler_already_run = true
103
+ handler.call(self)
104
+ end
105
+
106
+
107
+ def next_event; next_change; end
108
+ def next_change
109
+ loop {
110
+ case @changes.shift
111
+ when :change then return true
112
+ when :remove then return false
113
+ end
114
+ }
115
+ end
116
+
117
+
118
+ def do_change(change)
119
+ @changes.push(change)
120
+ end
121
+
122
+ end
123
+
124
+
125
+
126
+ class ObjectObserve
127
+
128
+ class ObservedObject < ManagedObject
129
+
130
+ def state
131
+ @contract.object_state
132
+ end
133
+
134
+ def data
135
+ @contract.object_state&.data
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+
142
+
143
+ class ObjectExpose
144
+
145
+ class ExposedObject < ManagedObject
146
+
147
+ def initialize(contract, descriptor)
148
+ super(contract, descriptor)
149
+ @assignment = contract.assignment
150
+ end
151
+
152
+ def run(handler)
153
+ super
154
+ @contract.desynchronize(@assignment)
155
+ end
156
+
157
+ def do_change(change)
158
+ @assignment = @contract.assignment
159
+ super(change)
160
+ end
161
+
162
+ def assignement
163
+ @assignment = @contract.assignment()
164
+ end
165
+
166
+ def assigned?
167
+ @contract.assigned?()
168
+ end
169
+
170
+ def set_state(*state, **kwargs)
171
+ kwargs[:assignment] ||= @assignment
172
+ @contract.set_object_state(*state, **kwargs)
173
+ end
174
+
175
+ def state=(state)
176
+ set_state(*state)
177
+ end
178
+
179
+ def set_data(value)
180
+ timestamp = Time.new.to_f
181
+ set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
182
+ end
183
+
184
+ def data=(value)
185
+ set_data(value)
186
+ end
187
+
188
+ end
189
+
190
+ end
191
+
192
+
193
+ class TagObserve
194
+
195
+ class ObservedObject < ManagedObject
196
+
197
+ def state
198
+ @contract.object_state(@descriptor)
199
+ end
200
+
201
+ def data
202
+ @contract.object_state(@descriptor)&.data
203
+ end
204
+
205
+ end
206
+
207
+ end
208
+
209
+
210
+ class TagExpose
211
+
212
+ class ExposedObject < ManagedObject
213
+
214
+ def initialize(contract, descriptor)
215
+ super(contract, descriptor)
216
+ @assignment = contract.assignment(@descriptor)
217
+ end
218
+
219
+ def run(handler)
220
+ super
221
+ @contract.desynchronize(@descriptor, @assignment)
222
+ end
223
+
224
+ def do_change(change)
225
+ @assignment = @contract.assignment(@descriptor)
226
+ super(change)
227
+ end
228
+
229
+ def assignement
230
+ @assignment = @contract.assignment(@descriptor)
231
+ end
232
+
233
+ def assigned?
234
+ @contract.assigned?(@descriptor)
235
+ end
236
+
237
+ def set_state(*args, **kwargs)
238
+ kwargs[:assignment] ||= @assignment
239
+ @contract.set_object_state(@descriptor, *args, **kwargs)
240
+ end
241
+
242
+ def state=(state)
243
+ set_state(*state)
244
+ end
245
+
246
+ def set_data(value)
247
+ timestamp = Time.new.to_f
248
+ set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
249
+ end
250
+
251
+ def data=(value)
252
+ set_data(value)
253
+ end
254
+
255
+ end
256
+
257
+ end
258
+
259
+ end
@@ -0,0 +1,44 @@
1
+ require 'hakuban'
2
+ require 'hakuban/manager'
3
+
4
+ module Hakuban
5
+
6
+ class ThreadObjectManager < ObjectManager
7
+ def async_run
8
+ Thread.new { yield }
9
+ end
10
+
11
+ def async_join(thread)
12
+ thread.join
13
+ end
14
+ end
15
+
16
+
17
+ class ObjectObserve
18
+ def thread(&block)
19
+ ThreadObjectManager.new(self, ObservedObject, block)
20
+ end
21
+ end
22
+
23
+
24
+ class ObjectExpose
25
+ def thread(&block)
26
+ ThreadObjectManager.new(self, ExposedObject, block)
27
+ end
28
+ end
29
+
30
+
31
+ class TagObserve
32
+ def thread(&block)
33
+ ThreadObjectManager.new(self, ObservedObject, block)
34
+ end
35
+ end
36
+
37
+
38
+ class TagExpose
39
+ def thread(&block)
40
+ ThreadObjectManager.new(self, ExposedObject, block)
41
+ end
42
+ end
43
+
44
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hakuban
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
data/lib/hakuban.rb CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  require_relative "hakuban/version"
4
4
  require_relative "hakuban/hakuban"
5
+ require_relative "hakuban/engine"
6
+ #require_relative "hakuban/async"
7
+ #require_relative "hakuban/thread"
5
8
 
6
9
  module Hakuban
7
10
  end
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.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yunta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-13 00:00:00.000000000 Z
11
+ date: 2022-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -39,55 +39,54 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.15'
41
41
  - !ruby/object:Gem::Dependency
42
- name: msgpack
42
+ name: json
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.4'
47
+ version: '2.5'
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.4'
54
+ version: '2.5'
55
55
  - !ruby/object:Gem::Dependency
56
- name: json
56
+ name: slop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '2.5'
61
+ version: '4.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: '4.9'
69
69
  description: Ruby binding for convenient data-object sharing library - Hakuban.
70
70
  email:
71
71
  - maciej.blomberg@mikoton.com
72
- executables: []
72
+ executables:
73
+ - hakuban-observer
74
+ - hakuban-thread-engine
73
75
  extensions: []
74
76
  extra_rdoc_files: []
75
77
  files:
76
- - ".gitignore"
77
- - ".rspec"
78
- - Gemfile
79
- - LICENSE.txt
78
+ - MIT-LICENSE
80
79
  - README.md
81
80
  - Rakefile
82
- - bin/console
83
- - bin/setup
84
- - examples/all-top-managed.rb
85
- - examples/all-top-simple.rb
86
- - examples/cpu-usage.rb
87
- - hakuban.gemspec
81
+ - bin/hakuban-observer
82
+ - bin/hakuban-thread-engine
88
83
  - lib/hakuban.rb
84
+ - lib/hakuban/async.rb
85
+ - lib/hakuban/engine.rb
89
86
  - lib/hakuban/ffi.rb
90
87
  - lib/hakuban/hakuban.rb
88
+ - lib/hakuban/manager.rb
89
+ - lib/hakuban/thread.rb
91
90
  - lib/hakuban/version.rb
92
91
  homepage: https://gitlab.com/yunta/hakuban-ruby
93
92
  licenses:
@@ -99,16 +98,16 @@ require_paths:
99
98
  - lib
100
99
  required_ruby_version: !ruby/object:Gem::Requirement
101
100
  requirements:
102
- - - ">="
101
+ - - "~>"
103
102
  - !ruby/object:Gem::Version
104
- version: 2.4.0
103
+ version: '3.0'
105
104
  required_rubygems_version: !ruby/object:Gem::Requirement
106
105
  requirements:
107
106
  - - ">="
108
107
  - !ruby/object:Gem::Version
109
108
  version: '0'
110
109
  requirements: []
111
- rubygems_version: 3.2.15
110
+ rubygems_version: 3.2.22
112
111
  signing_key:
113
112
  specification_version: 4
114
113
  summary: Ruby binding for Hakuban library
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
- /vendor/
10
-
11
- # rspec failure tracking
12
- .rspec_status
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- gemspec
6
-
data/LICENSE.txt DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2021 yunta
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "hakuban"
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require "irb"
15
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,46 +0,0 @@
1
- require 'hakuban'
2
- require_relative './cpu-usage.rb'
3
-
4
-
5
- class Utilization < Hakuban::ObjectManager::ManagedObject
6
- include Hakuban::ObjectManager::ObservedObject
7
- include Hakuban::ObjectManager::ExposedObject
8
-
9
- attr_reader :id
10
-
11
- def initialize(contract, descriptor)
12
- super
13
- @id = descriptor.json["id"]
14
- end
15
- end
16
-
17
-
18
- id = "#{$$}@#{`hostname`.strip}"
19
-
20
- hakuban = Hakuban::LocalNode.new
21
- connector = Hakuban::WebsocketConnector.new("ws://localhost:3001").start(hakuban)
22
- all_utilizations = hakuban.tag("utilizations").observe.manage(Utilization)
23
- my_utilization = hakuban.object(["utilizations"],{id: id}).expose.manage(Utilization)
24
-
25
- previous_cpu_usage_state = nil
26
- loop {
27
- previous_cpu_usage_state, cpu_usage = CPU::current_usage(previous_cpu_usage_state)
28
- my_utilization.data = [cpu_usage]
29
-
30
- utilizations = all_utilizations.objects.values.sort_by(&:id).map { |utilization|
31
- [utilization.id, utilization.data&.first]
32
- }.select { |id, utilization| utilization }
33
- next sleep 1 if utilizations.empty?
34
-
35
- widest_id_width = utilizations.transpose[0].map(&:size).max
36
- utilizations.each.with_index { |(id, utilization), i|
37
- bars = utilization.map { |percentage| " ▁▂▃▄▅▆▇█"[percentage*9/100] }.join("")
38
- bg_color = [0,0,180 + (i % 2)*64]
39
- fg_color = [255,255,0]
40
- puts "%s -> \e[48;2;%i;%i;%im\e[38;2;%i;%i;%im%s\e[49m\e[39m"%([id.ljust(widest_id_width)]+bg_color+fg_color+[bars])
41
- }
42
-
43
- sleep 1
44
- #TODO: make this more friendly, save state, etc.
45
- print "\e[2J\e[f"
46
- }
@@ -1,26 +0,0 @@
1
- require 'hakuban'
2
- require_relative './cpu-usage.rb'
3
-
4
-
5
- id = "#{$$}@#{`hostname`.strip}"
6
-
7
- hakuban = Hakuban::LocalNode.new
8
- connector = Hakuban::WebsocketConnector.new("ws://localhost:3001").start(hakuban)
9
- all_utilizations = hakuban.tag("utilizations").observe
10
- my_utilization = hakuban.object(["utilizations"],{id: id}).expose
11
-
12
- previous_cpu_usage_state = nil
13
-
14
- loop {
15
- previous_cpu_usage_state, cpu_usage = CPU::current_usage(previous_cpu_usage_state)
16
- my_utilization.set_object_state([Time.new.to_f],[],[cpu_usage]) if cpu_usage
17
-
18
- all_utilizations.object_descriptors.map { |descriptor|
19
- if data = all_utilizations.object_state(descriptor).data
20
- puts "#{descriptor.json["id"]} -> #{data.first}"
21
- end
22
- }
23
-
24
- sleep 1
25
- puts
26
- }
@@ -1,16 +0,0 @@
1
- module CPU
2
-
3
- def self.current_usage(previous_cpu_usage_state)
4
- current_uptime = open("/proc/uptime") { |file| file.read.split(" ")[0].to_f }
5
- current_cpu_ticks = open("/proc/stat") { |file|
6
- file.read.split("\n").select { |line| line.start_with?(/cpu[0-9]+/) }.map { |line| line.split(" ")[4].to_f }
7
- }
8
- cpu_usage = previous_cpu_usage_state&.dig(0)&.zip(current_cpu_ticks)&.map { |old_ticks, new_ticks|
9
- [0,(100 - (new_ticks - old_ticks) / (current_uptime - previous_cpu_usage_state[1]))].max.floor
10
- }
11
- [[current_cpu_ticks, current_uptime], cpu_usage]
12
- end
13
-
14
- end
15
-
16
-
data/hakuban.gemspec DELETED
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/hakuban/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "hakuban"
7
- spec.version = Hakuban::VERSION
8
- spec.authors = ["yunta"]
9
- spec.email = ["maciej.blomberg@mikoton.com"]
10
-
11
- spec.summary = "Ruby binding for Hakuban library"
12
- spec.description = "Ruby binding for convenient data-object sharing library - Hakuban."
13
- spec.homepage = "https://gitlab.com/yunta/hakuban-ruby"
14
- spec.license = "MIT"
15
- spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
16
-
17
- #spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
18
-
19
- #spec.metadata["homepage_uri"] = spec.homepage
20
- #spec.metadata["source_code_uri"] = "https://gitlab.com/yunta/hakuban-ruby"
21
- #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
22
-
23
- # Specify which files should be added to the gem when it is released.
24
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
27
- end
28
- #spec.bindir = "exe"
29
- #spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
- spec.require_paths = ["lib"]
31
-
32
- spec.add_development_dependency "rspec", "~>3.10"
33
- spec.add_dependency "ffi", "~>1.15"
34
- spec.add_dependency "msgpack", "~>1.4"
35
- spec.add_dependency "json", "~>2.5"
36
- end