Module: ExpoTurbo::Rails::Streams

Defined in:
lib/expo_turbo/rails.rb,
lib/expo_turbo/rails/streams.rb,
lib/expo_turbo/rails/streams/helper.rb,
lib/expo_turbo/rails/streams/tag_builder.rb,
lib/expo_turbo/rails/streams/broadcast_job.rb

Defined Under Namespace

Modules: Helper Classes: BroadcastJob, TagBuilder

Constant Summary collapse

STREAM_NAMESPACE =
:expo

Class Method Summary collapse

Class Method Details

.broadcast_later_to(*streamables, content:) ⇒ Object



13
14
15
# File 'lib/expo_turbo/rails/streams.rb', line 13

def broadcast_later_to(*streamables, content:)
  BroadcastJob.perform_later(stream_name_for(*streamables), content: valid_content!(content))
end

.broadcast_refresh_later_to(*streamables, request_id: ::Turbo.current_request_id, **attributes) ⇒ Object



24
25
26
27
28
29
30
31
# File 'lib/expo_turbo/rails/streams.rb', line 24

def broadcast_refresh_later_to(*streamables, request_id: ::Turbo.current_request_id, **attributes)
  stream_name = stream_name_for(*streamables)
  content = refresh_content(request_id:, **attributes)

  refresh_debouncer_for(stream_name, request_id).debounce do
    BroadcastJob.perform_later(stream_name, content: content)
  end
end

.broadcast_refresh_to(*streamables, request_id: ::Turbo.current_request_id, **attributes) ⇒ Object



17
18
19
20
21
22
# File 'lib/expo_turbo/rails/streams.rb', line 17

def broadcast_refresh_to(*streamables, request_id: ::Turbo.current_request_id, **attributes)
  broadcast_to_stream(
    stream_name_for(*streamables),
    content: refresh_content(request_id:, **attributes)
  )
end

.broadcast_to(*streamables, content:) ⇒ Object



9
10
11
# File 'lib/expo_turbo/rails/streams.rb', line 9

def broadcast_to(*streamables, content:)
  broadcast_to_stream(stream_name_for(*streamables), content: content)
end

.broadcast_to_stream(stream_name, content:) ⇒ Object



33
34
35
# File 'lib/expo_turbo/rails/streams.rb', line 33

def broadcast_to_stream(stream_name, content:)
  ::Turbo::StreamsChannel.broadcast_stream_to(valid_stream_name!(stream_name), content: valid_content!(content))
end

.stream_name_for(*streamables) ⇒ Object



44
45
46
47
48
49
# File 'lib/expo_turbo/rails/streams.rb', line 44

def stream_name_for(*streamables)
  stream_name = streamables_for(*streamables)
    .map { |streamable| streamable.try(:to_gid_param) || streamable.to_param }
    .join(":")
  valid_stream_name!(stream_name)
end

.streamables_for(*streamables) ⇒ Object

Raises:

  • (ArgumentError)


37
38
39
40
41
42
# File 'lib/expo_turbo/rails/streams.rb', line 37

def streamables_for(*streamables)
  normalized = streamables.flatten.compact_blank
  raise ArgumentError, "streamables must include a nonblank value" if normalized.empty?

  [*normalized, STREAM_NAMESPACE]
end

.valid_content!(content) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/expo_turbo/rails/streams.rb', line 59

def valid_content!(content)
  raise ArgumentError, "content must be a nonblank String" unless content.is_a?(String)
  raise TemplateError, "Expo Turbo Stream broadcasts must render valid UTF-8" unless content.encoding == Encoding::UTF_8 && content.valid_encoding?
  raise ArgumentError, "content must be a nonblank String" unless content.present?

  XmlFragments.parse_stream_fragment(content)

  content
rescue XmlFragments::ParseError
  raise TemplateError, "Expo Turbo Stream broadcasts must contain well-formed XML Stream fragments"
end

.valid_stream_name?(stream_name) ⇒ Boolean

Returns:

  • (Boolean)


51
52
53
54
55
56
57
# File 'lib/expo_turbo/rails/streams.rb', line 51

def valid_stream_name?(stream_name)
  stream_name.is_a?(String) &&
    stream_name.encoding == Encoding::UTF_8 &&
    stream_name.valid_encoding? &&
    stream_name.present? &&
    stream_name.end_with?(":#{STREAM_NAMESPACE}")
end