async-service 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 98f13081542982df8eeea93fc2ddf21f442693d1d4f15c48a272677640890edc
4
+ data.tar.gz: de30016e74b6a9bfd908355c4c7948f7d25c3deb11c1cb96d5b1636472a839bf
5
+ SHA512:
6
+ metadata.gz: 934012973a75ad970bcfed1de05d17895641dd21f438b78989c019f83c68770358c6b06f2cd153767429e8156b2d15ef0be02d84a74fc01a0d9fc9ce865d6482
7
+ data.tar.gz: 3a2880a72bd22554c47cce146a5c497e1a12173a967df0650983acaa25c79e20c9cf45bc7bc879fd5cc45cc27956c57afcd3f32f4b6c0dd16ed1d5d9452fbcaa
checksums.yaml.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ �Q3`������ u�,��rm�h����}� �~Y��~��8�=yN�����=�����q�r��:���d�Ȟ�+C�"�̿��+�Z�Os�5C�쁦�}VE�c~R9������ �-�V���酻r��M��#M�
2
  ���j��+�5����o�� �(���`=�r
3
+ "�eB�3U���U�L���=��Y������V ��Q�i�7�P�`=o'6K�`�J���$���򕎒�8�����$=$��fz�$�;�[��U���E"!H��� ^`����6q/C��$t����N�1/Q#l|S�/�`H�}+|Ԏ���԰Xo��%h���o�d�dS�W���2]�h�Ԛ��V/WwY#pj q��
data/bin/async-service ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'async/service'
4
+
5
+ configuration = Async::Service::Configuration.new
6
+
7
+ ARGV.each do |path|
8
+ configuration.load_file(path)
9
+ end
10
+
11
+ controller = Async::Service::Controller.new(configuration.services)
12
+
13
+ begin
14
+ Bundler.require(:preload)
15
+ rescue Bundler::GemfileNotFound
16
+ # Ignore.
17
+ end
18
+
19
+ if Process.respond_to?(:warmup)
20
+ Process.warmup
21
+ elsif GC.respond_to?(:compact)
22
+ GC.compact
23
+ end
24
+
25
+ controller.run
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative 'loader'
7
+ require_relative 'generic'
8
+
9
+ module Async
10
+ module Service
11
+ # Manages environments which describes how to host a specific set of services.
12
+ #
13
+ # Environments are key-value maps with lazy value resolution. An environment can inherit from a parent environment, which can provide defaults
14
+ class Configuration
15
+ # Initialize an empty configuration.
16
+ def initialize
17
+ @environments = []
18
+ end
19
+
20
+ attr :environments
21
+
22
+ def empty?
23
+ @environments.empty?
24
+ end
25
+
26
+ def services
27
+ return to_enum(:services) unless block_given?
28
+
29
+ @environments.each do |environment|
30
+ yield Generic.wrap(environment)
31
+ end
32
+ end
33
+
34
+ # Add the environment to the configuration.
35
+ def add(environment)
36
+ @environments << environment
37
+ end
38
+
39
+ # Load the specified configuration file. See {Loader#load_file} for more details.
40
+ def load_file(path)
41
+ Loader.load_file(self, path)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require 'async/container/controller'
7
+
8
+ module Async
9
+ module Service
10
+ class Controller < Async::Container::Controller
11
+ def initialize(services)
12
+ @services = services
13
+ end
14
+
15
+ # Start all named services.
16
+ def start
17
+ @services.each do |service|
18
+ service.start
19
+ end
20
+
21
+ super
22
+ end
23
+
24
+ # Setup all named services into the given container.
25
+ #
26
+ # @parameter container [Async::Container::Generic]
27
+ def setup(container)
28
+ super
29
+
30
+ @services.each do |service|
31
+ service.setup(container)
32
+ end
33
+
34
+ return container
35
+ end
36
+
37
+ # Stop all named services.
38
+ def stop(graceful = true)
39
+ @services.each do |service|
40
+ begin
41
+ service.stop
42
+ rescue => error
43
+ Console.error(self, error)
44
+ end
45
+ end
46
+
47
+ super
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ module Async
7
+ module Service
8
+ class Environment
9
+ class Builder < BasicObject
10
+ def self.for(initial, block)
11
+ builder = self.new(initial.dup)
12
+
13
+ builder.instance_exec(&block) if block
14
+
15
+ return builder
16
+ end
17
+
18
+ def initialize(cache = ::Hash.new)
19
+ @cache = cache
20
+ end
21
+
22
+ def include(target)
23
+ target.to_h.each do |key, value|
24
+ @cache[key] = value
25
+ end
26
+ end
27
+
28
+ def method_missing(name, argument = nil, &block)
29
+ previous = @cache[name]
30
+
31
+ if block
32
+ if block.arity == 0
33
+ @cache[name] = block
34
+ else
35
+ # Bind the |previous| argument to the block:
36
+ @cache[name] = ::Kernel.lambda{self.instance_exec(previous, &block)}
37
+ end
38
+ elsif previous.is_a?(::Array)
39
+ @cache[name] = previous + argument
40
+ elsif previous.is_a?(::Hash)
41
+ @cache[name] = previous.merge(argument)
42
+ else
43
+ @cache[name] = argument
44
+ end
45
+ end
46
+
47
+ def to_h
48
+ @cache
49
+ end
50
+ end
51
+
52
+ def initialize(**initial, &block)
53
+ @block = block
54
+ @initial = initial
55
+ end
56
+
57
+ def builder
58
+ Builder.for(@initial, @block)
59
+ end
60
+
61
+ # An evaluator is lazy read-only view of an environment. It allows for the evaluation of procs and other dynamic values.
62
+ # Those values are cached, and thus the evaluator is not thread safe.
63
+ class Evaluator < BasicObject
64
+ def initialize(source)
65
+ @source = source
66
+ @cache = {}
67
+ end
68
+
69
+ def inspect
70
+ "#<#{Evaluator} #{@source}>"
71
+ end
72
+
73
+ private def __evaluate__(value)
74
+ case value
75
+ when ::Array
76
+ value.collect{|item| __evaluate__(item)}
77
+ when ::Hash
78
+ value.transform_values{|item| __evaluate__(item)}
79
+ # when ::Symbol
80
+ # self[value]
81
+ when ::Proc
82
+ __evaluate__(instance_exec(&value))
83
+ else
84
+ value
85
+ end
86
+ end
87
+
88
+ def [](key)
89
+ @cache.fetch(key) do
90
+ @cache[key] = __evaluate__(@source[key])
91
+ end
92
+ end
93
+
94
+ def respond_to?(name, include_all = false)
95
+ @source.key?(name) || super
96
+ end
97
+
98
+ def method_missing(name, ...)
99
+ if @source.key?(name)
100
+ self[name]
101
+ else
102
+ super
103
+ end
104
+ end
105
+
106
+ def to_h
107
+ # Ensure all keys are evaluated:
108
+ @source.each_key{|key| self[key]}
109
+
110
+ return @cache
111
+ end
112
+
113
+ def key?(key)
114
+ @source.key?(key)
115
+ end
116
+ end
117
+
118
+ def evaluator
119
+ Evaluator.new(builder.to_h)
120
+ end
121
+
122
+ def to_h
123
+ evaluator.to_h
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ module Async
7
+ module Service
8
+ # Captures the stateful behaviour of a specific service.
9
+ # Specifies the interfaces required by derived classes.
10
+ #
11
+ # Designed to be invoked within an {Async::Controller::Container}.
12
+ class Generic
13
+ # Convert the given environment into a service if possible.
14
+ # @parameter environment [Build::Environment] The environment to use to construct the service.
15
+ def self.wrap(environment)
16
+ evaluator = environment.evaluator
17
+ service_class = evaluator.service_class || self
18
+
19
+ return service_class.new(environment)
20
+ end
21
+
22
+ # Initialize the service from the given environment.
23
+ # @parameter environment [Build::Environment]
24
+ def initialize(environment)
25
+ @environment = environment
26
+ @evaluator = @environment.evaluator
27
+ end
28
+
29
+ def to_h
30
+ @evaluator.to_h
31
+ end
32
+
33
+ # The name of the service - used for informational purposes like logging.
34
+ # e.g. `myapp.com`.
35
+ def name
36
+ @evaluator.name
37
+ end
38
+
39
+ # Start the service.
40
+ def start
41
+ Console.debug(self) {"Starting service #{self.name}..."}
42
+ end
43
+
44
+ # Setup the service into the specified container.
45
+ # @parameter container [Async::Container::Generic]
46
+ def setup(container)
47
+ Console.debug(self) {"Setting up service #{self.name}..."}
48
+ end
49
+
50
+ # Stop the service.
51
+ def stop(graceful = true)
52
+ Console.debug(self) {"Stopping service #{self.name}..."}
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative 'environment'
7
+
8
+ module Async
9
+ module Service
10
+ # The domain specific language for loading configuration files.
11
+ class Loader
12
+ # Initialize the loader, attached to a specific configuration instance.
13
+ # Any environments generated by the loader will be added to the configuration.
14
+ # @parameter configuration [Configuration]
15
+ # @parameter root [String] The file-system root path for relative path computations.
16
+ def initialize(configuration, root = nil)
17
+ @configuration = configuration
18
+ @root = root
19
+ end
20
+
21
+ # The file-system root path which is injected into the environments as required.
22
+ # @attribute [String]
23
+ attr :root
24
+
25
+ # Load the specified file into the given configuration.
26
+ # @parameter configuration [Configuration]
27
+ # @oaram path [String] The path to the configuration file, e.g. `falcon.rb`.
28
+ def self.load_file(configuration, path)
29
+ realpath = ::File.realpath(path)
30
+ root = ::File.dirname(realpath)
31
+
32
+ loader = self.new(configuration, root)
33
+
34
+ if ::Module.method_defined?(:set_temporary_name)
35
+ loader.singleton_class.set_temporary_name("#{self}[#{path.inspect}]")
36
+ end
37
+
38
+ loader.instance_eval(File.read(path), path)
39
+ end
40
+
41
+ # Create an environment.
42
+ def environment(**initial, &block)
43
+ Environment.new(**initial, &block)
44
+ end
45
+
46
+ # Define a host with the specified name.
47
+ # Adds `root` and `authority` keys.
48
+ # @parameter name [String] The name of the environment, usually a hostname.
49
+ def service(name, &block)
50
+ @configuration.add(self.environment(name: name, root: @root, &block))
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ module Async
7
+ module Service
8
+ VERSION = "0.1.0"
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative 'service/configuration'
7
+ require_relative 'service/controller'
8
+ require_relative 'service/version'
data/license.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright, 2024, by Samuel Williams.
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/readme.md ADDED
@@ -0,0 +1,23 @@
1
+ # Async::Service
2
+
3
+ Provides a simple service interface for configuring and running services.
4
+
5
+ [![Development Status](https://github.com/socketry/async-service/workflows/Test/badge.svg)](https://github.com/socketry/async-service/actions?workflow=Test)
6
+
7
+ ## Contributing
8
+
9
+ We welcome contributions to this project.
10
+
11
+ 1. Fork it.
12
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
13
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
14
+ 4. Push to the branch (`git push origin my-new-feature`).
15
+ 5. Create new Pull Request.
16
+
17
+ ### Developer Certificate of Origin
18
+
19
+ This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted.
20
+
21
+ ### Contributor Covenant
22
+
23
+ This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: async-service
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
14
+ ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
15
+ CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
16
+ MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
17
+ MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
18
+ bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
19
+ igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
20
+ 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
21
+ sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
22
+ e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
23
+ XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
24
+ RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
25
+ tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
26
+ zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
27
+ xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
28
+ BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
29
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
30
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
31
+ cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
32
+ xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
33
+ c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
34
+ 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
35
+ JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
36
+ eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
37
+ Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
38
+ voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
39
+ -----END CERTIFICATE-----
40
+ date: 2024-02-19 00:00:00.000000000 Z
41
+ dependencies:
42
+ - !ruby/object:Gem::Dependency
43
+ name: async
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: async-container
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 0.16.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 0.16.0
70
+ description:
71
+ email:
72
+ executables:
73
+ - async-service
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - bin/async-service
78
+ - lib/async/service.rb
79
+ - lib/async/service/configuration.rb
80
+ - lib/async/service/controller.rb
81
+ - lib/async/service/environment.rb
82
+ - lib/async/service/generic.rb
83
+ - lib/async/service/loader.rb
84
+ - lib/async/service/version.rb
85
+ - license.md
86
+ - readme.md
87
+ homepage: https://github.com/socketry/async-service
88
+ licenses:
89
+ - MIT
90
+ metadata:
91
+ documentation_uri: https://socketry.github.io/async-service/
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '3.0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubygems_version: 3.5.3
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: A service layer for Async.
111
+ test_files: []
metadata.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ bq��3���v�k?ˎ�ݔChb�e���ݟ0���"Eˈ��>�����}{�p�UҜ�e
2
+ ��H�������