Module: TurboCable::Broadcastable

Extended by:
ActiveSupport::Concern
Defined in:
lib/turbo_cable/broadcastable.rb

Overview

Provides Turbo Streams broadcasting methods for ActiveRecord models Drop-in replacement for Turbo::Streams::Broadcastable

Hybrid async/sync approach:

  • _later_to methods: Use Active Job if available, otherwise synchronous
  • Non-_later_to methods: Always synchronous

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.broadcast_json(stream_name, data) ⇒ Object

Module-level method for broadcasting custom JSON data Useful for progress updates, job status, or non-Turbo-Stream messages



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/turbo_cable/broadcastable.rb', line 16

def self.broadcast_json(stream_name, data)
  # Get the actual Puma/Rails server port
  # Priority: TURBO_CABLE_PORT > PORT > 3000
  # Use TURBO_CABLE_PORT to override PORT when it's set to proxy/Thruster port
  default_port = ENV["TURBO_CABLE_PORT"] || ENV["PORT"] || "3000"
  uri = URI(ENV.fetch("TURBO_CABLE_BROADCAST_URL", "http://localhost:#{default_port}/_broadcast"))

  Rails.logger.debug "[TurboCable] Broadcasting JSON to stream: #{stream_name} via #{uri}" if defined?(Rails)

  http = Net::HTTP.new(uri.host, uri.port)
  http.open_timeout = 1
  http.read_timeout = 1

  request = Net::HTTP::Post.new(uri.path, "Content-Type" => "application/json")
  request.body = {
    stream: stream_name,
    data: data
  }.to_json

  response = http.request(request)
  Rails.logger.debug "[TurboCable] Broadcast response: #{response.code}" if defined?(Rails)
  response
rescue => e
  Rails.logger.error("[TurboCable] JSON broadcast failed: #{e.class} - #{e.message}") if defined?(Rails)
end

Instance Method Details

#broadcast_append_later_to(stream_name, **options) ⇒ Object



59
60
61
62
63
64
65
# File 'lib/turbo_cable/broadcastable.rb', line 59

def broadcast_append_later_to(stream_name, **options)
  if async_broadcast_available?
    enqueue_broadcast_job(stream_name, action: :append, **options)
  else
    broadcast_action_now(stream_name, action: :append, **options)
  end
end

#broadcast_append_to(stream_name, **options) ⇒ Object



84
85
86
# File 'lib/turbo_cable/broadcastable.rb', line 84

def broadcast_append_to(stream_name, **options)
  broadcast_action_now(stream_name, action: :append, **options)
end

#broadcast_prepend_later_to(stream_name, **options) ⇒ Object



67
68
69
70
71
72
73
# File 'lib/turbo_cable/broadcastable.rb', line 67

def broadcast_prepend_later_to(stream_name, **options)
  if async_broadcast_available?
    enqueue_broadcast_job(stream_name, action: :prepend, **options)
  else
    broadcast_action_now(stream_name, action: :prepend, **options)
  end
end

#broadcast_prepend_to(stream_name, **options) ⇒ Object



88
89
90
# File 'lib/turbo_cable/broadcastable.rb', line 88

def broadcast_prepend_to(stream_name, **options)
  broadcast_action_now(stream_name, action: :prepend, **options)
end

#broadcast_remove_to(stream_name, target:) ⇒ Object



92
93
94
95
96
97
98
99
# File 'lib/turbo_cable/broadcastable.rb', line 92

def broadcast_remove_to(stream_name, target:)
  turbo_stream_html = <<~HTML
    <turbo-stream action="remove" target="#{target}">
    </turbo-stream>
  HTML

  broadcast_turbo_stream(stream_name, turbo_stream_html)
end

#broadcast_replace_later_to(stream_name, **options) ⇒ Object

Async broadcast methods (truly async if Active Job is configured)



43
44
45
46
47
48
49
# File 'lib/turbo_cable/broadcastable.rb', line 43

def broadcast_replace_later_to(stream_name, **options)
  if async_broadcast_available?
    enqueue_broadcast_job(stream_name, action: :replace, **options)
  else
    broadcast_action_now(stream_name, action: :replace, **options)
  end
end

#broadcast_replace_to(stream_name, **options) ⇒ Object

Synchronous broadcast methods (always immediate)



76
77
78
# File 'lib/turbo_cable/broadcastable.rb', line 76

def broadcast_replace_to(stream_name, **options)
  broadcast_action_now(stream_name, action: :replace, **options)
end

#broadcast_update_later_to(stream_name, **options) ⇒ Object



51
52
53
54
55
56
57
# File 'lib/turbo_cable/broadcastable.rb', line 51

def broadcast_update_later_to(stream_name, **options)
  if async_broadcast_available?
    enqueue_broadcast_job(stream_name, action: :update, **options)
  else
    broadcast_action_now(stream_name, action: :update, **options)
  end
end

#broadcast_update_to(stream_name, **options) ⇒ Object



80
81
82
# File 'lib/turbo_cable/broadcastable.rb', line 80

def broadcast_update_to(stream_name, **options)
  broadcast_action_now(stream_name, action: :update, **options)
end