Skip to content

Commit ab651bb

Browse files
committed
Add fully functional Ruby client compatible with Go client
Here, try to do a full port of River to Ruby using a similar technique to Rust through repeated refinement iterations. I also had it try to go through each of River's features sections in the docs and make sure we have equivalent Ruby functionality. I changed a fair bit through manual inspection of the resulting APIs, but there's still a reasonable possibility we'll notice more things and want to make a few additional tweaks before fully committing.
1 parent f29d1d4 commit ab651bb

160 files changed

Lines changed: 15085 additions & 636 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yaml‎

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,41 @@ on:
2424
- push
2525

2626
jobs:
27+
verify:
28+
runs-on: ubuntu-latest
29+
timeout-minutes: 3
30+
permissions:
31+
contents: read
32+
33+
steps:
34+
- name: Checkout
35+
uses: actions/checkout@v4
36+
37+
- name: Install Ruby
38+
uses: ruby/setup-ruby@v1
39+
with:
40+
ruby-version: ${{ env.RUBY_VERSION }}
41+
42+
- name: Read upstream River revision
43+
id: river_revision
44+
run: |
45+
ruby -rjson -e '
46+
revision = JSON.parse(File.read("migration/manifest.json")).fetch("revision")
47+
abort "Invalid River revision" unless /\A[0-9a-f]{40}\z/.match?(revision)
48+
File.open(ENV.fetch("GITHUB_OUTPUT"), "a") { |file| file.puts("revision=#{revision}") }
49+
'
50+
51+
- name: Checkout upstream River
52+
uses: actions/checkout@v4
53+
with:
54+
path: upstream/river
55+
persist-credentials: false
56+
ref: ${{ steps.river_revision.outputs.revision }}
57+
repository: riverqueue/river
58+
59+
- name: Verify migrations
60+
run: make verify RIVER_PATH="$GITHUB_WORKSPACE/upstream/river"
61+
2762
gem_build:
2863
runs-on: ubuntu-latest
2964
timeout-minutes: 3
@@ -50,6 +85,19 @@ jobs:
5085
run: gem build riverqueue-sequel.gemspec
5186
working-directory: ./driver/riverqueue-sequel
5287

88+
- name: Build gem (riverqueue-redis)
89+
run: gem build riverqueue-redis.gemspec
90+
working-directory: ./driver/riverqueue-redis
91+
92+
- name: Build gem (riverqueue-rails)
93+
run: gem build riverqueue-rails.gemspec
94+
working-directory: ./rails/riverqueue-rails
95+
96+
- name: Build gem (riverqueue-pro)
97+
if: ${{ hashFiles('pro/riverqueue-pro/riverqueue-pro.gemspec') != '' }}
98+
run: gem build riverqueue-pro.gemspec
99+
working-directory: ./pro/riverqueue-pro
100+
53101
lint:
54102
runs-on: ubuntu-latest
55103
timeout-minutes: 3
@@ -68,6 +116,10 @@ jobs:
68116
run: bundle exec standardrb
69117
working-directory: .
70118

119+
- name: Frozen string literal comments
120+
run: bundle exec rubocop --config .rubocop-frozen-string-literal.yaml --only Style/FrozenStringLiteralComment
121+
working-directory: .
122+
71123
- name: bundle install (riverqueue-activerecord)
72124
run: bundle install
73125
working-directory: ./driver/riverqueue-activerecord
@@ -84,6 +136,16 @@ jobs:
84136
run: bundle exec standardrb
85137
working-directory: ./driver/riverqueue-sequel
86138

139+
- name: bundle install (riverqueue-pro)
140+
if: ${{ hashFiles('pro/riverqueue-pro/Gemfile') != '' }}
141+
run: bundle install
142+
working-directory: ./pro/riverqueue-pro
143+
144+
- name: Standard Ruby (riverqueue-pro)
145+
if: ${{ hashFiles('pro/riverqueue-pro/Gemfile') != '' }}
146+
run: bundle exec standardrb
147+
working-directory: ./pro/riverqueue-pro
148+
87149
tool_versions_check:
88150
runs-on: ubuntu-latest
89151

