async-service 0.13.0 → 0.14.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd7c0c91471cbf80069a3c770f1a9489e1dac8a2b22db648a0942f31e35e2743
4
- data.tar.gz: a1aa57c3746945251bde7ad98baec11d28bd477a9c39bfe33fcdf22ac9f42f71
3
+ metadata.gz: 89fe1d184a09d9ded720abd6de21db5b499eb60aa50f24401264dc65213e0e1a
4
+ data.tar.gz: '0208be218dd12d5ad922cd830a304b53086494eb66f9dd2cda984758589677b4'
5
5
  SHA512:
6
- metadata.gz: 400055e9c5d608f4eae1551304c25b96207e7b2c6b92cadeba6f476dac3c0de1a2ff2d24eb39da282d7ba52a0a6f90c400063b8a94fe883ca064ed3cbbcc1099
7
- data.tar.gz: 0f416c8a882c91cc453111f0949498bf8e17d463b8dc623d06da07ca5e2435c966b2a6668c25170fe474afb9e684c368ef73522d6f29101cc1f319473624556a
6
+ metadata.gz: df3091625c43fff8e041d194fe64c0068e5e907b896ecd882f1682506890d05f04e7cb8606a0de13fa3bcdae28f89356c6720b31d0de195370c06bc952fd8680
7
+ data.tar.gz: c1a424f264717708460ab5216c6328d3fe946a0ea8ab56022a0b6a18c57afc8cac7872211dd836f260ba5e53460297a6be5dce4d56b43edc7911dbb437c88060
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 that includes all your service configurations. This file serves as the main entry point for your services. If you are specifically working with the Falcon web server, this file is typically called `falcon.rb` for historical reasons.
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.
@@ -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`.