election_machines 0.1.5-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 +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +212 -0
- data/ext/election_machines_native/core/CHANGELOG.md +22 -0
- data/ext/election_machines_native/core/README.md +17 -0
- data/ext/election_machines_native/extconf.rb +2 -0
- data/lib/election_machines/version.rb +4 -0
- data/lib/election_machines.rb +3 -0
- data/lib/election_machines_native/election_machines_native.bundle +0 -0
- data/sig/election_machines.rbs +15 -0
- metadata +62 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b961de842cf411204a93a7d9d9355daae92d28dd22de0c337b91229284522fc9
|
|
4
|
+
data.tar.gz: c2c10df934e7aee20731bcf4ee38e43128ce1c2541030afc1ab78f715a864cbc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e96dbd0847073ab57f016f0f1ab080a6bdf16dbfbc4efffcb6dcf7288c7dbf2f63285a0fa7b350313314f8cf4a23e3514edcd6708fe939ecc4a4c1d44c9d0da0
|
|
7
|
+
data.tar.gz: aa0cd946ac97c3ea2ec4920f53eb18dc669683f8c67c62aedc4fecf44f9ab6bdcf346b5e43f6276d133be6bf3cb71b7055321dd256865d9566cd6df17ab054cf
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.5](https://github.com/seuros/election_machines/compare/election_machines/v0.1.4...election_machines/v0.1.5) (2026-11-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* extract Wrapper::advance for election revision handling ([09bdd65](https://github.com/seuros/election_machines/commit/09bdd65226a7780bfe980bf19e5145ee2c2f1da0))
|
|
9
|
+
|
|
10
|
+
## [0.1.4](https://github.com/seuros/election_machines/compare/election_machines/v0.1.3...election_machines/v0.1.4) (2026-11-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* require lease-machines 0.3 ([d99d292](https://github.com/seuros/election_machines/commit/d99d2928ba0dd42e3013eb2d13e9a527387703ee))
|
|
16
|
+
|
|
17
|
+
## [0.1.3](https://github.com/seuros/election_machines/compare/election_machines/v0.1.2...election_machines/v0.1.3) (2026-11-06)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* crates.io keywords and categories for the core ([8c6d66c](https://github.com/seuros/election_machines/commit/8c6d66cc9ff19907ad68dc4043c9e421a66037ad))
|
|
23
|
+
|
|
24
|
+
## [0.1.2](https://github.com/seuros/election_machines/compare/election_machines/v0.1.1...election_machines/v0.1.2) (2026-11-06)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Bug Fixes
|
|
28
|
+
|
|
29
|
+
* keep test sources out of the packaged gem and crate ([27f00f1](https://github.com/seuros/election_machines/commit/27f00f1331d641a7d324ee7e560324a99f958bfa))
|
|
30
|
+
|
|
31
|
+
## [0.1.1](https://github.com/seuros/election_machines/compare/election_machines-v0.1.0...election_machines/v0.1.1) (2026-11-06)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Features
|
|
35
|
+
|
|
36
|
+
* initial extraction ([a804dd3](https://github.com/seuros/election_machines/commit/a804dd3a3c37b0f96acec918e7242475a63cd0c4))
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
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 copies
|
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
10
|
+
so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# ElectionMachines
|
|
2
|
+
|
|
3
|
+
> One captain at a time, and silence from the bridge is not a vacancy. 📡
|
|
4
|
+
|
|
5
|
+
Leader election for Ruby, backed by a lease. You hand in the list of workers that showed up, it names one of them captain and gives them a lease with a token. The captain keeps the chair by heartbeating before the lease runs out. Everyone else is told who the incumbent is. Nobody gets the chair by shouting louder, and a captain who stops answering loses it only when the clock says so.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gem 'election_machines'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
election = ElectionMachines::Election.new
|
|
15
|
+
|
|
16
|
+
# Three workers report in. The lowest id takes the chair.
|
|
17
|
+
status, leader, token, expires_at = election.elect([9, 2, 5], clock.now, 30.0)
|
|
18
|
+
# => ["elected", 2, 1, 30.0]
|
|
19
|
+
|
|
20
|
+
# The leader keeps the chair by heartbeating with its token.
|
|
21
|
+
election.heartbeat(2, token, clock.now, 30.0)
|
|
22
|
+
# => ["renewed", 2, 1, ...]
|
|
23
|
+
|
|
24
|
+
# Anyone else asking while the lease is live just learns who's in charge.
|
|
25
|
+
election.elect([5], clock.now, 30.0)
|
|
26
|
+
# => ["incumbent", 2, 1, ...]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Every call returns the same four-element shape: a status string, then the leader, token, and expiry, or three nils when there's nothing to report.
|
|
30
|
+
|
|
31
|
+
## A Message to the Resistance
|
|
32
|
+
|
|
33
|
+
So you have three schedulers and only one of them should be ticking the cron table. You picked "lowest hostname wins" and it worked for a year. Then the lowest hostname hit a network partition for ninety seconds. The other two couldn't reach it, decided it was dead, and the second-lowest took over. The first one never knew. For ninety seconds you had two captains, and your billing job ran twice.
|
|
34
|
+
|
|
35
|
+
"It stopped answering" and "it's gone" are different facts. The first is what you observed. The second is what you assumed. The gap between them is where the double-charge lives.
|
|
36
|
+
|
|
37
|
+
An election built on a lease closes that gap. The leader holds the chair until a stated time, not until someone stops hearing from it. If it's quiet but the lease is live, the answer to "can I take over?" is still no. When the lease lapses, the next election hands out a new token, and the old captain's token stops working everywhere it's checked.
|
|
38
|
+
|
|
39
|
+
## Chapter 1: Stardate 2025.186, the AMS LeLann
|
|
40
|
+
|
|
41
|
+
> **AMS LeLann**, NCC-4206, *LeLann*-class command relay.
|
|
42
|
+
> An unanswered hail is not permission to appoint a second captain.
|
|
43
|
+
|
|
44
|
+
The relay doesn't fly the ship. It keeps the roster and the chair. When a crew reports for duty, the relay names the lowest-numbered one captain and stamps a lease. Captains renew by hailing with their token. A crew that hails about an empty chair gets it. A crew that hails about an occupied one is told who sits there, and nothing else.
|
|
45
|
+
|
|
46
|
+
Somewhere on a lower deck, a lieutenant proposes:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
def am_i_leader?
|
|
50
|
+
peers.none? { |peer| peer.alive? && peer.id < my_id }
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
"That," says the relay officer, "is how a ship ends up with two captains during a comms blackout."
|
|
55
|
+
|
|
56
|
+
## The Flight Plan
|
|
57
|
+
|
|
58
|
+
```mermaid
|
|
59
|
+
stateDiagram-v2
|
|
60
|
+
[*] --> vacant
|
|
61
|
+
vacant --> held: elect, lowest candidate wins, token n
|
|
62
|
+
held --> held: heartbeat by leader with token, renewed
|
|
63
|
+
held --> held: elect by anyone before expiry, incumbent
|
|
64
|
+
held --> vacant: resign by leader with token, released
|
|
65
|
+
held --> held: lease lapses, next elect names a new leader with token n+1
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
And the ninety seconds the cron leader went quiet:
|
|
69
|
+
|
|
70
|
+
```mermaid
|
|
71
|
+
sequenceDiagram
|
|
72
|
+
participant A as Scheduler A (id 1)
|
|
73
|
+
participant E as Election
|
|
74
|
+
participant B as Scheduler B (id 2)
|
|
75
|
+
|
|
76
|
+
A->>E: elect([1, 2], t=0, ttl=30)
|
|
77
|
+
E-->>A: elected, leader 1, token 1, expires 30
|
|
78
|
+
Note over A: ticks the cron table, heartbeats every 10s
|
|
79
|
+
A->>E: heartbeat(1, token 1, t=10, ttl=30)
|
|
80
|
+
E-->>A: renewed, expires 40
|
|
81
|
+
|
|
82
|
+
Note over A: partitioned at t=12, silent
|
|
83
|
+
|
|
84
|
+
B->>E: elect([2], t=20, ttl=30)
|
|
85
|
+
E-->>B: incumbent, leader 1, token 1, expires 40
|
|
86
|
+
Note over B: lease still live. B waits.
|
|
87
|
+
|
|
88
|
+
B->>E: elect([2], t=45, ttl=30)
|
|
89
|
+
E-->>B: elected, leader 2, token 2, expires 75
|
|
90
|
+
Note over B: now ticks the cron table
|
|
91
|
+
|
|
92
|
+
Note over A: partition heals at t=50
|
|
93
|
+
A->>E: heartbeat(1, token 1, t=50, ttl=30)
|
|
94
|
+
E-->>A: WrongOwner
|
|
95
|
+
Note over A: stands down. One captain the whole time.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## How the Relay Works
|
|
99
|
+
|
|
100
|
+
### Elect
|
|
101
|
+
|
|
102
|
+
Pass the ids of the workers that are present, the current time, and how long the lease should last. The lowest id wins. The token starts at 1 and only ever goes up.
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
election = ElectionMachines::Election.new
|
|
106
|
+
|
|
107
|
+
election.elect([9, 2, 5], 0, 10) # => ["elected", 2, 1, 10.0]
|
|
108
|
+
election.lease # => [2, 1, 10.0]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Ask again while that lease is live and you get the incumbent, even if you offer a lower id. Sitting captains aren't unseated by a late arrival.
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
election.elect([1], 1, 10) # => ["incumbent", 2, 1, 10.0]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
An empty roster is `no_candidates`.
|
|
118
|
+
|
|
119
|
+
### Heartbeat
|
|
120
|
+
|
|
121
|
+
The leader renews by presenting its id and token. The new expiry is counted from `now`.
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
election = ElectionMachines::Election.new
|
|
125
|
+
election.elect([2], 0, 10)
|
|
126
|
+
|
|
127
|
+
election.heartbeat(2, 1, 3, 10) # => ["renewed", 2, 1, 13.0]
|
|
128
|
+
election.heartbeat(1, 1, 4, 10) # => ["WrongOwner", nil, nil, nil]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
A heartbeat from anyone but the leader is refused and changes nothing.
|
|
132
|
+
|
|
133
|
+
### Resign
|
|
134
|
+
|
|
135
|
+
A leader steps down by presenting its token. The chair is vacant immediately, and the next election fills it.
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
election.resign(2, 1, 5) # => ["released", nil, nil, nil]
|
|
139
|
+
election.lease # => nil
|
|
140
|
+
election.elect([7, 3], 6, 10) # => ["elected", 3, 2, 16.0]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Token 2. The old token is dead from here on.
|
|
144
|
+
|
|
145
|
+
### When the leader goes quiet
|
|
146
|
+
|
|
147
|
+
Nothing happens at the moment of expiry. The chair is only re-filled when someone asks. Until then, `lease` still describes the lapsed lease, so you can see who was last in charge.
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
election = ElectionMachines::Election.new
|
|
151
|
+
election.elect([2], 0, 10)
|
|
152
|
+
|
|
153
|
+
election.heartbeat(2, 1, 11, 10) # => ["Expired", nil, nil, nil]
|
|
154
|
+
election.lease # => [2, 1, 10.0]
|
|
155
|
+
|
|
156
|
+
election.elect([5], 12, 10) # => ["elected", 5, 2, 22.0]
|
|
157
|
+
election.heartbeat(2, 1, 13, 10) # => ["WrongOwner", nil, nil, nil]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The old leader's late heartbeat is rejected with `WrongOwner`. Its token is also stale, and `StaleToken` is what you'd see if it had the right id and the wrong token.
|
|
161
|
+
|
|
162
|
+
### Time only moves forward
|
|
163
|
+
|
|
164
|
+
You supply `now`. The election remembers the latest it has seen and refuses to go backwards.
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
election = ElectionMachines::Election.new
|
|
168
|
+
election.elect([2], 5, 10)
|
|
169
|
+
election.elect([2], 0, 10) # => ["ClockRegressed", nil, nil, nil]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Feed it a monotonic clock. Not the wall clock.
|
|
173
|
+
|
|
174
|
+
## Checkpoints
|
|
175
|
+
|
|
176
|
+
An election can hand you a `Checkpoint` and roll back to it, but only itself, and only to the most recent one.
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
election = ElectionMachines::Election.new
|
|
180
|
+
election.elect([1], 0, 10)
|
|
181
|
+
saved = election.checkpoint
|
|
182
|
+
|
|
183
|
+
election.heartbeat(1, 1, 1, 10) # => ["renewed", 1, 1, 11.0]
|
|
184
|
+
election.restore!(saved) # => ArgumentError, a transition happened since
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Any successful transition, or any successful restore, invalidates every checkpoint taken before it. That rules out rewinding past a decision someone has already acted on. A checkpoint from one election can't be fed to another. This is for testing and in-process simulation, not for surviving a restart.
|
|
188
|
+
|
|
189
|
+
## The Rules of the Relay
|
|
190
|
+
|
|
191
|
+
- **Ids and tokens are Integers** in `0..2**64-1`. Up to 64 candidates per election. Floats and strings raise `TypeError`, out of range raises `RangeError`, a 65th candidate raises `ArgumentError`.
|
|
192
|
+
- **Times are seconds** as Integer or Float, from a monotonic origin you choose. `ttl` must be positive. Negative, infinite, NaN, or absurdly large values raise `ArgumentError`.
|
|
193
|
+
- **Rejections don't raise.** `WrongOwner`, `StaleToken`, `Expired`, `ClockRegressed`, and `no_candidates` all come back as a status with three nils. Only malformed input raises.
|
|
194
|
+
- **Bad input never half-applies.** Arguments are checked before anything changes.
|
|
195
|
+
- **Frozen means frozen.** `freeze` the election and `elect`, `heartbeat`, `resign`, and `restore!` raise `FrozenError`. `lease` still reads.
|
|
196
|
+
|
|
197
|
+
## What You Think You're Doing vs Reality
|
|
198
|
+
|
|
199
|
+
### You Think: "If I can't reach the leader, the leader is down."
|
|
200
|
+
### Reality: You can't reach the leader. That's all you know.
|
|
201
|
+
|
|
202
|
+
A partition looks identical to a crash from the outside. If your election promotes on silence, every network hiccup is a coup. If it promotes on lease expiry, the worst case is a leaderless gap of one ttl, which is boring, and boring is what you want from the thing that decides who runs billing.
|
|
203
|
+
|
|
204
|
+
## What's Yours
|
|
205
|
+
|
|
206
|
+
The election decides. It doesn't talk to the network, run timers, or store anything across a restart. Run it in one place, write its decisions down before anyone acts on them, and make the thing the leader controls check the token before every action. A token the resource has already seen beaten is a captain who has already been replaced.
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
MIT License. See [LICENSE.txt](LICENSE.txt) for details.
|
|
211
|
+
|
|
212
|
+
**Remember: Scheduler A was never dead. It just couldn't hear you say it was.**
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.2](https://github.com/seuros/election_machines/compare/election-machines-v0.2.1...election-machines-v0.2.2) (2026-11-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* crates.io keywords and categories for the core ([8c6d66c](https://github.com/seuros/election_machines/commit/8c6d66cc9ff19907ad68dc4043c9e421a66037ad))
|
|
9
|
+
|
|
10
|
+
## [0.2.1](https://github.com/seuros/election_machines/compare/election-machines-v0.2.0...election-machines-v0.2.1) (2026-11-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* keep test sources out of the packaged gem and crate ([27f00f1](https://github.com/seuros/election_machines/commit/27f00f1331d641a7d324ee7e560324a99f958bfa))
|
|
16
|
+
|
|
17
|
+
## [0.2.0](https://github.com/seuros/election_machines/compare/election-machines-v0.1.0...election-machines-v0.2.0) (2026-11-06)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* initial extraction ([a804dd3](https://github.com/seuros/election_machines/commit/a804dd3a3c37b0f96acec918e7242475a63cd0c4))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# election-machines
|
|
2
|
+
|
|
3
|
+
> **AMS LeLann**, NCC-4206, *LeLann*-class command relay.
|
|
4
|
+
|
|
5
|
+
Engine-room flight manual: model command handovers through one lease authority.
|
|
6
|
+
|
|
7
|
+
`no_std` lease-backed owner nomination under one authoritative history.
|
|
8
|
+
Among participating candidates, the smallest numeric ID is nominated. An
|
|
9
|
+
unexpired incumbent remains leader even if absent from the current nomination
|
|
10
|
+
list; this list is not a consensus membership configuration.
|
|
11
|
+
|
|
12
|
+
All lease-machine persistence, fencing, authentication, and clock requirements
|
|
13
|
+
apply. A fresh election instance is a fresh namespace, not failover recovery.
|
|
14
|
+
Empty candidate lists make no authority transition. Heartbeats renew a lease;
|
|
15
|
+
they are not an asynchronous failure detector. This policy is **not Raft,
|
|
16
|
+
quorum voting, or partition-safe leader election without an external serialized
|
|
17
|
+
durable authority**. Raft elections remain in `consensus-machines`.
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module ElectionMachines
|
|
2
|
+
VERSION: String
|
|
3
|
+
type outcome = [String, Integer?, Integer?, Float?]
|
|
4
|
+
class Checkpoint
|
|
5
|
+
end
|
|
6
|
+
class Election
|
|
7
|
+
def self.new: () -> Election
|
|
8
|
+
def elect: (Array[Integer], Integer | Float, Integer | Float) -> outcome
|
|
9
|
+
def heartbeat: (Integer, Integer, Integer | Float, Integer | Float) -> outcome
|
|
10
|
+
def resign: (Integer, Integer, Integer | Float) -> outcome
|
|
11
|
+
def checkpoint: () -> Checkpoint
|
|
12
|
+
def restore!: (Checkpoint) -> bool
|
|
13
|
+
def lease: () -> [Integer, Integer, Float]?
|
|
14
|
+
end
|
|
15
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: election_machines
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.5
|
|
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: A thin Rust bridge for deterministic nomination and lease renewal. No
|
|
14
|
+
distributed election service.
|
|
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/election_machines_native/core/CHANGELOG.md
|
|
25
|
+
- ext/election_machines_native/core/README.md
|
|
26
|
+
- ext/election_machines_native/extconf.rb
|
|
27
|
+
- lib/election_machines.rb
|
|
28
|
+
- lib/election_machines/version.rb
|
|
29
|
+
- lib/election_machines_native/election_machines_native.bundle
|
|
30
|
+
- sig/election_machines.rbs
|
|
31
|
+
homepage: https://github.com/seuros/election_machines
|
|
32
|
+
licenses:
|
|
33
|
+
- MIT
|
|
34
|
+
metadata:
|
|
35
|
+
source_code_uri: https://github.com/seuros/election_machines
|
|
36
|
+
bug_tracker_uri: https://github.com/seuros/election_machines/issues
|
|
37
|
+
rubygems_mfa_required: 'true'
|
|
38
|
+
cargo_crate_name: election_machines_native
|
|
39
|
+
cargo_manifest_path: ext/election_machines_native/ffi/Cargo.toml
|
|
40
|
+
post_install_message:
|
|
41
|
+
rdoc_options: []
|
|
42
|
+
require_paths:
|
|
43
|
+
- lib
|
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '4.0'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: 4.1.dev
|
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '0'
|
|
57
|
+
requirements: []
|
|
58
|
+
rubygems_version: 3.5.23
|
|
59
|
+
signing_key:
|
|
60
|
+
specification_version: 4
|
|
61
|
+
summary: Native bounded lease-backed nomination model for Ruby
|
|
62
|
+
test_files: []
|