pool_machines 0.1.3-arm64-darwin
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 +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +218 -0
- data/ext/pool_machines_native/core/CHANGELOG.md +22 -0
- data/ext/pool_machines_native/core/README.md +18 -0
- data/ext/pool_machines_native/extconf.rb +2 -0
- data/lib/pool_machines/version.rb +4 -0
- data/lib/pool_machines.rb +3 -0
- data/lib/pool_machines_native/pool_machines_native.bundle +0 -0
- data/sig/pool_machines.rbs +16 -0
- metadata +61 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a23fd38927ea119f4c0e3e9943c647a7bee2638ecfdded64e442fe8e6309a660
|
|
4
|
+
data.tar.gz: 77bc4f1ab0bf2548f7eb3770f65a9b37cf3795b19847cf787d367bb4c32e1029
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e5fcd0c748ef31262360b9f00b7cb8dfd7b0d5ecda48a1f8c46956b68253197a3d3e7be05e604fbfb9891c8509d79462dd89e8e2704747df0f35f4dadbf1e490
|
|
7
|
+
data.tar.gz: 98ca13aad9f8db85249d9963e1bb40d4197f3462570591aad6fe2c4df4ec66a6d4dbb157ff4d60d6685f7c4cadffc0335a5baee9e79c8ae57fb10cce375900ae
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.3](https://github.com/seuros/pool_machines/compare/pool_machines/v0.1.2...pool_machines/v0.1.3) (2026-11-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* crates.io keywords and categories for the core ([e65b159](https://github.com/seuros/pool_machines/commit/e65b1592ae2868c0acea23df562fab5b43dab71f))
|
|
9
|
+
|
|
10
|
+
## [0.1.2](https://github.com/seuros/pool_machines/compare/pool_machines/v0.1.1...pool_machines/v0.1.2) (2026-11-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* keep test sources out of the packaged gem and crate ([d256e29](https://github.com/seuros/pool_machines/commit/d256e29c8cb52218216e7c796057a7fef3205b00))
|
|
16
|
+
|
|
17
|
+
## [0.1.1](https://github.com/seuros/pool_machines/compare/pool_machines-v0.1.0...pool_machines/v0.1.1) (2026-11-06)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* initial extraction ([030ecb6](https://github.com/seuros/pool_machines/commit/030ecb6977a3559cd397225ab2d38137a46d39b7))
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abdelkader Boudih
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# PoolMachines
|
|
2
|
+
|
|
3
|
+
> A connection returned twice is a connection somebody else is now using. 🛠️
|
|
4
|
+
|
|
5
|
+
A resource pool ledger for Ruby. You register the slots you've built, check them out, check them in, and ask which idle ones are old enough to tear down. The `Pool` never opens a socket or closes a file. It keeps the roster: which slot is busy, which is idle and since when, and whether the handle you just returned is the one it issued or a stale copy from an earlier trip.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gem 'pool_machines'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
pool = PoolMachines::Pool.new
|
|
15
|
+
now = -> { Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond) }
|
|
16
|
+
|
|
17
|
+
# You build the connection. The pool just learns that slot 0 is serviceable.
|
|
18
|
+
connections[0] = PG.connect(db_url)
|
|
19
|
+
pool.register(0, now.call) # => true
|
|
20
|
+
|
|
21
|
+
# Each request borrows a slot and gives it back.
|
|
22
|
+
handle = pool.checkout(now.call) # => PoolMachines::Handle, or nil when none is free
|
|
23
|
+
connections[handle.slot].exec(sql)
|
|
24
|
+
pool.checkin(handle, now.call) # => "idle"
|
|
25
|
+
|
|
26
|
+
# On a quiet night, tear down whatever has sat idle for five minutes.
|
|
27
|
+
while (slot = pool.retire_idle(now.call, 5 * 60 * 1_000_000_000))
|
|
28
|
+
connections.delete(slot).close
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Times are nanoseconds from a monotonic clock. Slots are the integers 0 through 31.
|
|
33
|
+
|
|
34
|
+
## A Message to the Resistance
|
|
35
|
+
|
|
36
|
+
So you wrote a connection pool. Everyone has. An array of connections, a mutex, `pop` on the way out, `push` on the way back. It worked for a year.
|
|
37
|
+
|
|
38
|
+
Then a request timed out mid-query, the rescue block pushed the connection back, the ensure block pushed it back again, and for the next six hours two threads shared one socket. The errors made no sense because the errors belonged to someone else's query.
|
|
39
|
+
|
|
40
|
+
The bug isn't the array. The bug is that a bare connection object can't tell you whether it's already been returned. The thing you push back has to carry a receipt, and the pool has to refuse a receipt it has already stamped. This gem is the receipt book. Your connections stay yours.
|
|
41
|
+
|
|
42
|
+
## Chapter 1: Stardate 2025.186, the AMS Little
|
|
43
|
+
|
|
44
|
+
> **AMS Little**, NCC-4214, *Little*-class service tender.
|
|
45
|
+
> Do not fabricate a new warp core for every departure.
|
|
46
|
+
|
|
47
|
+
The tender doesn't build engines. It keeps the dock roster: thirty-two bays, which are occupied, which hold a serviced core waiting for a ship, and how long each has waited. A crew that brings a core back gets a fresh bay ticket. A crew that turns up with last week's ticket is turned away, because that bay has been issued to someone else since.
|
|
48
|
+
|
|
49
|
+
Somewhere on the dock, a cadet proposes:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
def with_connection
|
|
53
|
+
conn = @free.pop || PG.connect(db_url)
|
|
54
|
+
yield conn
|
|
55
|
+
rescue => e
|
|
56
|
+
@free.push(conn) # give it back on error
|
|
57
|
+
raise
|
|
58
|
+
ensure
|
|
59
|
+
@free.push(conn) # and also give it back always
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
"That," says the dockmaster, "is how two ships leave on one core."
|
|
64
|
+
|
|
65
|
+
## The Flight Plan
|
|
66
|
+
|
|
67
|
+
Every slot is in exactly one state. Draining is a property of the whole pool.
|
|
68
|
+
|
|
69
|
+
```mermaid
|
|
70
|
+
stateDiagram-v2
|
|
71
|
+
[*] --> vacant
|
|
72
|
+
vacant --> idle: register(slot, now)
|
|
73
|
+
idle --> busy: checkout(now) issues a handle
|
|
74
|
+
busy --> idle: checkin(handle, now) returns "idle"
|
|
75
|
+
busy --> vacant: checkin while draining returns "retire"
|
|
76
|
+
idle --> vacant: retire_idle(now, idle_for) picks it
|
|
77
|
+
vacant --> idle: register again, same slot, fresh generation
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
And the deploy where the old pool was drained:
|
|
81
|
+
|
|
82
|
+
```mermaid
|
|
83
|
+
sequenceDiagram
|
|
84
|
+
participant App
|
|
85
|
+
participant Pool
|
|
86
|
+
participant DB as Connections
|
|
87
|
+
|
|
88
|
+
App->>Pool: register(0, t0)
|
|
89
|
+
App->>Pool: register(1, t0)
|
|
90
|
+
App->>Pool: checkout(t1)
|
|
91
|
+
Pool-->>App: handle for slot 0
|
|
92
|
+
Note over App: query in flight on slot 0
|
|
93
|
+
|
|
94
|
+
Note over App: SIGTERM, deploy starting
|
|
95
|
+
App->>Pool: begin_drain
|
|
96
|
+
App->>Pool: checkout(t2)
|
|
97
|
+
Pool-->>App: nil, no new departures
|
|
98
|
+
App->>Pool: retire_idle(t2, any)
|
|
99
|
+
Pool-->>App: 1
|
|
100
|
+
App->>DB: close connection 1
|
|
101
|
+
|
|
102
|
+
App->>Pool: checkin(handle, t3)
|
|
103
|
+
Pool-->>App: "retire"
|
|
104
|
+
App->>DB: close connection 0
|
|
105
|
+
Note over Pool: checked_out is 0. Dock is empty.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## How the Dock Works
|
|
109
|
+
|
|
110
|
+
### Register and check out
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
pool = PoolMachines::Pool.new
|
|
114
|
+
pool.checkout(0) # => nil, nothing registered yet
|
|
115
|
+
|
|
116
|
+
pool.register(0, 1) # => true
|
|
117
|
+
pool.register(0, 1) # => false, already on the roster
|
|
118
|
+
|
|
119
|
+
handle = pool.checkout(2)
|
|
120
|
+
handle.slot # => 0
|
|
121
|
+
pool.checked_out # => 1
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`checkout` returns `nil` when every registered slot is busy. It never blocks and never builds anything. If you want to grow the pool, build a resource and `register` a free slot number.
|
|
125
|
+
|
|
126
|
+
### Check in, and the receipt that only works once
|
|
127
|
+
|
|
128
|
+
A handle is good for exactly one `checkin`. Return it and the slot goes idle. Return it again and the pool raises, because the slot may already belong to a newer handle.
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
pool = PoolMachines::Pool.new
|
|
132
|
+
pool.register(0, 1)
|
|
133
|
+
|
|
134
|
+
first = pool.checkout(2)
|
|
135
|
+
pool.checkin(first, 3) # => "idle"
|
|
136
|
+
|
|
137
|
+
second = pool.checkout(4) # same slot 0, new receipt
|
|
138
|
+
pool.checkin(first, 5) # raises ArgumentError, that receipt is spent
|
|
139
|
+
pool.checkin(second, 5) # => "idle"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
That's the double-push bug from the cadet's code, caught at the second push instead of six hours later.
|
|
143
|
+
|
|
144
|
+
### Retire what's been sitting
|
|
145
|
+
|
|
146
|
+
`retire_idle` hands you one slot per call: an idle slot that has been idle for at least `idle_for` nanoseconds. It returns `nil` when nothing qualifies. Loop until `nil`, closing as you go.
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
pool = PoolMachines::Pool.new
|
|
150
|
+
pool.register(0, 0)
|
|
151
|
+
pool.register(1, 0)
|
|
152
|
+
|
|
153
|
+
pool.retire_idle(2, 3) # => nil, idle for 2, needs 3
|
|
154
|
+
pool.retire_idle(10, 5) # => 0
|
|
155
|
+
pool.retire_idle(10, 5) # => 1
|
|
156
|
+
pool.retire_idle(10, 5) # => nil
|
|
157
|
+
|
|
158
|
+
pool.register(0, 11) # => true, the slot is vacant again
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
A busy slot is never retired. The pool will tell you to close it when it comes back.
|
|
162
|
+
|
|
163
|
+
### Drain
|
|
164
|
+
|
|
165
|
+
`begin_drain` flips the pool into wind-down. No more registrations, no more checkouts. Idle slots retire regardless of how long they've been idle. Busy slots keep working, and when each one is checked in the answer is `"retire"` instead of `"idle"`, which is your cue to close it.
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
pool = PoolMachines::Pool.new
|
|
169
|
+
pool.register(0, 0)
|
|
170
|
+
pool.register(1, 0)
|
|
171
|
+
handle = pool.checkout(1)
|
|
172
|
+
|
|
173
|
+
pool.begin_drain
|
|
174
|
+
pool.checkout(3) # => nil
|
|
175
|
+
pool.register(2, 3) # => false
|
|
176
|
+
pool.retire_idle(3, 100) # => 1, idle_for is ignored while draining
|
|
177
|
+
pool.checkin(handle, 4) # => "retire"
|
|
178
|
+
pool.checked_out # => 0
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Drain never yanks a resource out from under a running query. It only refuses new work and tells you what to close as work finishes.
|
|
182
|
+
|
|
183
|
+
### The clock only moves forward
|
|
184
|
+
|
|
185
|
+
You pass `now` on every call. The pool remembers the latest time it has seen and treats anything earlier as a non-event: `register` returns `false`, `checkout` returns `nil`, `checkin` raises.
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
pool = PoolMachines::Pool.new
|
|
189
|
+
pool.register(0, 5)
|
|
190
|
+
pool.register(1, 3) # => false, time went backwards
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Use a monotonic clock. Wall clocks jump.
|
|
194
|
+
|
|
195
|
+
## The Rules of the Tender
|
|
196
|
+
|
|
197
|
+
- **32 slots, numbered 0 to 31.** Slot 32 or a negative slot raises `RangeError`.
|
|
198
|
+
- **Every number is an Integer.** Slots, times, and durations. Floats, strings, and NaN raise `TypeError`. Values at or above `2**64` raise `RangeError`.
|
|
199
|
+
- **Handles are opaque and single-use.** You can't construct one, `dup` one, or hand one to a different pool. A spent or foreign handle raises `ArgumentError` on `checkin`.
|
|
200
|
+
- **Rejections are quiet, misuse is loud.** A duplicate `register`, a backwards clock, or an empty pool returns `false` or `nil`. A bad handle or a bad type raises.
|
|
201
|
+
- **Frozen means frozen.** `freeze` the pool and every mutating call raises `FrozenError`. `checked_out` still answers.
|
|
202
|
+
|
|
203
|
+
## What You Think You're Doing vs Reality
|
|
204
|
+
|
|
205
|
+
### You Think: "The ensure block guarantees the connection goes back."
|
|
206
|
+
### Reality: The ensure block guarantees it goes back at least once.
|
|
207
|
+
|
|
208
|
+
`ensure` runs after `rescue`. If both return the connection, it's returned twice, and a plain array can't tell. A pool that issues receipts can, and refuses the second one on the spot.
|
|
209
|
+
|
|
210
|
+
## What's Yours
|
|
211
|
+
|
|
212
|
+
The pool holds slot numbers and timestamps, nothing else. You create the real resource before `register`, keep it somewhere indexed by slot, and close it when `retire_idle` or a `"retire"` check-in says so. Health checks, waiting for a free slot, and persistence across restarts are all outside the roster. Serialize calls to one pool; it doesn't lock for you.
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT License. See [LICENSE.txt](LICENSE.txt) for details.
|
|
217
|
+
|
|
218
|
+
**Remember: The second `push` always looks harmless. The receipt book is what makes it fail where you can see it.**
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.2](https://github.com/seuros/pool_machines/compare/pool-machines-v0.2.1...pool-machines-v0.2.2) (2026-11-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* crates.io keywords and categories for the core ([e65b159](https://github.com/seuros/pool_machines/commit/e65b1592ae2868c0acea23df562fab5b43dab71f))
|
|
9
|
+
|
|
10
|
+
## [0.2.1](https://github.com/seuros/pool_machines/compare/pool-machines-v0.2.0...pool-machines-v0.2.1) (2026-11-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* keep test sources out of the packaged gem and crate ([d256e29](https://github.com/seuros/pool_machines/commit/d256e29c8cb52218216e7c796057a7fef3205b00))
|
|
16
|
+
|
|
17
|
+
## [0.2.0](https://github.com/seuros/pool_machines/compare/pool-machines-v0.1.0...pool-machines-v0.2.0) (2026-11-06)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* initial extraction ([030ecb6](https://github.com/seuros/pool_machines/commit/030ecb6977a3559cd397225ab2d38137a46d39b7))
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# pool-machines
|
|
2
|
+
|
|
3
|
+
> **AMS Little**, NCC-4214, *Little*-class service tender.
|
|
4
|
+
|
|
5
|
+
Engine-room flight manual: track resource checkouts, returns, and retirement.
|
|
6
|
+
|
|
7
|
+
Allocation-free `no_std` lifecycle decisions for fixed resource slots.
|
|
8
|
+
Register externally created resources, check out the oldest idle slot, and return
|
|
9
|
+
it with a generation-checked handle. Duplicate/stale returns are rejected.
|
|
10
|
+
Begin draining to refuse new checkouts; returned resources then retire rather
|
|
11
|
+
than becoming idle. Idle retirement frees a slot for external destruction.
|
|
12
|
+
|
|
13
|
+
Clock observations must not regress. Generations fail closed at exhaustion.
|
|
14
|
+
Handles are scoped to one pool instance/lifetime; do not exchange handles between
|
|
15
|
+
pools. The adapter must serialize these decisions with resource storage and
|
|
16
|
+
actually destroy retired resources. There is no automatic construction, health
|
|
17
|
+
check, async waiting, timeout driver, forced reclamation of busy resources,
|
|
18
|
+
or runtime ownership guarantee.
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module PoolMachines
|
|
2
|
+
VERSION: String
|
|
3
|
+
# Opaque checkout result; direct construction is unsupported.
|
|
4
|
+
class Handle
|
|
5
|
+
def slot: () -> Integer
|
|
6
|
+
end
|
|
7
|
+
class Pool
|
|
8
|
+
def self.new: () -> instance
|
|
9
|
+
def register: (Integer slot, Integer now_ns) -> bool
|
|
10
|
+
def checkout: (Integer now_ns) -> Handle?
|
|
11
|
+
def checkin: (Handle handle, Integer now_ns) -> String
|
|
12
|
+
def begin_drain: () -> nil
|
|
13
|
+
def retire_idle: (Integer now_ns, Integer idle_for_ns) -> Integer?
|
|
14
|
+
def checked_out: () -> Integer
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: pool_machines
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.3
|
|
5
|
+
platform: arm64-darwin
|
|
6
|
+
authors:
|
|
7
|
+
- Abdelkader Boudih
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-11-06 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Initial native roster model with draining and retirement. Caller owns
|
|
14
|
+
resources and execution. Local sibling Rust checkout required.
|
|
15
|
+
email:
|
|
16
|
+
- oss@seuros.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- CHANGELOG.md
|
|
22
|
+
- LICENSE.txt
|
|
23
|
+
- README.md
|
|
24
|
+
- ext/pool_machines_native/core/CHANGELOG.md
|
|
25
|
+
- ext/pool_machines_native/core/README.md
|
|
26
|
+
- ext/pool_machines_native/extconf.rb
|
|
27
|
+
- lib/pool_machines.rb
|
|
28
|
+
- lib/pool_machines/version.rb
|
|
29
|
+
- lib/pool_machines_native/pool_machines_native.bundle
|
|
30
|
+
- sig/pool_machines.rbs
|
|
31
|
+
homepage: https://github.com/seuros/pool_machines
|
|
32
|
+
licenses:
|
|
33
|
+
- MIT
|
|
34
|
+
metadata:
|
|
35
|
+
source_code_uri: https://github.com/seuros/pool_machines
|
|
36
|
+
rubygems_mfa_required: 'true'
|
|
37
|
+
cargo_crate_name: pool_machines_native
|
|
38
|
+
cargo_manifest_path: ext/pool_machines_native/ffi/Cargo.toml
|
|
39
|
+
post_install_message:
|
|
40
|
+
rdoc_options: []
|
|
41
|
+
require_paths:
|
|
42
|
+
- lib
|
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '4.0'
|
|
48
|
+
- - "<"
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: 4.1.dev
|
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '0'
|
|
56
|
+
requirements: []
|
|
57
|
+
rubygems_version: 3.5.23
|
|
58
|
+
signing_key:
|
|
59
|
+
specification_version: 4
|
|
60
|
+
summary: Native generation-checked resource-slot policy for Ruby
|
|
61
|
+
test_files: []
|