Skip to content

Commit aaad360

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. I also have a full Pro implementation locally, but have left it out of this change set. We'd ship this from a private gem server separately.
1 parent f29d1d4 commit aaad360

197 files changed

Lines changed: 20834 additions & 824 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: 134 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,92 @@ env:
2323
on:
2424
- push
2525

26+
permissions:
27+
contents: read
28+
2629
jobs:
30+
conformance:
31+
runs-on: ubuntu-latest
32+
timeout-minutes: 15
33+
strategy:
34+
matrix:
35+
driver: [sequel, activerecord]
36+
env:
37+
RIVER_CONFORMANCE_DATABASE_URL: postgres://postgres:postgres@127.0.0.1:5432/river_conformance?sslmode=disable
38+
RIVER_CONFORMANCE_DRIVER: ${{ matrix.driver }}
39+
services:
40+
postgres:
41+
image: postgres:17
42+
env:
43+
POSTGRES_DB: river_conformance
44+
POSTGRES_PASSWORD: postgres
45+
options: >-
46+
--health-cmd pg_isready
47+
--health-interval 2s
48+
--health-timeout 5s
49+
--health-retries 5
50+
ports:
51+
- 5432:5432
52+
steps:
53+
- uses: actions/checkout@v4
54+
- uses: ruby/setup-ruby@v1
55+
with:
56+
ruby-version: ${{ env.RUBY_VERSION }}
57+
bundler-cache: true
58+
- name: Read conformance reference revision
59+
id: reference
60+
run: |
61+
ruby -rjson -e '
62+
revision = JSON.parse(File.read("conformance/reference.json")).fetch("revision")
63+
abort "Invalid River revision" unless /\A[0-9a-f]{40}\z/.match?(revision)
64+
File.open(ENV.fetch("GITHUB_OUTPUT"), "a") { |file| file.puts("revision=#{revision}") }
65+
'
66+
- uses: actions/checkout@v4
67+
with:
68+
repository: riverqueue/river
69+
ref: ${{ steps.reference.outputs.revision }}
70+
path: upstream/river
71+
persist-credentials: false
72+
- uses: actions/setup-go@v5
73+
with:
74+
go-version-file: upstream/river/go.mod
75+
cache-dependency-path: upstream/river/**/go.sum
76+
- name: Required insert-only conformance
77+
run: make test/conformance/insert-only RIVER_PATH="$GITHUB_WORKSPACE/upstream/river"
78+
79+
verify:
80+
runs-on: ubuntu-latest
81+
timeout-minutes: 3
82+
83+
steps:
84+
- name: Checkout
85+
uses: actions/checkout@v4
86+
87+
- name: Install Ruby
88+
uses: ruby/setup-ruby@v1
89+
with:
90+
ruby-version: ${{ env.RUBY_VERSION }}
91+
92+
- name: Read upstream River revision
93+
id: river_revision
94+
run: |
95+
ruby -rjson -e '
96+
revision = JSON.parse(File.read("migration/manifest.json")).fetch("revision")
97+
abort "Invalid River revision" unless /\A[0-9a-f]{40}\z/.match?(revision)
98+
File.open(ENV.fetch("GITHUB_OUTPUT"), "a") { |file| file.puts("revision=#{revision}") }
99+
'
100+
101+
- name: Checkout upstream River
102+
uses: actions/checkout@v4
103+
with:
104+
path: upstream/river
105+
persist-credentials: false
106+
ref: ${{ steps.river_revision.outputs.revision }}
107+
repository: riverqueue/river
108+
109+
- name: Verify migrations
110+
run: make verify RIVER_PATH="$GITHUB_WORKSPACE/upstream/river"
111+
27112
gem_build:
28113
runs-on: ubuntu-latest
29114
timeout-minutes: 3
@@ -50,6 +135,10 @@ jobs:
50135
run: gem build riverqueue-sequel.gemspec
51136
working-directory: ./driver/riverqueue-sequel
52137

138+
- name: Build gem (riverqueue-rails)
139+
run: gem build riverqueue-rails.gemspec
140+
working-directory: ./rails/riverqueue-rails
141+
53142
lint:
54143
runs-on: ubuntu-latest
55144
timeout-minutes: 3
@@ -68,6 +157,10 @@ jobs:
68157
run: bundle exec standardrb
69158
working-directory: .
70159

160+
- name: Frozen string literal comments
161+
run: bundle exec rubocop --config .rubocop-frozen-string-literal.yaml --only Style/FrozenStringLiteralComment
162+
working-directory: .
163+
71164
- name: bundle install (riverqueue-activerecord)
72165
run: bundle install
73166
working-directory: ./driver/riverqueue-activerecord
@@ -159,23 +252,9 @@ jobs:
159252
ruby-version: ${{ matrix.ruby_version }}
160253
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
161254

