Class: MaquinaStream::Renderer::Fence

Inherits:
Object
  • Object
show all
Defined in:
lib/maquina_stream/renderer/fence.rb

Overview

One fenced code block, and the decision about what to do with it.

The strategy comes from the fence registry; the fence's own state — open or closed — decides how much of that strategy actually runs. An open fence is never highlighted and never emits a client payload: highlighting would be thrown away on the next frame, and a payload would hand the client half a diagram to draw.

Which strategy a fence gets is the host's, through MaquinaStream.register_fence. This class is what reads that registration and applies it.

Constant Summary collapse

DEFAULT_STRATEGY =

What an unregistered language gets: Rouge highlighting once it closes.

:server
STRATEGIES =

The three strategies, documented on Registries#register_fence. A registration naming anything else falls back to DEFAULT_STRATEGY rather than raising — a typo in an initializer should not take a whole message down.

%i[server client passthrough].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(info:, source:, open:, config:) ⇒ Fence

Builds a fence. The render pipeline does this, once per fenced block per frame.



42
43
44
45
46
47
# File 'lib/maquina_stream/renderer/fence.rb', line 42

def initialize(info:, source:, open:, config:)
  @info = info.to_s
  @source = source
  @open = open
  @config = config
end

Instance Attribute Details

#config ⇒ Object (readonly)

The Configuration this fence reads.



38
39
40
# File 'lib/maquina_stream/renderer/fence.rb', line 38

def config
  @config
end

#info ⇒ Object (readonly)

The fence's whole info string, "ruby" or "mermaid graph".



29
30
31
# File 'lib/maquina_stream/renderer/fence.rb', line 29

def info
  @info
end

#open ⇒ Object (readonly)

Whether the fence is still unterminated.



35
36
37
# File 'lib/maquina_stream/renderer/fence.rb', line 35

def open
  @open
end

#source ⇒ Object (readonly)

The fence's body, as written.



32
33
34
# File 'lib/maquina_stream/renderer/fence.rb', line 32

def source
  @source
end

Instance Method Details

#closed? ⇒ Boolean

Whether the fence has closed, and may therefore be highlighted or emit a payload.

Returns:

  • (Boolean)


55
# File 'lib/maquina_stream/renderer/fence.rb', line 55

def closed? = !open?

#controller ⇒ Object

The Stimulus identifier a :client fence hands its payload to, from the registration's controller:. Nil for any other strategy.



76
77
78
# File 'lib/maquina_stream/renderer/fence.rb', line 76

def controller
  registration&.options&.fetch(:controller, nil)
end

#highlighted ⇒ Object

Highlighted HTML, or nil when this fence should not be highlighted at all: an open fence, a passthrough language, or a language Rouge does not know.



100
101
102
103
104
105
106
107
# File 'lib/maquina_stream/renderer/fence.rb', line 100

def highlighted
  return nil unless strategy == :server && closed?

  lexer = Rouge::Lexer.find(language)
  return nil unless lexer

  formatter.format(lexer.lex(source))
end

#language ⇒ Object

The first word of the info string — what the registry is keyed on.



58
59
60
# File 'lib/maquina_stream/renderer/fence.rb', line 58

def language
  info.split(/\s+/).first.to_s
end

#open? ⇒ Boolean

Whether the fence is still unterminated. An open fence is never highlighted and never emits a payload.

Returns:

  • (Boolean)


51
# File 'lib/maquina_stream/renderer/fence.rb', line 51

def open? = @open

#payload ⇒ Object

The Hash a :client fence hands its controller, or nil.

Built by the registration's payload: callable, and {source:, info:} when it has none. It is serialized to JSON into a data attribute, so the callable must return something JSON-representable.

The payload exists only once the fence has closed. Until then there is nothing here for the client to render, by design.



88
89
90
91
92
93
94
95
# File 'lib/maquina_stream/renderer/fence.rb', line 88

def payload
  return nil unless strategy == :client && closed?

  builder = registration&.options&.fetch(:payload, nil)
  return {source: source, info: info} unless builder.respond_to?(:call)

  builder.call(source, info)
end

#registration ⇒ Object

This language's registration, or nil when nobody registered it.



63
64
65
# File 'lib/maquina_stream/renderer/fence.rb', line 63

def registration
  MaquinaStream.fences[language]
end

#strategy ⇒ Object

The strategy in force for this fence: one of STRATEGIES, and DEFAULT_STRATEGY for anything unregistered or misregistered.



69
70
71
72
# File 'lib/maquina_stream/renderer/fence.rb', line 69

def strategy
  candidate = registration&.options&.fetch(:strategy, nil) || DEFAULT_STRATEGY
  STRATEGIES.include?(candidate) ? candidate : DEFAULT_STRATEGY
end