{"id":49400423,"url":"https://github.com/nemuba/broadcast_hub","last_synced_at":"2026-04-28T17:36:05.333Z","repository":{"id":346683473,"uuid":"1190193074","full_name":"nemuba/broadcast_hub","owner":"nemuba","description":"BroadcastHub is a reusable Action Cable broadcasting layer for Rails 5/6 apps that use server-rendered HTML and Sprockets.","archived":false,"fork":false,"pushed_at":"2026-04-03T19:12:30.000Z","size":5309,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T21:35:32.006Z","etag":null,"topics":["actioncable","jquery","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nemuba.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-24T03:41:14.000Z","updated_at":"2026-04-03T19:12:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nemuba/broadcast_hub","commit_stats":null,"previous_names":["nemuba/broadcast_hub"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/nemuba/broadcast_hub","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nemuba%2Fbroadcast_hub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nemuba%2Fbroadcast_hub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nemuba%2Fbroadcast_hub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nemuba%2Fbroadcast_hub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nemuba","download_url":"https://codeload.github.com/nemuba/broadcast_hub/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nemuba%2Fbroadcast_hub/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32392301,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T14:34:11.604Z","status":"ssl_error","status_checked_at":"2026-04-28T14:32:37.009Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actioncable","jquery","rails","ruby"],"created_at":"2026-04-28T17:36:04.535Z","updated_at":"2026-04-28T17:36:05.326Z","avatar_url":"https://github.com/nemuba.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BroadcastHub\n\nBroadcastHub is a reusable Action Cable broadcasting layer for Rails 5/6 apps that use server-rendered HTML and Sprockets. It replaces model-level Turbo stream helpers with an explicit payload contract sent over `BroadcastHub::StreamChannel`.\n\n## 1) What BroadcastHub is\n\n- Rails engine (`broadcast_hub`) scoped to Rails `\u003e= 5.2`, `\u003c 7.0`\n- Server concern (`BroadcastHub::Broadcaster`) for model callbacks and payload publishing\n- Generic Action Cable channel (`BroadcastHub::StreamChannel`) with authorization and stream key resolution\n- Browser helpers (`BroadcastHub.Subscription` and `BroadcastHub.JQueryController`) for applying append/prepend/update/remove/dispatch actions\n\nBroadcastHub is designed to work without `turbo-rails`.\n\n## 2) Installation in host app\n\nAdd the engine gem to the host app `Gemfile`:\n\n```ruby\ngem 'broadcast_hub', '~\u003e 0.2.1'\n```\n\nInstall dependencies, then generate the initializer template:\n\n```bash\nbundle install\nbin/rails generate broadcast_hub:install\n```\n\nThis creates `config/initializers/broadcast_hub.rb`.\n\n## 3) Initializer configuration\n\nMinimum required settings:\n\n- `allowed_resources`: allowlist of resource keys clients can subscribe to\n- `authorize_scope`: lambda that decides if the Action Cable connection can subscribe\n- `stream_key_resolver`: lambda that maps subscription context to a stream name used by both channel + model broadcaster\n\nAuthenticated example:\n\n```ruby\nBroadcastHub.configure do |config|\n  config.allowed_resources = %w[todo]\n\n  config.authorize_scope = lambda do |context|\n    context.current_user.present?\n  end\n\n  config.stream_key_resolver = lambda do |context|\n    \"resource:#{context.resource_name}:user:#{context.current_user.id}\"\n  end\nend\n```\n\nNo-auth/session example:\n\n```ruby\nBroadcastHub.configure do |config|\n  config.allowed_resources = %w[todo]\n\n  config.authorize_scope = lambda do |context|\n    context.session_id.present?\n  end\n\n  config.stream_key_resolver = lambda do |context|\n    \"resource:#{context.resource_name}:session:#{context.session_id}\"\n  end\nend\n```\n\nIf your Action Cable connection does not expose `current_user`, expose a safe identifier (for example `session_id`) in `ApplicationCable::Connection`.\n\n## 4) Model integration\n\nInclude the concern and declare broadcast settings in each model:\n\n```ruby\nclass Todo \u003c ApplicationRecord\n  include BroadcastHub::Broadcaster\n\n  broadcast_to :todo, partial: 'todos/partials/todo', target: '#todos'\nend\n```\n\n`broadcast_to` wires callbacks:\n\n- `after_create_commit` -\u003e append\n- `after_update_commit` -\u003e update\n- `after_destroy_commit` -\u003e remove\n\nOptional context hook for stream-key alignment (recommended when keys depend on tenant/user/session):\n\n```ruby\ndef broadcast_hub_stream_key_context_attributes\n  {\n    tenant_id: nil,\n    current_user: user,\n    session_id: nil,\n    params: {}\n  }\nend\n```\n\n## 5) Controller helper integration\n\nFor controller-triggered realtime updates (for example action-specific highlight/flash events), use `render_broadcast`.\n\nBroadcastHub also exposes `dom_id(record, positional_prefix = nil, prefix: nil, suffix: nil)` in both controllers and views.\n\n- `dom_id(todo)` -\u003e `todo_1`\n- `dom_id(todo, :edit)` -\u003e `edit_todo_1`\n- `dom_id(todo, prefix: 'row')` -\u003e `row_todo_1`\n- `dom_id(todo, suffix: 'highlight')` -\u003e `todo_1_highlight`\n- `dom_id(todo, prefix: 'row', suffix: 'highlight')` -\u003e `row_todo_1_highlight`\n\nIf both positional prefix and keyword `prefix` are provided, `dom_id` raises `ArgumentError`.\n\n```ruby\nclass TodosController \u003c ApplicationController\n  def highlight\n    respond_to do |format|\n      format.js { broadcast_todo_highlight }\n      format.json { broadcast_todo_highlight }\n    end\n  end\n\n  private\n\n  def broadcast_todo_highlight\n    render_broadcast(\n      action: 'dispatch',\n      target: \"##{dom_id(current_user.todos.find(params[:id]), prefix: 'row', suffix: 'flash')}\",\n      resource: 'todo',\n      event_name: 'todo:highlight',\n      event_data: { id: params[:id] }\n    )\n  end\nend\n```\n\nView usage example:\n\n```erb\n\u003ctr id=\"\u003c%= dom_id(@todo, prefix: 'row', suffix: 'flash') %\u003e\"\u003e\n  \u003ctd\u003e\u003c%= @todo.title %\u003e\u003c/td\u003e\n\u003c/tr\u003e\n```\n\n`render_broadcast` options:\n\n- Required: `action`, `target`, `resource`\n- For `append|prepend|update`: `partial` is required\n- For `remove|dispatch`: `content` is forced to `nil`\n- For `dispatch`: `event_name` is required and `event_data` must be a hash when provided\n- `id` defaults to a generated UUID when omitted\n- Stream authorization/identity is resolved through `BroadcastHub::StreamKeyResolver.resolve!` using `BroadcastHub::StreamKeyContext`\n\n## 6) Client-side integration (Sprockets)\n\nRequire BroadcastHub in `app/assets/javascripts/application.js`:\n\n```js\n//= require broadcast_hub/index\n```\n\nBasic subscription wiring (compatible with this repo style):\n\n```js\n(function (global) {\n  function wireTodoChannel(consumer, $) {\n    var controller = new BroadcastHubJQueryController($);\n    var subscription = new BroadcastHubSubscription(consumer, controller);\n\n    return subscription.subscribe('todo');\n  }\n\n  if (global.App \u0026\u0026 global.App.cable \u0026\u0026 global.jQuery) {\n    global.App.todo_channel = wireTodoChannel(global.App.cable, global.jQuery);\n  }\n})(this);\n```\n\n`BroadcastHubSubscription` sends `{ channel: 'BroadcastHub::StreamChannel', resource: 'todo' }` and the controller applies incoming payloads to the DOM.\n\n## 7) Payload contract\n\nPayloads emitted by `BroadcastHub::PayloadBuilder` follow this contract:\n\n```json\n{\n  \"version\": 1,\n  \"action\": \"append\",\n  \"target\": \"#todos\",\n  \"content\": \"\u003cdiv id=\\\"todo_1\\\"\u003e...\u003c/div\u003e\",\n  \"id\": \"todo_1\",\n  \"meta\": {}\n}\n```\n\nDispatch actions extend this payload with event fields:\n\n```json\n{\n  \"version\": 1,\n  \"action\": \"dispatch\",\n  \"target\": \"#todos\",\n  \"content\": null,\n  \"id\": \"todo_1\",\n  \"meta\": {},\n  \"event_name\": \"todo:highlight\",\n  \"event_data\": { \"id\": \"todo_1\" }\n}\n```\n\nField meaning:\n\n- `action`: one of `append`, `prepend`, `update`, `replace`, `remove`, `dispatch`\n- `target`: CSS selector used as insertion/update/remove/dispatch target\n- `content`: rendered HTML for append/prepend/update/replace (typically `null` on remove/dispatch)\n- `id`: DOM id used by update/remove fast-path replacement\n- `meta`: optional metadata hash (defaults to `{}`)\n- `event_name`: required when `action` is `dispatch`; event name passed to jQuery `trigger`\n- `event_data`: optional hash payload for `dispatch`; delivered as trigger argument data\n- `version`: payload contract version from `BroadcastHub.configuration.payload_version`\n\nDispatch-specific notes:\n\n- `event_name` and `event_data` are included only when `action` is `dispatch`\n- `event_data` must be a hash when provided\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnemuba%2Fbroadcast_hub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnemuba%2Fbroadcast_hub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnemuba%2Fbroadcast_hub/lists"}