162-
# Needed for River's CLI. There is a version of Go on Actions' base image,
163-
# but it's old and can't read modern `go.mod` annotations correctly.
164-
- name: Install Go
165-
uses: actions/setup-go@v5
166-
with:
167-
go-version: "stable"
168-
check-latest: true
169-
170255
- name: Create database
171256
run: psql --echo-errors --quiet -c '\timing off' -c "CREATE DATABASE ${TEST_DATABASE_NAME};" ${ADMIN_DATABASE_URL}
172257

173-
- name: Install River CLI
174-
run: go install github.com/riverqueue/river/cmd/river@latest
175-
176-
- name: river migrate-up
177-
run: river migrate-up --database-url "$TEST_DATABASE_URL"
178-
179258
- name: Rspec (riverqueue-ruby)
180259
run: bundle exec rspec
181260
working-directory: .
@@ -195,3 +274,44 @@ jobs:
195274
- name: Rspec (riverqueue-sequel)
196275
run: bundle exec rspec
197276
working-directory: ./driver/riverqueue-sequel
277+
278+
rails:
279+
runs-on: ubuntu-latest
280+
timeout-minutes: 5
281+
strategy:
282+
matrix:
283+
include:
284+
- rails: "~> 7.2.0"
285+
ruby: "3.2"
286+
- rails: "~> 8.0.0"
287+
ruby: "3.3"
288+
- rails: "~> 8.1.0"
289+
ruby: "4.0"
290+
env:
291+
BUNDLE_FROZEN: "false"
292+
RAILS_VERSION: ${{ matrix.rails }}
293+
RIVER_REQUIRE_DATABASES: "1"
294+
services:
295+
postgres:
296+
image: postgres:17
297+
env:
298+
POSTGRES_DB: river_test
299+
POSTGRES_PASSWORD: postgres
300+
options: >-
301+
--health-cmd pg_isready
302+
--health-interval 2s
303+
--health-timeout 5s
304+
--health-retries 5
305+
ports:
306+
- 5432:5432
307+
steps:
308+
- uses: actions/checkout@v4
309+
- uses: ruby/setup-ruby@v1
310+
with:
311+
ruby-version: ${{ matrix.ruby }}
312+
- run: bundle install
313+
working-directory: ./rails/riverqueue-rails
314+
- run: bundle exec rspec
315+
working-directory: ./rails/riverqueue-rails
316+
- run: bundle exec standardrb
317+
working-directory: ./rails/riverqueue-rails
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
AllCops:
2+
NewCops: disable
3+
Exclude:
4+
- ".ruby-lsp/**/*"
5+
- "**/coverage/**/*"
6+
- "**/vendor/**/*"
7+
8+
Style/FrozenStringLiteralComment:
9+
Enabled: true
10+
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, SQLite, and YugabyteDB through ActiveRecord or Sequel. Includes workers, retries, cancellation, periodic and resumable jobs, job-persisted logging, 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. [PR #70](https://github.com/riverqueue/riverqueue-ruby/pull/70).
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 & 1 deletion
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,14 @@ 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)
4364
ffi (1.17.4-x86_64-linux-gnu)
4465
fileutils (1.8.0)
66+
fugit (1.13.0)
67+
et-orbi (~> 1.4)
68+
raabro (~> 1.4)
4569
i18n (1.14.8)
4670
concurrent-ruby (~> 1.0)
4771
io-console (0.8.2)
@@ -62,6 +86,7 @@ GEM
6286
drb (~> 2.0)
6387
prism (~> 1.5)
6488
mutex_m (0.3.0)
89+
optparse (0.8.1)
6590
parallel (1.27.0)
6691
parser (3.3.11.1)
6792
ast (~> 2.4.1)
@@ -75,6 +100,7 @@ GEM
75100
psych (5.3.1)
76101
date
77102
stringio
103+
raabro (1.5.0)
78104
racc (1.8.1)
79105
rainbow (3.1.1)
80106
rb-fsevent (0.11.2)
@@ -159,6 +185,7 @@ GEM
159185
strscan (3.1.7)
160186
terminal-table (4.0.0)
161187
unicode-display_width (>= 1.1.1, < 4)
188+
timeout (0.6.1)
162189
tsort (0.2.0)
163190
tzinfo (2.0.6)
164191
concurrent-ruby (~> 1.0)
@@ -173,8 +200,11 @@ PLATFORMS
173200

174201
DEPENDENCIES
175202
debug
203+
fugit (~> 1.13)
204+
minitest
176205
pg
177206
riverqueue!
207+
riverqueue-activerecord!
178208
riverqueue-sequel!
179209
rspec-core
180210
rspec-expectations
@@ -184,4 +214,4 @@ DEPENDENCIES
184214
steep
185215

186216
BUNDLED WITH
187-
2.6.7
217+
4.0.9

0 commit comments

Comments
 (0)