async-service 0.13.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/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 +12 -0
- data/lib/async/service/container_environment.rb +42 -0
- data/lib/async/service/container_service.rb +77 -0
- data/lib/async/service/controller.rb +20 -0
- data/lib/async/service/environment.rb +59 -1
- data/lib/async/service/formatting.rb +84 -0
- data/lib/async/service/generic.rb +43 -4
- data/lib/async/service/loader.rb +2 -0
- data/lib/async/service/version.rb +2 -2
- data/lib/async/service.rb +7 -0
- data/readme.md +68 -1
- data/releases.md +85 -0
- data.tar.gz.sig +0 -0
- metadata +13 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 863ac83703c8c6a2abca7b9457344dbd723f90062e7b157f2d62837dd35899ee
|
4
|
+
data.tar.gz: 35343238d803b20259eba0bdbbcec35742ac32316c29d8db0d0b58ff70039fc6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd7daa08dfc0e693082dac4912011bee7e170130bd3415176013a0d1020b3b801dd99a62b385fb95e7b37f49c256e458b11143d27e63d22282f8d682fb26822f
|
7
|
+
data.tar.gz: a3d084807fcd460c8dd4c21e8909eed769e1b344356c0b9b2e4a69b29d5134faeee69e2f9c5a01c24b6b8c54c1a1305a477356d080b700533f7c16bac6a82c3e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/agent.md
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# Agent
|
2
|
+
|
3
|
+
## Context
|
4
|
+
|
5
|
+
This section provides links to documentation from installed packages. It is automatically generated and may be updated by running `bake agent:context:install`.
|
6
|
+
|
7
|
+
**Important:** Before performing any code, documentation, or analysis tasks, always read and apply the full content of any relevant documentation referenced in the following sections. These context files contain authoritative standards and best practices for documentation, code style, and project-specific workflows. **Do not proceed with any actions until you have read and incorporated the guidance from relevant context files.**
|
8
|
+
|
9
|
+
**Setup Instructions:** If the referenced files are not present or if dependencies have been updated, run `bake agent:context:install` to install the latest context files.
|
10
|
+
|
11
|
+
### agent-context
|
12
|
+
|
13
|
+
Install and manage context files from Ruby gems.
|
14
|
+
|
15
|
+
#### [Getting Started](.context/agent-context/getting-started.md)
|
16
|
+
|
17
|
+
This guide explains how to use `agent-context`, a tool for discovering and installing contextual information from Ruby gems to help AI agents.
|
18
|
+
|
19
|
+
### async
|
20
|
+
|
21
|
+
A concurrency framework for Ruby.
|
22
|
+
|
23
|
+
#### [Getting Started](.context/async/getting-started.md)
|
24
|
+
|
25
|
+
This guide shows how to add async to your project and run code asynchronously.
|
26
|
+
|
27
|
+
#### [Scheduler](.context/async/scheduler.md)
|
28
|
+
|
29
|
+
This guide gives an overview of how the scheduler is implemented.
|
30
|
+
|
31
|
+
#### [Tasks](.context/async/tasks.md)
|
32
|
+
|
33
|
+
This guide explains how asynchronous tasks work and how to use them.
|
34
|
+
|
35
|
+
#### [Best Practices](.context/async/best-practices.md)
|
36
|
+
|
37
|
+
This guide gives an overview of best practices for using Async.
|
38
|
+
|
39
|
+
#### [Debugging](.context/async/debugging.md)
|
40
|
+
|
41
|
+
This guide explains how to debug issues with programs that use Async.
|
42
|
+
|
43
|
+
#### [Thread safety](.context/async/thread-safety.md)
|
44
|
+
|
45
|
+
This guide explains thread safety in Ruby, focusing on fibers and threads, common pitfalls, and best practices to avoid problems like data corruption, race conditions, and deadlocks.
|
46
|
+
|
47
|
+
### decode
|
48
|
+
|
49
|
+
Code analysis for documentation generation.
|
50
|
+
|
51
|
+
#### [Getting Started with Decode](.context/decode/getting-started.md)
|
52
|
+
|
53
|
+
The Decode gem provides programmatic access to Ruby code structure and metadata. It can parse Ruby files and extract definitions, comments, and documentation pragmas, enabling code analysis, documentation generation, and other programmatic manipulations of Ruby codebases.
|
54
|
+
|
55
|
+
#### [Documentation Coverage](.context/decode/coverage.md)
|
56
|
+
|
57
|
+
This guide explains how to test and monitor documentation coverage in your Ruby projects using the Decode gem's built-in bake tasks.
|
58
|
+
|
59
|
+
#### [Ruby Documentation](.context/decode/ruby-documentation.md)
|
60
|
+
|
61
|
+
This guide covers documentation practices and pragmas supported by the Decode gem for documenting Ruby code. These pragmas provide structured documentation that can be parsed and used to generate API documentation and achieve complete documentation coverage.
|
62
|
+
|
63
|
+
#### [Setting Up RBS Types and Steep Type Checking for Ruby Gems](.context/decode/types.md)
|
64
|
+
|
65
|
+
This guide covers the process for establishing robust type checking in Ruby gems using RBS and Steep, focusing on automated generation from source documentation and proper validation.
|
66
|
+
|
67
|
+
### falcon
|
68
|
+
|
69
|
+
A fast, asynchronous, rack-compatible web server.
|
70
|
+
|
71
|
+
#### [Getting Started](.context/falcon/getting-started.md)
|
72
|
+
|
73
|
+
This guide gives an overview of how to use Falcon for running Ruby web applications.
|
74
|
+
|
75
|
+
#### [Rails Integration](.context/falcon/rails-integration.md)
|
76
|
+
|
77
|
+
This guide explains how to host Rails applications with Falcon.
|
78
|
+
|
79
|
+
#### [Deployment](.context/falcon/deployment.md)
|
80
|
+
|
81
|
+
This guide explains how to deploy applications using the Falcon web server. It covers the recommended deployment methods, configuration options, and examples for different environments, including systemd and kubernetes.
|
82
|
+
|
83
|
+
#### [Performance Tuning](.context/falcon/performance-tuning.md)
|
84
|
+
|
85
|
+
This guide explains the performance characteristics of Falcon.
|
86
|
+
|
87
|
+
#### [WebSockets](.context/falcon/websockets.md)
|
88
|
+
|
89
|
+
This guide explains how to use WebSockets with Falcon.
|
90
|
+
|
91
|
+
#### [Interim Responses](.context/falcon/interim-responses.md)
|
92
|
+
|
93
|
+
This guide explains how to use interim responses in Falcon to send early hints to the client.
|
94
|
+
|
95
|
+
#### [How It Works](.context/falcon/how-it-works.md)
|
96
|
+
|
97
|
+
This guide gives an overview of how Falcon handles an incoming web request.
|
98
|
+
|
99
|
+
### sus
|
100
|
+
|
101
|
+
A fast and scalable test runner.
|
102
|
+
|
103
|
+
#### [Using Sus Testing Framework](.context/sus/usage.md)
|
104
|
+
|
105
|
+
Sus is a modern Ruby testing framework that provides a clean, BDD-style syntax for writing tests. It's designed to be fast, simple, and expressive.
|
106
|
+
|
107
|
+
#### [Mocking](.context/sus/mocking.md)
|
108
|
+
|
109
|
+
There are two types of mocking in sus: `receive` and `mock`. The `receive` matcher is a subset of full mocking and is used to set expectations on method calls, while `mock` can be used to replace method implementations or set up more complex behavior.
|
110
|
+
|
111
|
+
#### [Shared Test Behaviors and Fixtures](.context/sus/shared.md)
|
112
|
+
|
113
|
+
Sus provides shared test contexts which can be used to define common behaviours or tests that can be reused across one or more test files.
|
114
|
+
|
115
|
+
### utopia-project
|
116
|
+
|
117
|
+
A project documentation tool based on Utopia.
|
118
|
+
|
119
|
+
#### [Getting Started](.context/utopia-project/getting-started.md)
|
120
|
+
|
121
|
+
This guide explains how to use `utopia-project` to add documentation to your project.
|
122
|
+
|
123
|
+
#### [Documentation Guides](.context/utopia-project/documentation-guidelines.md)
|
124
|
+
|
125
|
+
This guide explains how to create and maintain documentation for your project using `utopia-project`.
|
126
|
+
|
127
|
+
#### [GitHub Pages Integration](.context/utopia-project/github-pages-integration.md)
|
128
|
+
|
129
|
+
This guide shows you how to use `utopia-project` with GitHub Pages to deploy documentation.
|
@@ -0,0 +1,320 @@
|
|
1
|
+
# Best Practices
|
2
|
+
|
3
|
+
This guide outlines recommended patterns and practices for building robust, maintainable services with `async-service`.
|
4
|
+
|
5
|
+
## Application Structure
|
6
|
+
|
7
|
+
If you are creating an application that runs services, you should define a top level `services.rb` file:
|
8
|
+
|
9
|
+
### Service Configuration
|
10
|
+
|
11
|
+
Create a single top-level `service.rb` file as your main entry point:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
#!/usr/bin/env async-service
|
15
|
+
|
16
|
+
# Load your service configurations
|
17
|
+
require_relative 'lib/my_library/environment/web_environment'
|
18
|
+
require_relative 'lib/my_library/environment/worker_environment'
|
19
|
+
|
20
|
+
service "web" do
|
21
|
+
include MyLibrary::Environment::WebEnvironment
|
22
|
+
end
|
23
|
+
|
24
|
+
service "worker" do
|
25
|
+
include MyLibrary::Environment::WorkerEnvironment
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
### Multiple Service Configurations
|
30
|
+
|
31
|
+
In some cases, you may want to define multiple service configurations, e.g. for different environments or deployment targets. In those cases, you may create `web_service.rb` or `job_service.rb`, but this usage should be discouraged.
|
32
|
+
|
33
|
+
## Library Structure
|
34
|
+
|
35
|
+
If you are creating a library that exposes services, use the following structure and guidelines:
|
36
|
+
|
37
|
+
### Directory Structure
|
38
|
+
|
39
|
+
Organize your code following these conventions:
|
40
|
+
|
41
|
+
```
|
42
|
+
├── service.rb
|
43
|
+
└── lib/
|
44
|
+
└── my_library/
|
45
|
+
├── environment/
|
46
|
+
│ ├── web_environment.rb
|
47
|
+
│ ├── worker_environment.rb
|
48
|
+
│ ├── database_environment.rb
|
49
|
+
│ └── tls_environment.rb
|
50
|
+
└── service/
|
51
|
+
├── web_service.rb
|
52
|
+
└── worker_service.rb
|
53
|
+
```
|
54
|
+
|
55
|
+
### Environment Organization
|
56
|
+
|
57
|
+
Place environments in `lib/my_library/environment/`:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# lib/my_library/environment/web_environment.rb
|
61
|
+
module MyLibrary
|
62
|
+
module Environment
|
63
|
+
module WebEnvironment
|
64
|
+
include Async::Service::ContainerEnvironment
|
65
|
+
|
66
|
+
def service_class
|
67
|
+
MyLibrary::Service::WebService
|
68
|
+
end
|
69
|
+
|
70
|
+
def port
|
71
|
+
3000
|
72
|
+
end
|
73
|
+
|
74
|
+
def host
|
75
|
+
"localhost"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
### Service Organization
|
83
|
+
|
84
|
+
Place services in `lib/my_library/service/`:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
# lib/my_library/service/web_service.rb
|
88
|
+
module MyLibrary
|
89
|
+
module Service
|
90
|
+
class WebService < Async::Service::ContainerService
|
91
|
+
private def format_title(evaluator, server)
|
92
|
+
if server&.respond_to?(:connection_count)
|
93
|
+
"#{self.name} [#{evaluator.host}:#{evaluator.port}] (#{server.connection_count} connections)"
|
94
|
+
else
|
95
|
+
"#{self.name} [#{evaluator.host}:#{evaluator.port}]"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def run(instance, evaluator)
|
100
|
+
# Start your service and return the server object.
|
101
|
+
# ContainerService handles container setup, health checking, and process titles.
|
102
|
+
start_web_server(evaluator.host, evaluator.port)
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def start_web_server(host, port)
|
108
|
+
# The return value of this method will be the server object which is returned from `run` and passed to `format_title`.
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
115
|
+
### Use `ContainerEnvironment` for Services
|
116
|
+
|
117
|
+
Include {ruby Async::Service::ContainerEnvironment} for services that run in containers using {ruby Async::Service::ContainerService}:
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
module WebEnvironment
|
121
|
+
include Async::Service::ContainerEnvironment
|
122
|
+
|
123
|
+
def service_class
|
124
|
+
WebService
|
125
|
+
end
|
126
|
+
end
|
127
|
+
```
|
128
|
+
|
129
|
+
## Environment Best Practices
|
130
|
+
|
131
|
+
### Use Plain Modules
|
132
|
+
|
133
|
+
Prefer plain Ruby modules for environments:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
module DatabaseEnvironment
|
137
|
+
def database_url
|
138
|
+
"postgresql://localhost/app"
|
139
|
+
end
|
140
|
+
|
141
|
+
def max_connections
|
142
|
+
10
|
143
|
+
end
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
### One-to-One Service-Environment Correspondence
|
148
|
+
|
149
|
+
Maintain a 1:1 relationship between services and their primary environments:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
# Primary environment for WebService
|
153
|
+
module WebEnvironment
|
154
|
+
def service_class
|
155
|
+
WebService
|
156
|
+
end
|
157
|
+
|
158
|
+
# Default configuration:
|
159
|
+
def port
|
160
|
+
3000
|
161
|
+
end
|
162
|
+
|
163
|
+
def host
|
164
|
+
'0.0.0.0'
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Primary environment for WorkerService
|
169
|
+
module WorkerEnvironment
|
170
|
+
def service_class
|
171
|
+
WorkerService
|
172
|
+
end
|
173
|
+
|
174
|
+
def queue_name
|
175
|
+
'default'
|
176
|
+
end
|
177
|
+
|
178
|
+
def count
|
179
|
+
4
|
180
|
+
end
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
### Compose with Auxiliary Environments
|
185
|
+
|
186
|
+
Use additional environments for cross-cutting concerns:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
module WebEnvironment
|
190
|
+
include DatabaseEnvironment
|
191
|
+
include TLSEnvironment
|
192
|
+
include LoggingEnvironment
|
193
|
+
|
194
|
+
def service_class
|
195
|
+
WebService
|
196
|
+
end
|
197
|
+
end
|
198
|
+
```
|
199
|
+
|
200
|
+
## Service Best Practices
|
201
|
+
|
202
|
+
### Use ContainerService as Base Class
|
203
|
+
|
204
|
+
Prefer `Async::Service::ContainerService` over `Generic` for most services:
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
class WebService < Async::Service::ContainerService
|
208
|
+
# ContainerService automatically handles:
|
209
|
+
# - Container setup with proper options.
|
210
|
+
# - Health checking with process title updates.
|
211
|
+
# - Integration with Formatting module.
|
212
|
+
|
213
|
+
private def format_title(evaluator, server)
|
214
|
+
# Customize process title display
|
215
|
+
"#{self.name} [#{evaluator.host}:#{evaluator.port}]"
|
216
|
+
end
|
217
|
+
|
218
|
+
def run(instance, evaluator)
|
219
|
+
# Focus only on your service logic
|
220
|
+
start_web_server(evaluator.host, evaluator.port)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
### Implement Meaningful Process Titles
|
226
|
+
|
227
|
+
Use the `format_title` method to provide dynamic process information:
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
private def format_title(evaluator, server)
|
231
|
+
# Good - Include service-specific info
|
232
|
+
"#{self.name} [#{evaluator.host}:#{evaluator.port}]"
|
233
|
+
|
234
|
+
# Better - Include dynamic runtime status
|
235
|
+
if connection_count = server&.connection_count
|
236
|
+
"#{self.name} [#{evaluator.host}:#{evaluator.port}] (C=#{format_count connection_count})"
|
237
|
+
else
|
238
|
+
"#{self.name} [#{evaluator.host}:#{evaluator.port}]"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
243
|
+
Try to keep process titles short and focused.
|
244
|
+
|
245
|
+
### Use `start` and `stop` Hooks for Shared Resources
|
246
|
+
|
247
|
+
Utilize the `start` and `stop` hooks to manage shared resources effectively:
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
class WebService < Async::Service::ContainerService
|
251
|
+
def start
|
252
|
+
# Bind to the endpoint in the container:
|
253
|
+
@endpoint = @evaluator.endpoint.bind
|
254
|
+
|
255
|
+
super
|
256
|
+
end
|
257
|
+
|
258
|
+
def stop
|
259
|
+
@endpoint&.close
|
260
|
+
end
|
261
|
+
end
|
262
|
+
```
|
263
|
+
|
264
|
+
## Testing Best Practices
|
265
|
+
|
266
|
+
### Test Environments in Isolation
|
267
|
+
|
268
|
+
Test environment modules independently:
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
# test/my_library/environment/web_environment.rb
|
272
|
+
describe MyLibrary::Environment::WebEnvironment do
|
273
|
+
let(:environment) do
|
274
|
+
Async::Service::Environment.build do
|
275
|
+
include MyLibrary::Environment::WebEnvironment
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
it "provides default port" do
|
280
|
+
expect(environment.port).to be == 3000
|
281
|
+
end
|
282
|
+
end
|
283
|
+
```
|
284
|
+
|
285
|
+
### Test Services with Service Controller
|
286
|
+
|
287
|
+
Use test environments for service testing:
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
# test/my_library/service/web_service.rb
|
291
|
+
describe MyLibrary::Service::WebService do
|
292
|
+
let(:environment) do
|
293
|
+
Async::Service::Environment.build do
|
294
|
+
include MyLibrary::Environment::WebEnvironment
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
let(:evaluator) {environment.evaluator}
|
299
|
+
let(:service) {evaluator.service_class(environment, evaluator)}
|
300
|
+
let(:controller) {Async::Service::Controller.for(service)}
|
301
|
+
|
302
|
+
before do
|
303
|
+
controller.start
|
304
|
+
end
|
305
|
+
|
306
|
+
after do
|
307
|
+
controller.stop
|
308
|
+
end
|
309
|
+
|
310
|
+
let(:uri) {URI "http://#{evaluator.host}:#{evaluator.port}"}
|
311
|
+
|
312
|
+
it "responds to requests" do
|
313
|
+
Net::HTTP.get(uri).tap do |response|
|
314
|
+
expect(response).to be_a(Net::HTTPSuccess)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
```
|
319
|
+
|
320
|
+
Note that full end-to-end service tests like this are typically slow and hard to isolate, so it's better to use unit tests for individual components whenever possible.
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# Getting Started
|
2
|
+
|
3
|
+
This guide explains how to get started with `async-service` to create and run services in Ruby.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add the gem to your project:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
$ bundle add async-service
|
11
|
+
```
|
12
|
+
|
13
|
+
## Core Concepts
|
14
|
+
|
15
|
+
`async-service` has several core concepts:
|
16
|
+
|
17
|
+
- A {ruby Async::Service::Generic} which represents the base class for implementing services.
|
18
|
+
- A {ruby Async::Service::Configuration} which manages service configurations and environments.
|
19
|
+
- A {ruby Async::Service::Controller} which handles starting, stopping, and managing services.
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Services are long-running processes that can be managed as a group. Each service extends `Async::Service::Generic` and implements a `setup` method that defines how the service runs.
|
24
|
+
|
25
|
+
### Basic Service
|
26
|
+
|
27
|
+
Create a simple service that runs continuously:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
#!/usr/bin/env async-service
|
31
|
+
|
32
|
+
require 'async/service'
|
33
|
+
|
34
|
+
class HelloService < Async::Service::Generic
|
35
|
+
def setup(container)
|
36
|
+
super
|
37
|
+
|
38
|
+
container.run(count: 1, restart: true) do |instance|
|
39
|
+
instance.ready!
|
40
|
+
|
41
|
+
while true
|
42
|
+
puts "Hello World!"
|
43
|
+
sleep 1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
service "hello" do
|
50
|
+
service_class HelloService
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Make the file executable and run it:
|
55
|
+
|
56
|
+
```bash
|
57
|
+
$ chmod +x hello_service.rb
|
58
|
+
$ ./hello_service.rb
|
59
|
+
```
|
60
|
+
|
61
|
+
### Service Configuration
|
62
|
+
|
63
|
+
Services can be configured with custom properties:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
service "web-server" do
|
67
|
+
service_class WebServerService
|
68
|
+
port 3000
|
69
|
+
host "localhost"
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
In your service implementation, you can access these values through the environment and evaluator:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
class WebServerService < Async::Service::Generic
|
77
|
+
def setup(container)
|
78
|
+
super
|
79
|
+
|
80
|
+
container.run(count: 1, restart: true) do |instance|
|
81
|
+
# Access the configuration for the service:
|
82
|
+
evaluator = self.environment.evaluator
|
83
|
+
port = evaluator.port
|
84
|
+
host = evaluator.host
|
85
|
+
|
86
|
+
puts "Starting web server on #{host}:#{port}"
|
87
|
+
instance.ready!
|
88
|
+
|
89
|
+
# Your web server implementation here
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
### Multiple Services
|
96
|
+
|
97
|
+
You can define multiple services in a single configuration file:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
#!/usr/bin/env async-service
|
101
|
+
|
102
|
+
require 'async/service'
|
103
|
+
|
104
|
+
class WebService < Async::Service::Generic
|
105
|
+
def setup(container)
|
106
|
+
super
|
107
|
+
|
108
|
+
container.run(count: 1, restart: true) do |instance|
|
109
|
+
instance.ready!
|
110
|
+
puts "Web service starting..."
|
111
|
+
# Web server implementation
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class WorkerService < Async::Service::Generic
|
117
|
+
def setup(container)
|
118
|
+
super
|
119
|
+
|
120
|
+
container.run(count: 2, restart: true) do |instance|
|
121
|
+
instance.ready!
|
122
|
+
puts "Worker #{instance.name} starting..."
|
123
|
+
# Background job processing
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
service "web" do
|
129
|
+
service_class WebService
|
130
|
+
port 3000
|
131
|
+
host "localhost"
|
132
|
+
end
|
133
|
+
|
134
|
+
service "worker" do
|
135
|
+
service_class WorkerService
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
### Programmatic Usage
|
140
|
+
|
141
|
+
You can also create and run services programmatically:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
require 'async/service'
|
145
|
+
|
146
|
+
configuration = Async::Service::Configuration.build do
|
147
|
+
service "my-service" do
|
148
|
+
service_class MyService
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
Async::Service::Controller.run(configuration)
|
153
|
+
```
|
154
|
+
|
155
|
+
### Accessing Configuration Values
|
156
|
+
|
157
|
+
Services have access to their configuration through the environment and evaluator:
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
class ConfigurableService < Async::Service::Generic
|
161
|
+
def setup(container)
|
162
|
+
super
|
163
|
+
|
164
|
+
container.run(count: 1, restart: true) do |instance|
|
165
|
+
# Clone the evaluator for thread safety
|
166
|
+
evaluator = self.environment.evaluator
|
167
|
+
database_url = evaluator.database_url
|
168
|
+
max_connections = evaluator.max_connections
|
169
|
+
debug_mode = evaluator.debug_mode
|
170
|
+
|
171
|
+
puts "Database URL: #{database_url}"
|
172
|
+
puts "Max connections: #{max_connections}"
|
173
|
+
puts "Debug mode: #{debug_mode}"
|
174
|
+
|
175
|
+
instance.ready!
|
176
|
+
|
177
|
+
# Your service implementation using these values
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
service "configurable" do
|
183
|
+
service_class ConfigurableService
|
184
|
+
database_url "postgresql://localhost/myapp"
|
185
|
+
max_connections 10
|
186
|
+
debug_mode true
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
The evaluator is a memoized instance of the service's configuration, allowing for efficient access to configuration values throughout the service's lifecycle.
|
data/context/index.yaml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Automatically generated context index for Utopia::Project guides.
|
2
|
+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
|
3
|
+
---
|
4
|
+
description: A service layer for Async.
|
5
|
+
metadata:
|
6
|
+
documentation_uri: https://socketry.github.io/async-service/
|
7
|
+
source_code_uri: https://github.com/socketry/async-service.git
|
8
|
+
files:
|
9
|
+
- path: getting-started.md
|
10
|
+
title: Getting Started
|
11
|
+
description: This guide explains how to get started with `async-service` to create
|
12
|
+
and run services in Ruby.
|
13
|
+
- path: service-architecture.md
|
14
|
+
title: Service Architecture
|
15
|
+
description: This guide explains the key architectural components of `async-service`
|
16
|
+
and how they work together to provide a clean separation of concerns.
|
17
|
+
- path: best-practices.md
|
18
|
+
title: Best Practices
|
19
|
+
description: This guide outlines recommended patterns and practices for building
|
20
|
+
robust, maintainable services with `async-service`.
|