async-service 0.12.0 → 0.14.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/agent.md +129 -0
- data/bin/async-service +2 -1
- data/context/best-practices.md +320 -0
- data/context/getting-started.md +190 -0
- data/context/index.yaml +20 -0
- data/context/service-architecture.md +499 -0
- data/lib/async/service/configuration.rb +30 -7
- data/lib/async/service/container_environment.rb +42 -0
- data/lib/async/service/container_service.rb +77 -0
- data/lib/async/service/controller.rb +23 -3
- data/lib/async/service/environment.rb +63 -1
- data/lib/async/service/formatting.rb +84 -0
- data/lib/async/service/generic.rb +49 -7
- data/lib/async/service/loader.rb +12 -7
- data/lib/async/service/version.rb +2 -2
- data/lib/async/service.rb +11 -4
- data/license.md +1 -1
- data/readme.md +71 -4
- data/releases.md +85 -0
- data.tar.gz.sig +0 -0
- metadata +14 -9
- metadata.gz.sig +0 -0
@@ -1,16 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2024, by Samuel Williams.
|
4
|
+
# Copyright, 2024-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
6
|
+
require "async/container/controller"
|
7
7
|
|
8
8
|
module Async
|
9
9
|
module Service
|
10
|
+
# Controls multiple services and their lifecycle.
|
11
|
+
#
|
12
|
+
# The controller manages starting, stopping, and monitoring multiple services
|
13
|
+
# within containers. It extends Async::Container::Controller to provide
|
14
|
+
# service-specific functionality.
|
10
15
|
class Controller < Async::Container::Controller
|
16
|
+
# Warm up the Ruby process by preloading gems and running GC.
|
11
17
|
def self.warmup
|
12
18
|
begin
|
13
|
-
require
|
19
|
+
require "bundler"
|
14
20
|
Bundler.require(:preload)
|
15
21
|
rescue Bundler::GemfileNotFound, LoadError
|
16
22
|
# Ignore.
|
@@ -24,6 +30,9 @@ module Async
|
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
33
|
+
# Run a configuration of services.
|
34
|
+
# @parameter configuration [Configuration] The service configuration to run.
|
35
|
+
# @parameter options [Hash] Additional options for the controller.
|
27
36
|
def self.run(configuration, **options)
|
28
37
|
controller = Async::Service::Controller.new(configuration.services.to_a, **options)
|
29
38
|
|
@@ -32,6 +41,17 @@ module Async
|
|
32
41
|
controller.run
|
33
42
|
end
|
34
43
|
|
44
|
+
# Create a controller for the given services.
|
45
|
+
# @parameter services [Array(Generic)] The services to control.
|
46
|
+
# @parameter options [Hash] Additional options for the controller.
|
47
|
+
# @returns [Controller] A new controller instance.
|
48
|
+
def self.for(*services, **options)
|
49
|
+
self.new(services, **options)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Initialize a new controller with services.
|
53
|
+
# @parameter services [Array(Generic)] The services to manage.
|
54
|
+
# @parameter options [Hash] Options passed to the parent controller.
|
35
55
|
def initialize(services, **options)
|
36
56
|
super(**options)
|
37
57
|
|
@@ -5,8 +5,17 @@
|
|
5
5
|
|
6
6
|
module Async
|
7
7
|
module Service
|
8
|
+
# Represents a service configuration with lazy evaluation and module composition.
|
9
|
+
#
|
10
|
+
# Environments store configuration as methods that can be overridden and composed using Ruby modules. They support lazy evaluation through evaluators.
|
8
11
|
class Environment
|
12
|
+
# A builder for constructing environments using a DSL.
|
9
13
|
class Builder < BasicObject
|
14
|
+
# Create a new environment with facets and values.
|
15
|
+
# @parameter facets [Array(Module)] Modules to include in the environment.
|
16
|
+
# @parameter values [Hash] Key-value pairs to define as methods.
|
17
|
+
# @parameter block [Proc] A block for additional configuration.
|
18
|
+
# @returns [Module] The constructed environment module.
|
10
19
|
def self.for(*facets, **values, &block)
|
11
20
|
top = ::Module.new
|
12
21
|
|
@@ -46,10 +55,14 @@ module Async
|
|
46
55
|
return top
|
47
56
|
end
|
48
57
|
|
58
|
+
# Initialize a new builder.
|
59
|
+
# @parameter facet [Module] The module to build into, defaults to a new `Module`.
|
49
60
|
def initialize(facet = ::Module.new)
|
50
61
|
@facet = facet
|
51
62
|
end
|
52
63
|
|
64
|
+
# Include a module or other includable object into the environment.
|
65
|
+
# @parameter target [Module] The module to include.
|
53
66
|
def include(target)
|
54
67
|
if target.class == ::Module
|
55
68
|
@facet.include(target)
|
@@ -60,6 +73,10 @@ module Async
|
|
60
73
|
end
|
61
74
|
end
|
62
75
|
|
76
|
+
# Define methods dynamically on the environment.
|
77
|
+
# @parameter name [Symbol] The method name to define.
|
78
|
+
# @parameter argument [Object] The value to return from the method.
|
79
|
+
# @parameter block [Proc] A block to use as the method implementation.
|
63
80
|
def method_missing(name, argument = nil, &block)
|
64
81
|
if block
|
65
82
|
@facet.define_method(name, &block)
|
@@ -67,12 +84,26 @@ module Async
|
|
67
84
|
@facet.define_method(name){argument}
|
68
85
|
end
|
69
86
|
end
|
87
|
+
|
88
|
+
# Always respond to missing methods for dynamic method definition.
|
89
|
+
# @parameter name [Symbol] The method name.
|
90
|
+
# @parameter include_private [Boolean] Whether to include private methods.
|
91
|
+
# @returns [Boolean] Always true to enable dynamic method definition.
|
92
|
+
def respond_to_missing?(name, include_private = false)
|
93
|
+
true
|
94
|
+
end
|
70
95
|
end
|
71
96
|
|
97
|
+
# Build a new environment using the builder DSL.
|
98
|
+
# @parameter arguments [Array] Arguments passed to Builder.for
|
99
|
+
# @returns [Environment] A new environment instance.
|
72
100
|
def self.build(...)
|
73
101
|
Environment.new(Builder.for(...))
|
74
102
|
end
|
75
103
|
|
104
|
+
# Initialize a new environment.
|
105
|
+
# @parameter facet [Module] The facet module containing the configuration methods.
|
106
|
+
# @parameter parent [Environment | Nil] The parent environment for inheritance.
|
76
107
|
def initialize(facet = ::Module.new, parent = nil)
|
77
108
|
unless facet.class == ::Module
|
78
109
|
raise ArgumentError, "Facet must be a module!"
|
@@ -88,21 +119,32 @@ module Async
|
|
88
119
|
# @attribute [Environment | Nil] The parent environment, if any.
|
89
120
|
attr :parent
|
90
121
|
|
122
|
+
# Include this environment's facet into a target module.
|
123
|
+
# @parameter target [Module] The target module to include into.
|
91
124
|
def included(target)
|
92
125
|
@parent&.included(target)
|
93
126
|
target.include(@facet)
|
94
127
|
end
|
95
128
|
|
129
|
+
# Create a new environment with additional configuration.
|
130
|
+
# @parameter arguments [Array] Arguments passed to Environment.build.
|
131
|
+
# @returns [Environment] A new environment with this as parent.
|
96
132
|
def with(...)
|
97
133
|
return self.class.new(Builder.for(...), self)
|
98
134
|
end
|
99
135
|
|
136
|
+
# Check if this environment implements a given interface.
|
137
|
+
# @parameter interface [Module] The interface to check.
|
138
|
+
# @returns [Boolean] True if this environment implements the interface.
|
100
139
|
def implements?(interface)
|
101
140
|
@facet <= interface
|
102
141
|
end
|
103
142
|
|
104
143
|
# An evaluator is lazy read-only view of an environment. It memoizes all method calls.
|
105
144
|
class Evaluator
|
145
|
+
# Create an evaluator wrapper for an environment.
|
146
|
+
# @parameter environment [Environment] The environment to wrap.
|
147
|
+
# @returns [Evaluator] A new evaluator instance.
|
106
148
|
def self.wrap(environment)
|
107
149
|
evaluator = ::Class.new(self)
|
108
150
|
|
@@ -133,14 +175,19 @@ module Async
|
|
133
175
|
return evaluator.new
|
134
176
|
end
|
135
177
|
|
178
|
+
# Initialize a new evaluator.
|
136
179
|
def initialize
|
137
180
|
@cache = {}
|
138
181
|
end
|
139
182
|
|
183
|
+
# Inspect representation of the evaluator.
|
184
|
+
# @returns [String] A string representation of the evaluator with its keys.
|
140
185
|
def inspect
|
141
186
|
"#<#{Evaluator} #{self.keys}>"
|
142
187
|
end
|
143
188
|
|
189
|
+
# Convert the evaluator to a hash.
|
190
|
+
# @returns [Hash] A hash with all evaluated keys and values.
|
144
191
|
def to_h
|
145
192
|
# Ensure all keys are evaluated:
|
146
193
|
self.keys.each do |name|
|
@@ -150,23 +197,38 @@ module Async
|
|
150
197
|
return @cache
|
151
198
|
end
|
152
199
|
|
200
|
+
# Convert the evaluator to JSON.
|
201
|
+
# @parameter arguments [Array] Arguments passed to to_json.
|
202
|
+
# @returns [String] A JSON representation of the evaluator.
|
153
203
|
def to_json(...)
|
154
204
|
self.to_h.to_json(...)
|
155
205
|
end
|
156
206
|
|
207
|
+
# Get value for a given key.
|
208
|
+
# @parameter key [Symbol] The key to look up.
|
209
|
+
# @returns [Object, nil] The value for the key, or nil if not found.
|
157
210
|
def [](key)
|
158
|
-
self.
|
211
|
+
if self.key?(key)
|
212
|
+
self.__send__(key)
|
213
|
+
end
|
159
214
|
end
|
160
215
|
|
216
|
+
# Check if a key is available.
|
217
|
+
# @parameter key [Symbol] The key to check.
|
218
|
+
# @returns [Boolean] True if the key exists.
|
161
219
|
def key?(key)
|
162
220
|
self.keys.include?(key)
|
163
221
|
end
|
164
222
|
end
|
165
223
|
|
224
|
+
# Create an evaluator for this environment.
|
225
|
+
# @returns [Evaluator] A lazy evaluator for this environment.
|
166
226
|
def evaluator
|
167
227
|
return Evaluator.wrap(self)
|
168
228
|
end
|
169
229
|
|
230
|
+
# Convert the environment to a hash.
|
231
|
+
# @returns [Hash] A hash representation of the environment.
|
170
232
|
def to_h
|
171
233
|
evaluator.to_h
|
172
234
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
module Async
|
7
|
+
module Service
|
8
|
+
# Formatting utilities for service titles.
|
9
|
+
#
|
10
|
+
# Services need meaningful process/thread names for monitoring and debugging. This module provides consistent formatting for common service metrics like connection counts, request ratios, and load values in process titles.
|
11
|
+
#
|
12
|
+
# It is expected you will include these into your service class and use them to update the `instance.name` in the health check.
|
13
|
+
module Formatting
|
14
|
+
UNITS = [nil, "K", "M", "B", "T", "P", "E", "Z", "Y"]
|
15
|
+
|
16
|
+
# Format a count into a human-readable string.
|
17
|
+
# @parameter value [Numeric] The count to format.
|
18
|
+
# @parameter units [Array] The units to use for formatting (default: UNITS).
|
19
|
+
# @returns [String] A formatted string representing the count.
|
20
|
+
def format_count(value, units = UNITS)
|
21
|
+
value = value.to_f
|
22
|
+
index = 0
|
23
|
+
limit = units.size - 1
|
24
|
+
|
25
|
+
# Handle negative numbers by working with absolute value:
|
26
|
+
negative = value < 0
|
27
|
+
value = value.abs
|
28
|
+
|
29
|
+
while value >= 1000 and index < limit
|
30
|
+
value = value / 1000
|
31
|
+
index += 1
|
32
|
+
end
|
33
|
+
|
34
|
+
result = String.new
|
35
|
+
result << "-" if negative
|
36
|
+
result << value.round(2).to_s
|
37
|
+
result << units[index].to_s if units[index]
|
38
|
+
|
39
|
+
return result
|
40
|
+
end
|
41
|
+
|
42
|
+
module_function :format_count
|
43
|
+
|
44
|
+
# Format a ratio as "current/total" with human-readable counts.
|
45
|
+
# @parameter current [Numeric] The current value.
|
46
|
+
# @parameter total [Numeric] The total value.
|
47
|
+
# @returns [String] A formatted ratio string.
|
48
|
+
def format_ratio(current, total)
|
49
|
+
"#{format_count(current)}/#{format_count(total)}"
|
50
|
+
end
|
51
|
+
|
52
|
+
module_function :format_ratio
|
53
|
+
|
54
|
+
# Format a load value as a decimal with specified precision.
|
55
|
+
# @parameter load [Numeric] The load value (typically 0.0 to 1.0+).
|
56
|
+
# @returns [String] A formatted load string.
|
57
|
+
def format_load(load)
|
58
|
+
load.round(2).to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
module_function :format_load
|
62
|
+
|
63
|
+
# Format multiple statistics into a compact string.
|
64
|
+
# @parameter stats [Hash] Hash of statistic names to values or [current, total] arrays.
|
65
|
+
# @returns [String] A formatted statistics string.
|
66
|
+
def format_statistics(**pairs)
|
67
|
+
pairs.map do |key, value|
|
68
|
+
case value
|
69
|
+
when Array
|
70
|
+
if value.length == 2
|
71
|
+
"#{key.to_s.upcase}=#{format_ratio(value[0], value[1])}"
|
72
|
+
else
|
73
|
+
"#{key.to_s.upcase}=#{value.join('/')}"
|
74
|
+
end
|
75
|
+
else
|
76
|
+
"#{key.to_s.upcase}=#{format_count(value)}"
|
77
|
+
end
|
78
|
+
end.join(" ")
|
79
|
+
end
|
80
|
+
|
81
|
+
module_function :format_statistics
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2024, by Samuel Williams.
|
4
|
+
# Copyright, 2024-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Async
|
7
7
|
module Service
|
@@ -11,22 +11,30 @@ module Async
|
|
11
11
|
# Designed to be invoked within an {Async::Controller::Container}.
|
12
12
|
class Generic
|
13
13
|
# Convert the given environment into a service if possible.
|
14
|
-
# @parameter environment [
|
14
|
+
# @parameter environment [Environment] The environment to use to construct the service.
|
15
|
+
# @returns [Generic | Nil] The constructed service if the environment specifies a service class.
|
15
16
|
def self.wrap(environment)
|
16
17
|
evaluator = environment.evaluator
|
17
18
|
|
18
|
-
if
|
19
|
-
|
19
|
+
if evaluator.key?(:service_class)
|
20
|
+
if service_class = evaluator.service_class
|
21
|
+
return service_class.new(environment, evaluator)
|
22
|
+
end
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
23
26
|
# Initialize the service from the given environment.
|
24
|
-
# @parameter environment [
|
27
|
+
# @parameter environment [Environment]
|
25
28
|
def initialize(environment, evaluator = environment.evaluator)
|
26
29
|
@environment = environment
|
27
30
|
@evaluator = evaluator
|
28
31
|
end
|
29
32
|
|
33
|
+
# @attribute [Environment] The environment which is used to configure the service.
|
34
|
+
attr :environment
|
35
|
+
|
36
|
+
# Convert the service evaluator to a hash.
|
37
|
+
# @returns [Hash] A hash representation of the evaluator.
|
30
38
|
def to_h
|
31
39
|
@evaluator.to_h
|
32
40
|
end
|
@@ -37,7 +45,7 @@ module Async
|
|
37
45
|
@evaluator.name
|
38
46
|
end
|
39
47
|
|
40
|
-
# Start the service.
|
48
|
+
# Start the service. Called before the container setup.
|
41
49
|
def start
|
42
50
|
Console.debug(self) {"Starting service #{self.name}..."}
|
43
51
|
end
|
@@ -48,10 +56,44 @@ module Async
|
|
48
56
|
Console.debug(self) {"Setting up service #{self.name}..."}
|
49
57
|
end
|
50
58
|
|
51
|
-
# Stop the service.
|
59
|
+
# Stop the service. Called after the container is stopped.
|
52
60
|
def stop(graceful = true)
|
53
61
|
Console.debug(self) {"Stopping service #{self.name}..."}
|
54
62
|
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
# Start the health checker.
|
67
|
+
#
|
68
|
+
# If a timeout is specified, a transient child task will be scheduled, which will yield the instance if a block is given, then mark the instance as ready, and finally sleep for half the health check duration (so that we guarantee that the health check runs in time).
|
69
|
+
#
|
70
|
+
# If a timeout is not specified, the health checker will yield the instance immediately and then mark the instance as ready.
|
71
|
+
#
|
72
|
+
# @parameter instance [Object] The service instance to check.
|
73
|
+
# @parameter timeout [Numeric] The timeout duration for the health check.
|
74
|
+
# @parameter parent [Async::Task] The parent task to run the health checker in.
|
75
|
+
# @yields {|instance| ...} If a block is given, it will be called with the service instance at least once.
|
76
|
+
def health_checker(instance, timeout = @evaluator.health_check_timeout, parent: Async::Task.current, &block)
|
77
|
+
if timeout
|
78
|
+
parent.async(transient: true) do
|
79
|
+
while true
|
80
|
+
if block_given?
|
81
|
+
yield(instance)
|
82
|
+
end
|
83
|
+
|
84
|
+
instance.ready!
|
85
|
+
|
86
|
+
sleep(timeout / 2)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
else
|
90
|
+
if block_given?
|
91
|
+
yield(instance)
|
92
|
+
end
|
93
|
+
|
94
|
+
instance.ready!
|
95
|
+
end
|
96
|
+
end
|
55
97
|
end
|
56
98
|
end
|
57
99
|
end
|
data/lib/async/service/loader.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2024, by Samuel Williams.
|
4
|
+
# Copyright, 2024-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "environment"
|
7
7
|
|
8
8
|
module Async
|
9
9
|
module Service
|
10
|
-
|
10
|
+
# The domain specific language for loading configuration files.
|
11
11
|
class Loader
|
12
12
|
# Initialize the loader, attached to a specific configuration instance.
|
13
13
|
# Any environments generated by the loader will be added to the configuration.
|
@@ -38,6 +38,8 @@ module Async
|
|
38
38
|
loader.instance_eval(File.read(path), path)
|
39
39
|
end
|
40
40
|
|
41
|
+
# Load a file relative to the loader's root directory.
|
42
|
+
# @parameter path [String] The path to the file to load.
|
41
43
|
def load_file(path)
|
42
44
|
Loader.load_file(@configuration, File.expand_path(path, @root))
|
43
45
|
end
|
@@ -47,11 +49,14 @@ module Async
|
|
47
49
|
Environment.build(**initial, &block)
|
48
50
|
end
|
49
51
|
|
50
|
-
# Define a
|
51
|
-
# Adds `root` and `
|
52
|
+
# Define a service with the specified name.
|
53
|
+
# Adds `root` and `name` keys.
|
52
54
|
# @parameter name [String] The name of the environment, usually a hostname.
|
53
|
-
def service(name, &block)
|
54
|
-
|
55
|
+
def service(name = nil, **options, &block)
|
56
|
+
options[:name] = name
|
57
|
+
options[:root] ||= @root
|
58
|
+
|
59
|
+
@configuration.add(self.environment(**options, &block))
|
55
60
|
end
|
56
61
|
end
|
57
62
|
end
|
data/lib/async/service.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2024, by Samuel Williams.
|
4
|
+
# Copyright, 2024-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
6
|
+
require_relative "service/configuration"
|
7
|
+
require_relative "service/controller"
|
8
|
+
require_relative "service/version"
|
9
|
+
|
10
|
+
# @namespace
|
11
|
+
module Async
|
12
|
+
# @namespace
|
13
|
+
module Service
|
14
|
+
end
|
15
|
+
end
|
data/license.md
CHANGED
data/readme.md
CHANGED
@@ -1,9 +1,76 @@
|
|
1
1
|
# Async::Service
|
2
2
|
|
3
|
-
Provides a simple service interface for configuring and running services.
|
3
|
+
Provides a simple service interface for configuring and running asynchronous services in Ruby.
|
4
4
|
|
5
5
|
[](https://github.com/socketry/async-service/actions?workflow=Test)
|
6
6
|
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- **Service Management**: Define, configure, and run long-running services.
|
10
|
+
- **Container Integration**: Built on `async-container` for robust process management.
|
11
|
+
- **Multiple Services**: Run and coordinate multiple services together.
|
12
|
+
- **Automatic Restart**: Services automatically restart on failure.
|
13
|
+
- **Graceful Shutdown**: Clean shutdown handling with proper resource cleanup.
|
14
|
+
- **Environment Configuration**: Configure services with environment variables and settings.
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
Please see the [project documentation](https://socketry.github.io/async-service/) for more details.
|
19
|
+
|
20
|
+
- [Getting Started](https://socketry.github.io/async-service/guides/getting-started/index) - This guide explains how to get started with `async-service` to create and run services in Ruby.
|
21
|
+
|
22
|
+
- [Service Architecture](https://socketry.github.io/async-service/guides/service-architecture/index) - This guide explains the key architectural components of `async-service` and how they work together to provide a clean separation of concerns.
|
23
|
+
|
24
|
+
- [Best Practices](https://socketry.github.io/async-service/guides/best-practices/index) - This guide outlines recommended patterns and practices for building robust, maintainable services with `async-service`.
|
25
|
+
|
26
|
+
## Releases
|
27
|
+
|
28
|
+
Please see the [project releases](https://socketry.github.io/async-service/releases/index) for all releases.
|
29
|
+
|
30
|
+
### v0.14.0
|
31
|
+
|
32
|
+
- Introduce `ContainerEnvironment` and `ContainerService` for implementing best-practice services.
|
33
|
+
|
34
|
+
### v0.13.0
|
35
|
+
|
36
|
+
- Fix null services handling.
|
37
|
+
- Modernize code and improve documentation.
|
38
|
+
- Make service name optional and improve code comments.
|
39
|
+
- Add `respond_to_missing?` for completeness.
|
40
|
+
|
41
|
+
### v0.12.0
|
42
|
+
|
43
|
+
- Add convenient `Configuration.build{...}` method for constructing inline configurations.
|
44
|
+
|
45
|
+
### v0.11.0
|
46
|
+
|
47
|
+
- Allow builder with argument for more flexible configuration construction.
|
48
|
+
|
49
|
+
### v0.10.0
|
50
|
+
|
51
|
+
- Add `Environment::Evaluator#as_json` for JSON serialization support.
|
52
|
+
- Allow constructing a configuration with existing environments.
|
53
|
+
|
54
|
+
### v0.9.0
|
55
|
+
|
56
|
+
- Allow providing a list of modules to include in environments.
|
57
|
+
|
58
|
+
### v0.8.0
|
59
|
+
|
60
|
+
- Introduce `Environment#implements?` and related methods for interface checking.
|
61
|
+
|
62
|
+
### v0.7.0
|
63
|
+
|
64
|
+
- Allow instance methods that take arguments in environments.
|
65
|
+
|
66
|
+
### v0.6.1
|
67
|
+
|
68
|
+
- Fix requirement that facet must be a module.
|
69
|
+
|
70
|
+
### v0.6.0
|
71
|
+
|
72
|
+
- Unify construction of environments for better consistency.
|
73
|
+
|
7
74
|
## Contributing
|
8
75
|
|
9
76
|
We welcome contributions to this project.
|
@@ -16,8 +83,8 @@ We welcome contributions to this project.
|
|
16
83
|
|
17
84
|
### Developer Certificate of Origin
|
18
85
|
|
19
|
-
|
86
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
20
87
|
|
21
|
-
###
|
88
|
+
### Community Guidelines
|
22
89
|
|
23
|
-
This project is
|
90
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
data/releases.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Releases
|
2
|
+
|
3
|
+
## v0.14.0
|
4
|
+
|
5
|
+
- Introduce `ContainerEnvironment` and `ContainerService` for implementing best-practice services.
|
6
|
+
|
7
|
+
## v0.13.0
|
8
|
+
|
9
|
+
- Fix null services handling.
|
10
|
+
- Modernize code and improve documentation.
|
11
|
+
- Make service name optional and improve code comments.
|
12
|
+
- Add `respond_to_missing?` for completeness.
|
13
|
+
|
14
|
+
## v0.12.0
|
15
|
+
|
16
|
+
- Add convenient `Configuration.build{...}` method for constructing inline configurations.
|
17
|
+
|
18
|
+
## v0.11.0
|
19
|
+
|
20
|
+
- Allow builder with argument for more flexible configuration construction.
|
21
|
+
|
22
|
+
## v0.10.0
|
23
|
+
|
24
|
+
- Add `Environment::Evaluator#as_json` for JSON serialization support.
|
25
|
+
- Allow constructing a configuration with existing environments.
|
26
|
+
|
27
|
+
## v0.9.0
|
28
|
+
|
29
|
+
- Allow providing a list of modules to include in environments.
|
30
|
+
|
31
|
+
## v0.8.0
|
32
|
+
|
33
|
+
- Introduce `Environment#implements?` and related methods for interface checking.
|
34
|
+
|
35
|
+
## v0.7.0
|
36
|
+
|
37
|
+
- Allow instance methods that take arguments in environments.
|
38
|
+
|
39
|
+
## v0.6.1
|
40
|
+
|
41
|
+
- Fix requirement that facet must be a module.
|
42
|
+
|
43
|
+
## v0.6.0
|
44
|
+
|
45
|
+
- Unify construction of environments for better consistency.
|
46
|
+
|
47
|
+
## v0.5.1
|
48
|
+
|
49
|
+
- Relax dependency on async-container for better compatibility.
|
50
|
+
|
51
|
+
## v0.5.0
|
52
|
+
|
53
|
+
- Add support for passing through options to controllers.
|
54
|
+
|
55
|
+
## v0.4.0
|
56
|
+
|
57
|
+
- Reuse evaluator for service instances for better performance.
|
58
|
+
- Expose `Configuration.load` and `Controller.start` for better composition.
|
59
|
+
- Add simple service example.
|
60
|
+
|
61
|
+
## v0.3.1
|
62
|
+
|
63
|
+
- Fix usage of `raise` in `BasicObject` context.
|
64
|
+
|
65
|
+
## v0.3.0
|
66
|
+
|
67
|
+
- Use modules for environments instead of basic objects.
|
68
|
+
- Allow non-modules to be included in environments.
|
69
|
+
|
70
|
+
## v0.2.1
|
71
|
+
|
72
|
+
- Add missing call to `super` in service implementations.
|
73
|
+
|
74
|
+
## v0.2.0
|
75
|
+
|
76
|
+
- Add support for loading other configuration files.
|
77
|
+
- Minor bug fixes and improvements.
|
78
|
+
|
79
|
+
## v0.1.0
|
80
|
+
|
81
|
+
- Initial release with core service framework.
|
82
|
+
- Environment abstraction for service configuration.
|
83
|
+
- Improved evaluator implementation with comprehensive tests.
|
84
|
+
- Controller for handling service execution.
|
85
|
+
- Support for explicit `service_class` configuration.
|
data.tar.gz.sig
CHANGED
Binary file
|