@@ -195,3 +257,74 @@ jobs:
195257
- name: Rspec (riverqueue-sequel)
196258
run: bundle exec rspec
197259
working-directory: ./driver/riverqueue-sequel
260+
261+
- name: bundle install (riverqueue-pro)
262+
if: ${{ hashFiles('pro/riverqueue-pro/Gemfile') != '' }}
263+
run: bundle install
264+
working-directory: ./pro/riverqueue-pro
265+
266+
- name: Rspec (riverqueue-pro, SQLite)
267+
if: ${{ hashFiles('pro/riverqueue-pro/Gemfile') != '' }}
268+
run: bundle exec rspec
269+
working-directory: ./pro/riverqueue-pro
270+
271+
redis:
272+
runs-on: ubuntu-latest
273+
timeout-minutes: 5
274+
strategy:
275+
matrix:
276+
ruby: ["3.2", "3.3", "3.4", "4.0"]
277+
steps:
278+
- uses: actions/checkout@v4
279+
- uses: ruby/setup-ruby@v1
280+
with:
281+
ruby-version: ${{ matrix.ruby }}
282+
- name: Install Redis
283+
run: sudo apt-get update && sudo apt-get install -y redis-server
284+
- run: bundle install
285+
working-directory: ./driver/riverqueue-redis
286+
- run: bundle exec rspec
287+
working-directory: ./driver/riverqueue-redis
288+
- run: bundle exec standardrb
289+
working-directory: ./driver/riverqueue-redis
290+
291+
rails:
292+
runs-on: ubuntu-latest
293+
timeout-minutes: 5
294+
strategy:
295+
matrix:
296+
include:
297+
- rails: "~> 7.2.0"
298+
ruby: "3.2"
299+
- rails: "~> 8.0.0"
300+
ruby: "3.3"
301+
- rails: "~> 8.1.0"
302+
ruby: "4.0"
303+
env:
304+
BUNDLE_FROZEN: "false"
305+
RAILS_VERSION: ${{ matrix.rails }}
306+
RIVER_REQUIRE_DATABASES: "1"
307+
services:
308+
postgres:
309+
image: postgres:17
310+
env:
311+
POSTGRES_DB: river_test
312+
POSTGRES_PASSWORD: postgres
313+
options: >-
314+
--health-cmd pg_isready
315+
--health-interval 2s
316+
--health-timeout 5s
317+
--health-retries 5
318+
ports:
319+
- 5432:5432
320+
steps:
321+
- uses: actions/checkout@v4
322+
- uses: ruby/setup-ruby@v1
323+
with:
324+
ruby-version: ${{ matrix.ruby }}
325+
- run: bundle install
326+
working-directory: ./rails/riverqueue-rails
327+
- run: bundle exec rspec
328+
working-directory: ./rails/riverqueue-rails
329+
- run: bundle exec standardrb
330+
working-directory: ./rails/riverqueue-rails

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.DS_Store
22
*.gem
3+
/benchmarks/
34
coverage/
5+
/pro/
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
AllCops:
2+
NewCops: disable
3+
Exclude:
4+
- ".ruby-lsp/**/*"
5+
- "**/coverage/**/*"
6+
7+
Style/FrozenStringLiteralComment:
8+
Enabled: true
9+
EnforcedStyle: always

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Add a full Ruby client for River with Go-compatible job insertion and execution on PostgreSQL and SQLite through ActiveRecord or Sequel. Includes workers, retries, cancellation, periodic and resumable jobs, job administration, migration and worker CLIs, and testing helpers. Rails and Active Job integration is available through `riverqueue-rails`, with workflows, batches, sequences, concurrency controls, and other advanced features in the separately distributed `riverqueue-pro` gem.
13+
1014
## [0.11.0] - 2026-09-02
1115

