Skip to content

Rubycent v4#28

Merged
FZambia merged 5 commits intomasterfrom
rubycent_v4
Apr 25, 2026
Merged

Rubycent v4#28
FZambia merged 5 commits intomasterfrom
rubycent_v4

Conversation

@FZambia
Copy link
Copy Markdown
Member

@FZambia FZambia commented Apr 24, 2026

Release v4.0.0

v4 of this gem targets the current Centrifugo HTTP API (tested against Centrifugo v6) and drops the legacy POST /api JSON-RPC-style protocol the gem used through v3.

Highlights

  • Modern Centrifugo HTTP API (per-method endpoints, X-API-Key header).
  • New client methods: subscribe, refresh, history_remove, batch.
  • Richer arguments on existing methods (publish/broadcast accept tags, skip_history, idempotency_key, delta, version, version_epoch, b64data; history accepts limit, since, reverse; channels accepts pattern; disconnect accepts client/session/whitelist/custom disconnect).
  • Typed error hierarchy — Cent::TimeoutError, Cent::NetworkError, Cent::TransportError, Cent::UnauthorizedError, Cent::DecodeError.
  • Expanded Cent::Notary covering every standard and Centrifugo-specific JWT claim.
  • Supports Faraday 2, JWT 2 and 3, tested against all four combinations.
  • Requires Ruby 3.0+.
  • Integration test suite exercising a real Centrifugo via Docker.

Changes

Transport

  • Now sends requests to per-method paths: POST /api/publish, POST /api/broadcast, and so on — instead of a single POST /api endpoint with {"method": "...", "params": {...}} body.
  • Sends the API key as X-API-Key: <key> per current Centrifugo docs (previously Authorization: apikey <key>).
  • Cent::Client.new gained a timeout: keyword (default 10 seconds) that sets both open and read timeouts on the Faraday connection. The initializer still yields the Faraday::Connection for further customization.

Cent::Client — new methods

  • subscribe(user:, channel:, ...) — server-side subscribe a user session to a channel.
  • refresh(user:, ...) — refresh a connection (useful for unidirectional transports).
  • history_remove(channel:) — remove all publications from a channel's history.
  • batch(commands:, parallel: nil) — send many commands in one HTTP request; inspect each reply individually for errors. (The batch response is shaped { "replies" => [...] } with no top-level result wrapper.)

Cent::Client — expanded arguments

  • publish(channel:, data:, skip_history: nil, tags: nil, b64data: nil, idempotency_key: nil, delta: nil, version: nil, version_epoch: nil)
  • broadcast(channels:, data:, skip_history: nil, tags: nil, b64data: nil, idempotency_key: nil, delta: nil, version: nil, version_epoch: nil)
  • disconnect(user:, client: nil, session: nil, whitelist: nil, disconnect: nil)
  • unsubscribe(user:, channel:, client: nil, session: nil)
  • history(channel:, limit: nil, since: nil, reverse: nil)
  • channels(pattern: nil)

Any keyword passed as nil is omitted from the request body.

Cent::Client — return values and error handling

On success every method returns the parsed Centrifugo response body — typically { "result" => { ... } }. If Centrifugo rejects the request with a top-level error, Cent::ResponseError is raised (same class, same code / message attributes as v3) — your existing rescue Cent::ResponseError blocks keep working.

Transport-level problems (network failure, timeout, non-2xx HTTP, bad JSON) raise the new typed errors described below.

batch and broadcast — per-reply errors are not raised

batch and broadcast return an array of independent sub-replies; a single partial failure shouldn't short-circuit the whole response, so their per-reply errors are not turned into exceptions.

  • batch response is { "replies" => [...] } — note there is no top-level result wrapper. Each entry is either { "<method>" => <result> } or { "error" => {...} }.
  • broadcast response has { "result" => { "responses" => [...] } }. Each entry in responses is either { "result" => {...} } or { "error" => {...} }.

Walk the array to check for per-entry errors. Cent::ResponseError is still raised for these calls if Centrifugo rejects the whole request (e.g. malformed top-level body).

