redis-promise 0.1.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 +7 -0
- data/.github/workflows/ci.yml +74 -0
- data/CHANGELOG.md +19 -0
- data/LICENSE +21 -0
- data/README.md +151 -0
- data/Rakefile +12 -0
- data/lib/redis/promise/resolver.rb +47 -0
- data/lib/redis/promise/resque.rb +49 -0
- data/lib/redis/promise/version.rb +8 -0
- data/lib/redis/promise.rb +83 -0
- data/sorbet/config +7 -0
- data/sorbet/rbi/annotations/.gitattributes +1 -0
- data/sorbet/rbi/annotations/minitest.rbi +120 -0
- data/sorbet/rbi/annotations/rainbow.rbi +269 -0
- data/sorbet/rbi/gems/.gitattributes +1 -0
- data/sorbet/rbi/gems/ast@2.4.3.rbi +586 -0
- data/sorbet/rbi/gems/byebug@13.0.0.rbi +37 -0
- data/sorbet/rbi/gems/connection_pool@3.0.2.rbi +340 -0
- data/sorbet/rbi/gems/date@3.5.1.rbi +403 -0
- data/sorbet/rbi/gems/erb@6.0.4.rbi +814 -0
- data/sorbet/rbi/gems/io-console@0.8.2.rbi +9 -0
- data/sorbet/rbi/gems/json@2.19.5.rbi +2250 -0
- data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +323 -0
- data/sorbet/rbi/gems/minitest@5.27.0.rbi +1549 -0
- data/sorbet/rbi/gems/parallel@2.1.0.rbi +359 -0
- data/sorbet/rbi/gems/pp@0.6.3.rbi +388 -0
- data/sorbet/rbi/gems/prettyprint@0.2.0.rbi +477 -0
- data/sorbet/rbi/gems/psych@5.3.1.rbi +2555 -0
- data/sorbet/rbi/gems/racc@1.8.1.rbi +168 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +403 -0
- data/sorbet/rbi/gems/rake@13.4.2.rbi +3258 -0
- data/sorbet/rbi/gems/redis-client@0.29.0.rbi +1203 -0
- data/sorbet/rbi/gems/redis@5.4.1.rbi +3552 -0
- data/sorbet/rbi/gems/reline@0.6.3.rbi +2995 -0
- data/sorbet/rbi/gems/rubocop-espago@1.2.0.rbi +9 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1318 -0
- data/sorbet/rbi/gems/shoulda-context@2.0.0.rbi +563 -0
- data/sorbet/rbi/gems/stringio@3.2.0.rbi +9 -0
- data/sorbet/rbi/gems/tsort@0.2.0.rbi +393 -0
- data/sorbet/rbi/gems/unicode-display_width@3.2.0.rbi +132 -0
- data/sorbet/rbi/gems/unicode-emoji@4.2.0.rbi +254 -0
- data/sorbet/rbi/shims/gems/resque.rbi +9 -0
- data/sorbet/rbi/shims/gems/shoulda-context.rbi +16 -0
- data/sorbet/rbi/todo.rbi +5 -0
- data/sorbet/tapioca/config.yml +31 -0
- data/sorbet/tapioca/extensions/load_gem.rb +1 -0
- data/sorbet/tapioca/require.rb +5 -0
- metadata +117 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b87c0c0b6399954b74fb54514c2e7b97cb800a5cf6b18025d80cbdcf084d6402
|
|
4
|
+
data.tar.gz: de74a5dc0a7e53dfa122af59ec5410a0cb4addeacd848695b14d9da8eecbee70
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: bdd765fc640c834fb59eb97e1eda6bb59e8b5623f348ebbb677f5c1e067103824b64f192f89d5f302287e6e1a16b44503e251a2e751358be1d1dc83bf3a576d8
|
|
7
|
+
data.tar.gz: '080b8d58f3ad11e1305d9575d8393561584a7a452a04c1a7f2168216ba1371fb13346357d12586df60a8b2e0fb173a9cd221a24a292b1922b55700a441208d63'
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: CI Ruby
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "main", "develop" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "main", "develop" ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
services:
|
|
13
|
+
# Label used to access the service container
|
|
14
|
+
redis:
|
|
15
|
+
# Docker Hub image
|
|
16
|
+
image: redis
|
|
17
|
+
# Set health checks to wait until redis has started
|
|
18
|
+
options: >-
|
|
19
|
+
--health-cmd "redis-cli ping"
|
|
20
|
+
--health-interval 10s
|
|
21
|
+
--health-timeout 5s
|
|
22
|
+
--health-retries 5
|
|
23
|
+
ports:
|
|
24
|
+
# Maps port 6379 on service container to the host
|
|
25
|
+
- 6379:6379
|
|
26
|
+
env:
|
|
27
|
+
CI: true
|
|
28
|
+
# The hostname used to communicate with the Redis service container
|
|
29
|
+
REDIS_HOST: localhost
|
|
30
|
+
# The default Redis port
|
|
31
|
+
REDIS_PORT: 6379
|
|
32
|
+
strategy:
|
|
33
|
+
matrix:
|
|
34
|
+
ruby-version: [ "3.3", "3.4", "4.0" ]
|
|
35
|
+
steps:
|
|
36
|
+
- name: Checkout code
|
|
37
|
+
uses: actions/checkout@v3
|
|
38
|
+
- name: Install Ruby and gems
|
|
39
|
+
uses: ruby/setup-ruby@v1
|
|
40
|
+
with:
|
|
41
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
42
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
43
|
+
- name: Run unit tests
|
|
44
|
+
run: bundle exec rake test
|
|
45
|
+
|
|
46
|
+
lint:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
env:
|
|
49
|
+
CI: true
|
|
50
|
+
steps:
|
|
51
|
+
- name: Checkout code
|
|
52
|
+
uses: actions/checkout@v3
|
|
53
|
+
- name: Install Ruby and gems
|
|
54
|
+
uses: ruby/setup-ruby@v1
|
|
55
|
+
with:
|
|
56
|
+
ruby-version: "4.0"
|
|
57
|
+
bundler-cache: true
|
|
58
|
+
- name: Lint Ruby files
|
|
59
|
+
run: bundle exec rubocop --parallel
|
|
60
|
+
|
|
61
|
+
typecheck:
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
env:
|
|
64
|
+
CI: true
|
|
65
|
+
steps:
|
|
66
|
+
- name: Checkout code
|
|
67
|
+
uses: actions/checkout@v3
|
|
68
|
+
- name: Install Ruby and gems
|
|
69
|
+
uses: ruby/setup-ruby@v1
|
|
70
|
+
with:
|
|
71
|
+
ruby-version: "4.0"
|
|
72
|
+
bundler-cache: true
|
|
73
|
+
- name: Typecheck Ruby files
|
|
74
|
+
run: bundle exec srb tc
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
Add changes in new features here. Do not change the gem's version in pull/merge requests.
|
|
11
|
+
|
|
12
|
+
### Changes
|
|
13
|
+
-
|
|
14
|
+
|
|
15
|
+
## [0.1.0] - 22.05.2026
|
|
16
|
+
|
|
17
|
+
[Diff](https://github.com/Verseth/ruby-redis-promise/compare/v0.0.0...v0.1.0)
|
|
18
|
+
|
|
19
|
+
- Initial release
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mateusz Drewniak
|
|
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 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,151 @@
|
|
|
1
|
+
# Redis::Promise
|
|
2
|
+
|
|
3
|
+
This Ruby gem adds promises built on Redis.
|
|
4
|
+
It enables you to resolve or reject a redis promise based on a key and
|
|
5
|
+
await the result in another thread or on another machine/service.
|
|
6
|
+
|
|
7
|
+
It can be used in tandem with job frameworks like `resque` or `sidekiq` in order
|
|
8
|
+
to get back a result from an asynchronous task.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bundle add redis-promise
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
gem install redis-promise
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
This library adds two basic types `Redis::Promise` and `Redis::Promise::Resolver`.
|
|
27
|
+
The Promise object can be used to await a resolved (success) value or a rejected (error) value.
|
|
28
|
+
The Resolver object can be used to resolve or reject a promise. These two objects are tied together by a common redis key where the result gets stored.
|
|
29
|
+
|
|
30
|
+
The basic idea is that you use a `Redis::Promise::Resolver` to resolve/reject the promise in one context eg. a thread or a different service. You then use `Redis::Promise` in another context to get (await) the value.
|
|
31
|
+
|
|
32
|
+
### Threads
|
|
33
|
+
|
|
34
|
+
Using the `Redis::Promise::create` helper to create a promise
|
|
35
|
+
and its resolver at the same time.
|
|
36
|
+
|
|
37
|
+
```rb
|
|
38
|
+
require 'redis/promise'
|
|
39
|
+
|
|
40
|
+
redis = Redis.new
|
|
41
|
+
promise, resolver = Redis::Promise.create(redis)
|
|
42
|
+
|
|
43
|
+
threads = []
|
|
44
|
+
threads << Thread.new do
|
|
45
|
+
sleep(3)
|
|
46
|
+
resolver.resolve("OK!")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
threads << Thread.new do
|
|
50
|
+
result = promise.await #=> "OK!"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
threads.each(&:join)
|
|
54
|
+
# should print "OK!" after 3 seconds
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Creating a separate promise and resolver.
|
|
58
|
+
Notice that the promise and resolver have to get their own
|
|
59
|
+
Redis connection instances, that's because the `promise.await`
|
|
60
|
+
blocks the entire redis connection making it impossible to resolve
|
|
61
|
+
the promise using the same connection.
|
|
62
|
+
|
|
63
|
+
```rb
|
|
64
|
+
require 'redis/promise'
|
|
65
|
+
|
|
66
|
+
redis = Redis.new
|
|
67
|
+
threads = []
|
|
68
|
+
|
|
69
|
+
promise = Redis::Promise.new(redis)
|
|
70
|
+
threads << Thread.new do
|
|
71
|
+
puts promise.await #=> "OK!"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
resolver = Redis::Promise::Resolver.new(redis, key: promise.key)
|
|
75
|
+
threads << Thread.new do
|
|
76
|
+
sleep(3)
|
|
77
|
+
resolver.resolve("OK!")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
threads.each(&:join)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Resque
|
|
84
|
+
|
|
85
|
+
You could use it in resque or any other job system to get the result from an asynchronous task.
|
|
86
|
+
|
|
87
|
+
```rb
|
|
88
|
+
# ========
|
|
89
|
+
# job definition
|
|
90
|
+
|
|
91
|
+
class SomeJob
|
|
92
|
+
@queue = :example
|
|
93
|
+
|
|
94
|
+
def self.perform(promise_key)
|
|
95
|
+
resolver = Redis::Promise::Resolver.new(
|
|
96
|
+
Resque.redis,
|
|
97
|
+
key: promise_key,
|
|
98
|
+
)
|
|
99
|
+
resolver.resolve(42)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# ========
|
|
104
|
+
# some other place in the app
|
|
105
|
+
|
|
106
|
+
promise = Redis::Promise.new(Resque.redis)
|
|
107
|
+
Resque.enqueue(SomeJob, promise.key)
|
|
108
|
+
|
|
109
|
+
promise.await #=> 42
|
|
110
|
+
# you got a value back from a resque job!
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### Plugin
|
|
114
|
+
|
|
115
|
+
There is an additional resque plugin you can load
|
|
116
|
+
to make it more convenient.
|
|
117
|
+
|
|
118
|
+
```rb
|
|
119
|
+
require 'redis/promise/resque'
|
|
120
|
+
|
|
121
|
+
class SomeJob
|
|
122
|
+
include Redis::Promise::Resque
|
|
123
|
+
|
|
124
|
+
@queue = :example
|
|
125
|
+
|
|
126
|
+
run do |n|
|
|
127
|
+
raise ArgumentError, "number must be positive: #{n}" if n < 0
|
|
128
|
+
|
|
129
|
+
n * 69
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# enqueueing returns a promise
|
|
134
|
+
promise = SomeJob.enqueue(42)
|
|
135
|
+
promise.await #=> 2898
|
|
136
|
+
|
|
137
|
+
# errors thrown in the job get caught and rethrown on await
|
|
138
|
+
promise = SomeJob.enqueue(-2)
|
|
139
|
+
promise.await
|
|
140
|
+
#! Redis::Promise::RejectedError(value: "[ArgumentError]: number must be positive: -2")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Development
|
|
144
|
+
|
|
145
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
146
|
+
|
|
147
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
148
|
+
|
|
149
|
+
## Contributing
|
|
150
|
+
|
|
151
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Verseth/ruby-redis-promise.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
class Redis
|
|
5
|
+
class Promise
|
|
6
|
+
# An object used to resolve or reject
|
|
7
|
+
# a redis promise.
|
|
8
|
+
class Resolver
|
|
9
|
+
#: Redis
|
|
10
|
+
attr_reader :redis
|
|
11
|
+
|
|
12
|
+
#: String
|
|
13
|
+
attr_reader :key
|
|
14
|
+
|
|
15
|
+
#: (Redis, ?id: String, ?namespace: String, ?key: String) -> void
|
|
16
|
+
def initialize(redis, id: SecureRandom.uuid_v4, namespace: 'global', key: "promise:#{namespace}:#{id}")
|
|
17
|
+
@redis = redis.dup #: Redis
|
|
18
|
+
@key = key
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Resolve the promise with the given value.
|
|
22
|
+
# The value gets serialized to JSON using `to_json`.
|
|
23
|
+
#
|
|
24
|
+
#: (top) -> void
|
|
25
|
+
def resolve(value)
|
|
26
|
+
serialized = { value: value }.to_json
|
|
27
|
+
push(serialized)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Reject the promise with the given value.
|
|
31
|
+
# The value gets serialized to JSON using `to_json`.
|
|
32
|
+
#
|
|
33
|
+
#: (top) -> void
|
|
34
|
+
def reject(err)
|
|
35
|
+
serialized = { err: err }.to_json
|
|
36
|
+
push(serialized)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
#: (String) -> void
|
|
42
|
+
def push(serialized)
|
|
43
|
+
@redis.rpush(@key, serialized)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
class Redis
|
|
5
|
+
class Promise
|
|
6
|
+
# An additional mixin for Resque jobs.
|
|
7
|
+
module Resque
|
|
8
|
+
extend T::Helpers
|
|
9
|
+
|
|
10
|
+
# Thrown when an invalid resque job class
|
|
11
|
+
# was used to enqueue a promise
|
|
12
|
+
class InvalidJobError < Error; end
|
|
13
|
+
|
|
14
|
+
# @requires_ancestor: Kernel
|
|
15
|
+
module ClassMethods
|
|
16
|
+
#: -> Redis
|
|
17
|
+
def promise_redis = ::Resque.redis
|
|
18
|
+
|
|
19
|
+
#: (*top) -> Promise
|
|
20
|
+
def enqueue(*args)
|
|
21
|
+
promise = Promise.new(promise_redis)
|
|
22
|
+
T.unsafe(::Resque).enqueue(self, promise.key, *args) # rubocop:disable Sorbet/ForbidTUnsafe
|
|
23
|
+
|
|
24
|
+
promise
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#: (String, *untyped) -> void
|
|
28
|
+
def perform(promise_key, *args); end
|
|
29
|
+
|
|
30
|
+
#: { (untyped) -> void } -> void
|
|
31
|
+
def run(&block)
|
|
32
|
+
define_singleton_method :perform do |promise_key, *args|
|
|
33
|
+
promise = Promise::Resolver.new(promise_redis, key: promise_key)
|
|
34
|
+
|
|
35
|
+
begin
|
|
36
|
+
result = block.call(*args)
|
|
37
|
+
promise.resolve(result)
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
promise.reject("[#{e.class}]: #{e.message}")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
mixes_in_class_methods(ClassMethods)
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'redis'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'securerandom'
|
|
7
|
+
require 'sorbet-runtime'
|
|
8
|
+
require_relative 'promise/version'
|
|
9
|
+
require_relative 'promise/resolver'
|
|
10
|
+
|
|
11
|
+
class Redis
|
|
12
|
+
# An object used to wait for a value or an error
|
|
13
|
+
# from redis.
|
|
14
|
+
class Promise
|
|
15
|
+
# Base error for `Redis::Promise`
|
|
16
|
+
class Error < StandardError; end
|
|
17
|
+
# Thrown when the timeout has been exceeded while
|
|
18
|
+
# awaiting a `Redis::Promise`
|
|
19
|
+
class TimeoutError < Error; end
|
|
20
|
+
|
|
21
|
+
# Thrown when an awaited promise has been rejected.
|
|
22
|
+
class RejectedError < Error
|
|
23
|
+
#: untyped
|
|
24
|
+
attr_reader :value
|
|
25
|
+
|
|
26
|
+
#: (untyped) -> void
|
|
27
|
+
def initialize(value)
|
|
28
|
+
@value = value
|
|
29
|
+
super("rejected redis promise: #{value.inspect}")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class << self
|
|
34
|
+
# Create a pair of Promise and Resolver objects for the same key.
|
|
35
|
+
# The resolver can be used to either resolve or reject the promise.
|
|
36
|
+
# The promise can be used to wait for the result.
|
|
37
|
+
#
|
|
38
|
+
#: (Redis, ?id: String, ?namespace: String) -> [Promise, Resolver]
|
|
39
|
+
def create(redis, id: SecureRandom.uuid_v4, namespace: 'global')
|
|
40
|
+
key = "promise:#{namespace}:#{id}"
|
|
41
|
+
|
|
42
|
+
promise = Promise.new(redis, key: key)
|
|
43
|
+
resolver = Resolver.new(redis, key: key)
|
|
44
|
+
|
|
45
|
+
[promise, resolver]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#: Redis
|
|
50
|
+
attr_reader :redis
|
|
51
|
+
|
|
52
|
+
#: String
|
|
53
|
+
attr_reader :key
|
|
54
|
+
|
|
55
|
+
#: (Redis, ?id: String, ?namespace: String, ?key: String) -> void
|
|
56
|
+
def initialize(redis, id: SecureRandom.uuid_v4, namespace: 'global', key: "promise:#{namespace}:#{id}")
|
|
57
|
+
@redis = redis.dup #: Redis
|
|
58
|
+
@key = key
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Blocks the current thread until the resolved or rejected value
|
|
62
|
+
# is available. By default it blocks indefinitely.
|
|
63
|
+
# If the promise has been successfully resolved its value gets returned.
|
|
64
|
+
# If the promise has been rejected a `Redis::Promise::RejectedError` gets thrown.
|
|
65
|
+
#
|
|
66
|
+
# You can provide an optional `timeout:` argument that is a float
|
|
67
|
+
# of number of seconds the client should wait for the value.
|
|
68
|
+
# If the timeout is exceeded a `Redis::Promise::TimeoutError` is thrown.
|
|
69
|
+
#
|
|
70
|
+
#: (?timeout: Float | Integer) -> untyped
|
|
71
|
+
def await(timeout: 0)
|
|
72
|
+
result = @redis.blpop(@key, timeout: timeout)
|
|
73
|
+
raise TimeoutError unless result
|
|
74
|
+
|
|
75
|
+
parsed = JSON.parse(result[1], symbolize_names: true)
|
|
76
|
+
|
|
77
|
+
err = parsed[:err]
|
|
78
|
+
raise RejectedError.new(err) if err # rubocop:disable Style/RaiseArgs
|
|
79
|
+
|
|
80
|
+
parsed[:value]
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/sorbet/config
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
**/*.rbi linguist-vendored=true
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
# DO NOT EDIT MANUALLY
|
|
4
|
+
# This file was pulled from a central RBI files repository.
|
|
5
|
+
# Please run `bin/tapioca annotations` to update it.
|
|
6
|
+
|
|
7
|
+
module Minitest::Assertions
|
|
8
|
+
sig { params(test: T.anything, msg: T.anything).returns(TrueClass) }
|
|
9
|
+
def assert(test, msg = nil); end
|
|
10
|
+
|
|
11
|
+
sig { params(obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
12
|
+
def assert_empty(obj, msg = nil); end
|
|
13
|
+
|
|
14
|
+
sig { params(exp: T.anything, act: T.anything, msg: T.anything).returns(TrueClass) }
|
|
15
|
+
def assert_equal(exp, act, msg = nil); end
|
|
16
|
+
|
|
17
|
+
sig { params(exp: T.anything, act: T.anything, delta: Numeric, msg: T.anything).returns(TrueClass) }
|
|
18
|
+
def assert_in_delta(exp, act, delta = T.unsafe(nil), msg = nil); end
|
|
19
|
+
|
|
20
|
+
sig { params(a: T.anything, b: T.anything, epsilon: Numeric, msg: T.anything).returns(TrueClass) }
|
|
21
|
+
def assert_in_epsilon(a, b, epsilon = T.unsafe(nil), msg = nil); end
|
|
22
|
+
|
|
23
|
+
sig { params(collection: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
24
|
+
def assert_includes(collection, obj, msg = nil); end
|
|
25
|
+
|
|
26
|
+
sig { params(cls: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
27
|
+
def assert_instance_of(cls, obj, msg = nil); end
|
|
28
|
+
|
|
29
|
+
sig { params(cls: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
30
|
+
def assert_kind_of(cls, obj, msg = nil); end
|
|
31
|
+
|
|
32
|
+
sig { params(matcher: T.any(String, Regexp), obj: T.anything, msg: T.anything).returns(MatchData) }
|
|
33
|
+
def assert_match(matcher, obj, msg = nil); end
|
|
34
|
+
|
|
35
|
+
sig { params(obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
36
|
+
def assert_nil(obj, msg = nil); end
|
|
37
|
+
|
|
38
|
+
sig { params(o1: T.anything, op: T.any(Symbol, String), o2: T.anything, msg: T.anything).returns(TrueClass) }
|
|
39
|
+
def assert_operator(o1, op, o2 = T.unsafe(nil), msg = nil); end
|
|
40
|
+
|
|
41
|
+
sig { params(stdout: T.nilable(T.any(String, Regexp)), stderr: T.nilable(T.any(String, Regexp)), block: T.proc.void).returns(T::Boolean) }
|
|
42
|
+
def assert_output(stdout = nil, stderr = nil, &block); end
|
|
43
|
+
|
|
44
|
+
sig { params(path: T.any(String, Pathname), msg: T.anything).returns(TrueClass) }
|
|
45
|
+
def assert_path_exists(path, msg = nil); end
|
|
46
|
+
|
|
47
|
+
sig { params(block: T.proc.void).returns(TrueClass) }
|
|
48
|
+
def assert_pattern(&block); end
|
|
49
|
+
|
|
50
|
+
sig { params(o1: T.anything, op: T.any(String, Symbol), msg: T.anything).returns(TrueClass) }
|
|
51
|
+
def assert_predicate(o1, op, msg = nil); end
|
|
52
|
+
|
|
53
|
+
sig { params(exp: NilClass, block: T.proc.void).returns(StandardError) }
|
|
54
|
+
sig { type_parameters(:T).params(exp: T.any(T::Class[T.type_parameter(:T)], Regexp, String), block: T.proc.void).returns(T.type_parameter(:T)) }
|
|
55
|
+
def assert_raises(*exp, &block); end
|
|
56
|
+
|
|
57
|
+
sig { params(obj: T.anything, meth: T.any(String, Symbol), msg: T.anything, include_all: T::Boolean).returns(TrueClass) }
|
|
58
|
+
def assert_respond_to(obj, meth, msg = nil, include_all: false); end
|
|
59
|
+
|
|
60
|
+
sig { params(exp: T.anything, act: T.anything, msg: T.anything).returns(TrueClass) }
|
|
61
|
+
def assert_same(exp, act, msg = nil); end
|
|
62
|
+
|
|
63
|
+
# @version < 6.0.0
|
|
64
|
+
sig { params(send_ary: T::Array[T.anything], m: T.anything).returns(T::Boolean) }
|
|
65
|
+
def assert_send(send_ary, m = nil); end
|
|
66
|
+
|
|
67
|
+
sig { params(block: T.proc.void).returns(T::Boolean) }
|
|
68
|
+
def assert_silent(&block); end
|
|
69
|
+
|
|
70
|
+
sig { params(sym: Symbol, msg: T.anything, block: T.proc.void).returns(T.anything) }
|
|
71
|
+
def assert_throws(sym, msg = nil, &block); end
|
|
72
|
+
|
|
73
|
+
sig { params(test: T.anything, msg: T.anything).returns(TrueClass) }
|
|
74
|
+
def refute(test, msg = nil); end
|
|
75
|
+
|
|
76
|
+
sig { params(obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
77
|
+
def refute_empty(obj, msg = nil); end
|
|
78
|
+
|
|
79
|
+
sig { params(exp: T.anything, act: T.anything, msg: T.anything).returns(TrueClass) }
|
|
80
|
+
def refute_equal(exp, act, msg = nil); end
|
|
81
|
+
|
|
82
|
+
sig { params(exp: T.anything, act: T.anything, delta: Numeric, msg: T.anything).returns(TrueClass) }
|
|
83
|
+
def refute_in_delta(exp, act, delta = T.unsafe(nil), msg = nil); end
|
|
84
|
+
|
|
85
|
+
sig { params(a: T.anything, b: T.anything, epsilon: Numeric, msg: T.anything).returns(TrueClass) }
|
|
86
|
+
def refute_in_epsilon(a, b, epsilon = T.unsafe(nil), msg = nil); end
|
|
87
|
+
|
|
88
|
+
sig { params(collection: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
89
|
+
def refute_includes(collection, obj, msg = nil); end
|
|
90
|
+
|
|
91
|
+
sig { params(cls: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
92
|
+
def refute_instance_of(cls, obj, msg = nil); end
|
|
93
|
+
|
|
94
|
+
sig { params(cls: T.anything, obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
95
|
+
def refute_kind_of(cls, obj, msg = nil); end
|
|
96
|
+
|
|
97
|
+
sig { params(matcher: T.any(String, Regexp), obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
98
|
+
def refute_match(matcher, obj, msg = nil); end
|
|
99
|
+
|
|
100
|
+
sig { params(obj: T.anything, msg: T.anything).returns(TrueClass) }
|
|
101
|
+
def refute_nil(obj, msg = nil); end
|
|
102
|
+
|
|
103
|
+
sig { params(block: T.proc.void).returns(TrueClass) }
|
|
104
|
+
def refute_pattern(&block); end
|
|
105
|
+
|
|
106
|
+
sig { params(o1: T.anything, op: T.any(Symbol, String), o2: T.anything, msg: T.anything).returns(TrueClass) }
|
|
107
|
+
def refute_operator(o1, op, o2 = T.unsafe(nil), msg = nil); end
|
|
108
|
+
|
|
109
|
+
sig { params(path: T.any(String, Pathname), msg: T.anything).returns(TrueClass) }
|
|
110
|
+
def refute_path_exists(path, msg = nil); end
|
|
111
|
+
|
|
112
|
+
sig { params(o1: T.anything, op: T.any(String, Symbol), msg: T.anything).returns(TrueClass) }
|
|
113
|
+
def refute_predicate(o1, op, msg = nil); end
|
|
114
|
+
|
|
115
|
+
sig { params(obj: T.anything, meth: T.any(String, Symbol), msg: T.anything, include_all: T::Boolean).returns(TrueClass) }
|
|
116
|
+
def refute_respond_to(obj, meth, msg = nil, include_all: false); end
|
|
117
|
+
|
|
118
|
+
sig { params(exp: T.anything, act: T.anything, msg: T.anything).returns(TrueClass) }
|
|
119
|
+
def refute_same(exp, act, msg = nil); end
|
|
120
|
+
end
|