1216
### Added

‎Gemfile‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
source "https://rubygems.org"
24

35
gemspec
@@ -9,9 +11,12 @@ end
911

1012
group :test do
1113
gem "debug"
14+
gem "fugit", "~> 1.13", require: false
15+
gem "minitest", require: false
1216
gem "pg"
1317
gem "rspec-core"
1418
gem "rspec-expectations"
19+
gem "riverqueue-activerecord", path: "driver/riverqueue-activerecord"
1520
gem "riverqueue-sequel", path: "driver/riverqueue-sequel"
1621
gem "simplecov", require: false
1722
gem "sqlite3"

‎Gemfile.lock‎

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,35 @@ PATH
22
remote: .
33
specs:
44
riverqueue (0.11.0)
5+
logger (> 0, < 1000)
6+
optparse (> 0, < 1000)
7+
securerandom (> 0, < 1000)
8+
timeout (> 0, < 1000)
9+
10+
PATH
11+
remote: driver/riverqueue-activerecord
12+
specs:
13+
riverqueue-activerecord (0.11.0)
14+
activerecord (> 0, < 1000)
15+
activesupport (> 0, < 1000)
16+
riverqueue (= 0.11.0)
517

618
PATH
719
remote: driver/riverqueue-sequel
820
specs:
921
riverqueue-sequel (0.11.0)
22+
riverqueue (= 0.11.0)
1023
sequel (> 0, < 1000)
1124

1225
GEM
1326
remote: https://rubygems.org/
1427
specs:
28+
activemodel (8.1.3)
29+
activesupport (= 8.1.3)
30+
activerecord (8.1.3)
31+
activemodel (= 8.1.3)
32+
activesupport (= 8.1.3)
33+
timeout (>= 0.4.0)
1534
activesupport (8.1.3)
1635
base64
1736
bigdecimal
@@ -39,9 +58,13 @@ GEM
3958
docile (1.4.1)
4059
drb (2.2.3)
4160
erb (6.0.4)
61+
et-orbi (1.4.2)
62+
tzinfo
4263
ffi (1.17.4-arm64-darwin)
43-
ffi (1.17.4-x86_64-linux-gnu)
4464
fileutils (1.8.0)
65+
fugit (1.13.0)
66+
et-orbi (~> 1.4)
67+
raabro (~> 1.4)
4568
i18n (1.14.8)
4669
concurrent-ruby (~> 1.0)
4770
io-console (0.8.2)
@@ -62,19 +85,20 @@ GEM
6285
drb (~> 2.0)
6386
prism (~> 1.5)
6487
mutex_m (0.3.0)
88+
optparse (0.8.1)
6589
parallel (1.27.0)
6690
parser (3.3.11.1)
6791
ast (~> 2.4.1)
6892
racc
6993
pg (1.6.3-arm64-darwin)
70-
pg (1.6.3-x86_64-linux)
7194
pp (0.6.3)
7295
prettyprint
7396
prettyprint (0.2.0)
7497
prism (1.9.0)
7598
psych (5.3.1)
7699
date
77100
stringio
101+
raabro (1.5.0)
78102
racc (1.8.1)
79103
rainbow (3.1.1)
80104
rb-fsevent (0.11.2)
@@ -125,7 +149,6 @@ GEM
125149
simplecov-html (0.13.2)
126150
simplecov_json_formatter (0.1.4)
127151
sqlite3 (2.9.6-arm64-darwin)
128-
sqlite3 (2.9.6-x86_64-linux-gnu)
129152
standard (1.54.0)
130153
language_server-protocol (~> 3.17.0.2)
131154
lint_roller (~> 1.0)
@@ -159,6 +182,7 @@ GEM
159182
strscan (3.1.7)
160183
terminal-table (4.0.0)
161184
unicode-display_width (>= 1.1.1, < 4)
185+
timeout (0.6.1)
162186
tsort (0.2.0)
163187
tzinfo (2.0.6)
164188
concurrent-ruby (~> 1.0)
@@ -169,12 +193,14 @@ GEM
169193