response = client.batch(commands: [...])
response['replies'].each_with_index do |reply, i|
  warn "command #{i} failed: #{reply['error']['message']}" if reply['error']
end

Errors

The error hierarchy has been expanded:

  • Cent::Error (base, StandardError)
    • Cent::ResponseError — Centrifugo returned a top-level API error. Exposes #code and #message. Unchanged from v3.
    • Cent::TimeoutError — request timed out.
    • Cent::NetworkError — could not reach Centrifugo.
    • Cent::TransportError — non-2xx HTTP status. Exposes #status.
      • Cent::UnauthorizedError — HTTP 401 (bad API key).
    • Cent::DecodeError — response body was not valid JSON.

All new errors subclass Cent::Error, so a broad rescue Cent::Error still catches everything.

Cent::Notary

Both token methods now support every standard and Centrifugo-specific JWT claim:

  • issue_connection_token(sub:, exp: nil, iat: nil, jti: nil, aud: nil, iss: nil, info: nil, b64info: nil, channels: nil, subs: nil, meta: nil, expire_at: nil)
  • issue_channel_token(sub:, channel:, exp: nil, iat: nil, jti: nil, aud: nil, iss: nil, info: nil, b64info: nil, override: nil, expire_at: nil)

The client: keyword that used to be on issue_channel_token is replaced by the standard sub: claim (this was already done in v3 via #24 and is carried forward unchanged).

Dependencies

  • Ruby: >= 3.0 (previously >= 2.5).

CI and testing

  • GitHub Actions matrix covers the cross product of Faraday 2/3 × JWT 2/3 on Ruby 3.4, plus a ruby-head experimental job tracking Ruby 4.
  • Appraisal-based gemfiles live in gemfiles/ for reproducing any matrix cell locally (bundle exec appraisal rspec).
  • Integration test suite at spec/integration/ runs against a real Centrifugo instance. Unit tests continue to use WebMock. docker-compose.yml at the repo root spins up Centrifugo v6.7.1 with history, presence, and the GRPC API enabled for local development.

Migration from v3

  1. Upgrade your Centrifugo server to v4 or newer. v4 of this gem speaks the current API and no longer works with pre-v4 Centrifugo.

  2. rescue Cent::ResponseError keeps working. No change needed. The class, its #code, and its #message are preserved from v3.

  3. Consider switching to more specific transport-error rescues. If you previously rescued Faraday::ClientError or a broad Cent::Error, you can now target specific classes: Cent::TimeoutError, Cent::NetworkError, Cent::UnauthorizedError, Cent::TransportError, Cent::DecodeError. The broad rescue Cent::Error still catches all of them.

  4. If you use batch or broadcast, inspect the reply arrays. Per-reply errors in response["replies"] (batch) or response["result"]["responses"] (broadcast) are returned as hash entries with an "error" key — they are not raised. See the README for examples.

  5. Update Cent::Notary#issue_channel_token calls if you are coming from v2.x: the client: keyword is now sub:.

    # Before (v2.x and earlier)
    notary.issue_channel_token(client: 'abc', channel: 'c')
    
    # After (v3+)
    notary.issue_channel_token(sub: '42', channel: 'c')
  6. Bump your Ruby to 3.0 or newer if you are on 2.x. Ruby 2.7 has been end-of-life since March 2023.

  7. No changes required for existing happy-path calls. client.publish(channel:, data:), broadcast(channels:, data:), presence, presence_stats, history(channel:), channels, info, disconnect(user:), unsubscribe(channel:, user:) all keep their v3 signatures (with optional new kwargs). The response shape ({ "result" => ... }) is unchanged on success.

Thanks

Thanks to @alfonsouc (#27) and @trushkevich (#24) for the groundwork that landed ahead of this release.

@FZambia FZambia merged commit 44e04a1 into master Apr 25, 2026
20 checks passed
@FZambia FZambia deleted the rubycent_v4 branch April 25, 2026 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant