async-tools 0.2.9 → 0.2.10
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/Gemfile.lock +1 -1
- data/lib/async/app/autoload_component.rb +5 -0
- data/lib/async/app/component.rb +15 -1
- data/lib/async/app/event_logger.rb +1 -1
- data/lib/async/app/injector.rb +1 -1
- data/lib/async/app/metrics/components_metrics_collector.rb +21 -0
- data/lib/async/app/metrics/ruby_runtime_metrics_collector.rb +7 -8
- data/lib/async/app/timer_component.rb +46 -0
- data/lib/async/app/{web_server → web_apps}/health_app.rb +4 -4
- data/lib/async/app/{web_server → web_apps}/metrics_app/serializer.rb +1 -1
- data/lib/async/app/{web_server → web_apps}/metrics_app/store.rb +1 -1
- data/lib/async/app/{web_server → web_apps}/metrics_app.rb +7 -4
- data/lib/async/app/web_component.rb +15 -0
- data/lib/async/app/web_server.rb +24 -7
- data/lib/async/app.rb +39 -39
- data/lib/async/timer.rb +6 -4
- data/lib/async/tools/version.rb +1 -1
- data/lib/async/tools.rb +3 -2
- metadata +10 -7
- data/lib/async/app/web_server/router.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d1ee91075ee99708de1e43d75c7184d9a23c79a0cf36291d91d7ede9314d22d
|
4
|
+
data.tar.gz: b49e3539aadec63fa3aeed3c3e95bb6bbeca1ef33ef3487bf406b8a28430941d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02e5dce453e331558d760ff45769f466192e1b95963373e23cd9668c364bc131902f3d09457363b987308326def15e841651c3311b09d1a1878c61fba03b595d
|
7
|
+
data.tar.gz: ab0d8f31349e0514d14ff1c232072c530658db77c6302e5af13238b8bb25cc96226e3b8d7075367653c62095dcf1ca9e64b2120a234364119ed3310e126c2a7f
|
data/Gemfile.lock
CHANGED
data/lib/async/app/component.rb
CHANGED
@@ -7,5 +7,19 @@ module Async::App::Component
|
|
7
7
|
base.include(Async::Logger)
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def start!
|
11
|
+
init!
|
12
|
+
after_init
|
13
|
+
run!
|
14
|
+
after_run
|
15
|
+
end
|
16
|
+
|
17
|
+
def init! = nil
|
18
|
+
def run! = info { "Started" }
|
19
|
+
|
20
|
+
# TODO: unsubscribe from everything on stop
|
21
|
+
def stop! = info { "Stopped" }
|
22
|
+
|
23
|
+
def after_init = nil
|
24
|
+
def after_run = nil
|
11
25
|
end
|
data/lib/async/app/injector.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Async::App::Metrics::ComponentsMetricsCollector
|
4
|
+
include Async::App::TimerComponent
|
5
|
+
include Async::App::AutoloadComponent
|
6
|
+
|
7
|
+
def on_tick = bus.publish("metrics.updated", metrics)
|
8
|
+
def interval = 5
|
9
|
+
def run_on_start = true
|
10
|
+
def on_error(exception) = warn { exception }
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def metrics
|
15
|
+
{
|
16
|
+
async_app_components: { value: Async::App.instance.components.count },
|
17
|
+
async_app_autoloadable_components: { value: Async::App.instance.autoloadable_components.count },
|
18
|
+
async_app_timer_components: { value: Async::App.instance.timer_components.count }
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
@@ -1,16 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Async::App::Metrics::RubyRuntimeMetricsCollector
|
4
|
-
include Async::App::
|
4
|
+
include Async::App::TimerComponent
|
5
|
+
include Async::App::AutoloadComponent
|
5
6
|
|
6
|
-
|
7
|
+
def on_tick = bus.publish("metrics.updated", metrics)
|
8
|
+
def interval = 5
|
9
|
+
def run_on_start = true
|
10
|
+
def on_error(exception) = warn { exception }
|
7
11
|
|
8
|
-
|
9
|
-
Async::Timer.new(INTERVAL, run_on_start: true, on_error: ->(e) { warn(e) }) do
|
10
|
-
bus.publish("metrics.updated", metrics)
|
11
|
-
end
|
12
|
-
info { "Started" }
|
13
|
-
end
|
12
|
+
private
|
14
13
|
|
15
14
|
def metrics
|
16
15
|
fibers = ObjectSpace.each_object(Fiber)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Async::App::TimerComponent
|
4
|
+
def self.included(base)
|
5
|
+
base.include(Async::App::Component)
|
6
|
+
base.include(InstanceMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
def init!
|
11
|
+
super
|
12
|
+
@timer = Async::Timer.new(run_on_start:, start: false, on_error: method(:on_error)) { tick! }
|
13
|
+
end
|
14
|
+
|
15
|
+
def run!
|
16
|
+
return if @timer.active?
|
17
|
+
|
18
|
+
@timer.start(interval)
|
19
|
+
info { "Started. Interval = #{interval}" }
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop!
|
23
|
+
@timer&.stop
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# TimerComponent - specific methods
|
28
|
+
def tick!
|
29
|
+
debug { "Started" }
|
30
|
+
on_tick
|
31
|
+
debug { "Finished" }
|
32
|
+
end
|
33
|
+
|
34
|
+
def restart!
|
35
|
+
@timer.restart(interval)
|
36
|
+
info { "Restarted. Polling interval=#{interval}" }
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def interval = raise NotImplementedError
|
42
|
+
def run_on_start = raise NotImplementedError
|
43
|
+
def on_tick = raise NotImplementedError
|
44
|
+
def on_error(exception) = raise exception
|
45
|
+
end
|
46
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class Async::App::
|
4
|
-
include Async::App::
|
3
|
+
class Async::App::WebApps::HealthApp
|
4
|
+
include Async::App::WebComponent
|
5
|
+
include Async::App::AutoloadComponent
|
5
6
|
|
6
7
|
PATHS = ["/health", "/health/"].freeze
|
7
8
|
|
8
|
-
def
|
9
|
+
def after_init
|
9
10
|
@healthy = false
|
10
|
-
|
11
11
|
bus.subscribe("health.updated") { @healthy = _1 }
|
12
12
|
end
|
13
13
|
|
@@ -1,13 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class Async::App::
|
4
|
-
include Async::App::
|
3
|
+
class Async::App::WebApps::MetricsApp
|
4
|
+
include Async::App::WebComponent
|
5
|
+
include Async::App::AutoloadComponent
|
5
6
|
|
6
7
|
PATHS = ["/metrics", "/metrics/"].freeze
|
7
8
|
|
8
|
-
|
9
|
+
inject :async_app_name
|
10
|
+
|
11
|
+
def after_init
|
9
12
|
store = Store.new
|
10
|
-
@serializer = Serializer.new(prefix:
|
13
|
+
@serializer = Serializer.new(prefix: async_app_name, store:)
|
11
14
|
|
12
15
|
bus.subscribe("metrics.updated") do |metrics|
|
13
16
|
metrics.each { store.set(_1, **_2) }
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Async::App::WebComponent
|
4
|
+
def self.included(base)
|
5
|
+
base.include(Async::App::Component)
|
6
|
+
base.include(InstanceMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
def run! = bus.publish(Async::App::WebServer::APP_ADDED, self)
|
11
|
+
|
12
|
+
def can_handle?(request) = raise NotImplementedError
|
13
|
+
def call(*) = raise NotImplementedError
|
14
|
+
end
|
15
|
+
end
|
data/lib/async/app/web_server.rb
CHANGED
@@ -1,17 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Async::App::WebServer
|
4
|
-
include Async::
|
4
|
+
include Async::App::Component
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
APP_ADDED = "async-app.web_app.added"
|
7
|
+
|
8
|
+
class Router
|
9
|
+
def initialize
|
10
|
+
@apps = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(app) = @apps << app
|
14
|
+
|
15
|
+
def call(request)
|
16
|
+
@apps.reverse_each { return Protocol::HTTP::Response[*_1.call(request)] if _1.can_handle?(request) }
|
17
|
+
|
18
|
+
Protocol::HTTP::Response[404, {}, ["Not found"]]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(port: 8080)
|
23
|
+
@router = Router.new
|
11
24
|
@endpoint = Async::HTTP::Endpoint.parse("http://0.0.0.0:#{port}")
|
12
25
|
end
|
13
26
|
|
14
|
-
def
|
27
|
+
def after_init = bus.subscribe(APP_ADDED) { add_app(_1) }
|
28
|
+
|
29
|
+
def add_app(app) = @router.add(app)
|
30
|
+
|
31
|
+
def run!
|
15
32
|
Async { Async::HTTP::Server.new(@router, @endpoint).run }
|
16
33
|
info { "Started on #{@endpoint.url}" }
|
17
34
|
end
|
data/lib/async/app.rb
CHANGED
@@ -1,68 +1,68 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Async::App
|
4
|
-
|
4
|
+
include Component
|
5
5
|
|
6
|
-
|
6
|
+
class << self
|
7
|
+
def instance = @instance = instances(self).first
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
# rubocop:disable Style/GlobalVars
|
11
|
-
def initialize
|
12
|
-
raise "only one instance of #{self.class} is allowed" if $__ASYNC_APP
|
9
|
+
def instances(klass) = ObjectSpace.each_object(klass)
|
10
|
+
end
|
13
11
|
|
14
|
-
|
15
|
-
|
12
|
+
def init!
|
13
|
+
raise "only one instance of #{self.class} is allowed" if instances(self.class).count > 1
|
16
14
|
|
15
|
+
@parent = Async::Task.current
|
17
16
|
set_traps!
|
18
17
|
init_container!
|
19
|
-
|
20
|
-
start_event_logger!
|
21
|
-
start_web_server!
|
22
|
-
|
23
|
-
start_runtime_metrics_collector!
|
24
|
-
|
25
|
-
run!
|
26
|
-
|
27
|
-
info { "Started" }
|
28
|
-
bus.publish("health.updated", true)
|
18
|
+
super
|
29
19
|
rescue StandardError => e
|
30
20
|
fatal { e }
|
31
|
-
stop
|
21
|
+
stop!
|
32
22
|
exit(1)
|
33
23
|
end
|
34
24
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
def app_name = :async_app
|
39
|
-
|
40
|
-
def stop
|
41
|
-
@task&.stop
|
42
|
-
$__ASYNC_APP = nil
|
43
|
-
info { "Stopped" }
|
25
|
+
def stop!
|
26
|
+
@parent&.stop(true)
|
27
|
+
super
|
44
28
|
end
|
45
29
|
|
46
|
-
|
30
|
+
def container = @container ||= Dry::Container.new
|
31
|
+
def instances(klass) = self.class.instances(klass)
|
32
|
+
def components = instances(Class).select { _1.included_modules.include?(Component) }.reject { _1 <= self.class }
|
33
|
+
def autoloadable_components = components.select { _1.included_modules.include?(AutoloadComponent) }
|
34
|
+
def timer_components = components.select { _1.included_modules.include?(TimerComponent) }
|
47
35
|
|
48
36
|
private
|
49
37
|
|
38
|
+
def container_config = {}
|
39
|
+
def async_app_name = :async_app
|
40
|
+
|
41
|
+
def run!
|
42
|
+
start_event_logger!
|
43
|
+
start_web_server!
|
44
|
+
|
45
|
+
autoload_components!
|
46
|
+
super
|
47
|
+
bus.publish("health.updated", true)
|
48
|
+
end
|
49
|
+
|
50
50
|
def set_traps!
|
51
51
|
trap("INT") do
|
52
52
|
force_exit! if @stopping
|
53
53
|
@stopping = true
|
54
54
|
warn { "Interrupted, stopping. Press ^C once more to force exit." }
|
55
|
-
stop
|
55
|
+
stop!
|
56
56
|
end
|
57
57
|
|
58
|
-
trap("TERM") { stop }
|
58
|
+
trap("TERM") { stop! }
|
59
59
|
end
|
60
60
|
|
61
61
|
def init_container!
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
container.register(:bus, Async::Bus.new)
|
63
|
+
container.register(:async_app_name, async_app_name)
|
64
|
+
|
65
|
+
container_config.each { container.register(_1, _2) }
|
66
66
|
end
|
67
67
|
|
68
68
|
def force_exit!
|
@@ -70,7 +70,7 @@ class Async::App
|
|
70
70
|
exit(1)
|
71
71
|
end
|
72
72
|
|
73
|
-
def
|
74
|
-
def
|
75
|
-
def
|
73
|
+
def autoload_components! = autoloadable_components.each { _1.new.start! }
|
74
|
+
def start_web_server! = WebServer.new.start!
|
75
|
+
def start_event_logger! = EventLogger.new.start!
|
76
76
|
end
|
data/lib/async/timer.rb
CHANGED
@@ -7,7 +7,7 @@ class Async::Timer
|
|
7
7
|
|
8
8
|
class AlreadyStarted < Error; end
|
9
9
|
|
10
|
-
def initialize(delay, # rubocop:disable Metrics/CyclomaticComplexity,Metrics/ParameterLists
|
10
|
+
def initialize(delay = nil, # rubocop:disable Metrics/CyclomaticComplexity,Metrics/ParameterLists
|
11
11
|
repeat: true,
|
12
12
|
start: true,
|
13
13
|
run_on_start: false,
|
@@ -32,14 +32,16 @@ class Async::Timer
|
|
32
32
|
|
33
33
|
def active? = @active
|
34
34
|
|
35
|
-
def restart
|
35
|
+
def restart(delay = @delay, run: false)
|
36
36
|
stop
|
37
|
-
start
|
37
|
+
start(delay, run:)
|
38
38
|
end
|
39
39
|
|
40
|
-
def start(run: false)
|
40
|
+
def start(delay = @delay, run: false)
|
41
41
|
raise AlreadyStarted, "Timer already started" if active?
|
42
|
+
raise ArgumentError, "delay cannot be nil" if delay.nil?
|
42
43
|
|
44
|
+
@delay = delay
|
43
45
|
@active = true
|
44
46
|
|
45
47
|
@task = @parent.async do
|
data/lib/async/tools/version.rb
CHANGED
data/lib/async/tools.rb
CHANGED
@@ -13,8 +13,6 @@ loader.tag = File.basename(__FILE__, ".rb")
|
|
13
13
|
loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
|
14
14
|
loader.push_dir(File.expand_path("..", __dir__.to_s))
|
15
15
|
|
16
|
-
loader.setup
|
17
|
-
|
18
16
|
module Async
|
19
17
|
# Your code goes here...
|
20
18
|
module Tools # rubocop:disable Style/ClassAndModuleChildren
|
@@ -30,3 +28,6 @@ module Async
|
|
30
28
|
end
|
31
29
|
end
|
32
30
|
end
|
31
|
+
|
32
|
+
loader.setup
|
33
|
+
loader.eager_load
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gleb Sinyavskiy
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -61,16 +61,19 @@ files:
|
|
61
61
|
- bin/console
|
62
62
|
- bin/setup
|
63
63
|
- lib/async/app.rb
|
64
|
+
- lib/async/app/autoload_component.rb
|
64
65
|
- lib/async/app/component.rb
|
65
66
|
- lib/async/app/event_logger.rb
|
66
67
|
- lib/async/app/injector.rb
|
68
|
+
- lib/async/app/metrics/components_metrics_collector.rb
|
67
69
|
- lib/async/app/metrics/ruby_runtime_metrics_collector.rb
|
70
|
+
- lib/async/app/timer_component.rb
|
71
|
+
- lib/async/app/web_apps/health_app.rb
|
72
|
+
- lib/async/app/web_apps/metrics_app.rb
|
73
|
+
- lib/async/app/web_apps/metrics_app/serializer.rb
|
74
|
+
- lib/async/app/web_apps/metrics_app/store.rb
|
75
|
+
- lib/async/app/web_component.rb
|
68
76
|
- lib/async/app/web_server.rb
|
69
|
-
- lib/async/app/web_server/health_app.rb
|
70
|
-
- lib/async/app/web_server/metrics_app.rb
|
71
|
-
- lib/async/app/web_server/metrics_app/serializer.rb
|
72
|
-
- lib/async/app/web_server/metrics_app/store.rb
|
73
|
-
- lib/async/app/web_server/router.rb
|
74
77
|
- lib/async/bus.rb
|
75
78
|
- lib/async/cache.rb
|
76
79
|
- lib/async/channel.rb
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Async::App::WebServer::Router
|
4
|
-
extend Async::App::Injector
|
5
|
-
|
6
|
-
def initialize(*apps)
|
7
|
-
@apps = apps
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(request)
|
11
|
-
@apps.each { return Protocol::HTTP::Response[*_1.call(request)] if _1.can_handle?(request) }
|
12
|
-
|
13
|
-
Protocol::HTTP::Response[404, {}, ["Not found"]]
|
14
|
-
end
|
15
|
-
end
|