170194
PLATFORMS
171195
arm64-darwin-25
172-
x86_64-linux
173196

174197
DEPENDENCIES
175198
debug
199+
fugit (~> 1.13)
200+
minitest
176201
pg
177202
riverqueue!
203+
riverqueue-activerecord!
178204
riverqueue-sequel!
179205
rspec-core
180206
rspec-expectations
@@ -184,4 +210,4 @@ DEPENDENCIES
184210
steep
185211

186212
BUNDLED WITH
187-
2.6.7
213+
4.0.9

‎Makefile‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
.DEFAULT_GOAL := help
22

3+
RIVER_PATH ?= ../river
4+
35
# Looks at comments using ## on targets and uses them to produce a help output.
46
.PHONY: help
57
help: ALIGN=14
@@ -11,9 +13,11 @@ install: ## Run `bundle install` on gem and all subgems
1113
bundle install
1214
cd driver/riverqueue-activerecord && bundle install
1315
cd driver/riverqueue-sequel && bundle install
16+
cd rails/riverqueue-rails && bundle install
17+
@if [ -d pro/riverqueue-pro ]; then cd pro/riverqueue-pro && bundle install; fi
1418

1519
.PHONY: lint
16-
lint: standardrb ## Run linter (standardrb) on gem and all subgems
20+
lint: standardrb frozen-string-literals ## Run linters on gem and all subgems
1721

1822
.PHONY: rspec
1923
rspec: spec
@@ -23,12 +27,20 @@ spec:
2327
bundle exec rspec
2428
cd driver/riverqueue-activerecord && bundle exec rspec
2529
cd driver/riverqueue-sequel && bundle exec rspec
30+
cd rails/riverqueue-rails && bundle exec rspec
31+
@if [ -d pro/riverqueue-pro ]; then cd pro/riverqueue-pro && bundle exec rspec; fi
2632

2733
.PHONY: standardrb
2834
standardrb:
2935
bundle exec standardrb --fix
3036
cd driver/riverqueue-activerecord && bundle exec standardrb --fix
3137
cd driver/riverqueue-sequel && bundle exec standardrb --fix
38+
cd rails/riverqueue-rails && bundle exec standardrb --fix
39+
@if [ -d pro/riverqueue-pro ]; then cd pro/riverqueue-pro && bundle exec standardrb --fix; fi
40+
41+
.PHONY: frozen-string-literals
42+
frozen-string-literals:
43+
bundle exec rubocop --config .rubocop-frozen-string-literal.yaml --only Style/FrozenStringLiteralComment
3244

3345
.PHONY: steep
3446
steep:
@@ -45,3 +57,9 @@ update: ## Run `bundle update` on gem and all subgems
4557
bundle update
4658
cd driver/riverqueue-activerecord && bundle update
4759
cd driver/riverqueue-sequel && bundle update
60+
cd rails/riverqueue-rails && bundle update
61+
@if [ -d pro/riverqueue-pro ]; then cd pro/riverqueue-pro && bundle update; fi
62+
63+
.PHONY: verify
64+
verify: ## Verify bundled migrations against RIVER_PATH (default ../river)
65+
ruby scripts/sync_migrations.rb --check "$(RIVER_PATH)"

‎Steepfile‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
# frozen_string_literal: true
2+
13
D = Steep::Diagnostic
24

35
target :lib do
46
check "lib"
57

68
library "digest"
79
library "json"
10+
library "logger"
11+
library "optparse"
12+
library "securerandom"
13+
library "socket"
814
library "time"
15+
library "timeout"
916

1017
signature "sig"
1118

0 commit comments

Comments
 (0)