Module: Funicular::SSR

Defined in:
lib/funicular/ssr.rb,
lib/funicular/ssr/runtime.rb

Overview

Server-side rendering entry point.

Usage (typically from a Rails controller / view helper):

result = Funicular::SSR.render(
  path: request.path,
  state: { channels: Channel.all.as_json }
)
# result[:html]  -> HTML string for the #app container
# result[:state] -> data to embed as window.__FUNICULAR_STATE__

Defined Under Namespace

Modules: Runtime

Class Method Summary collapse

Class Method Details

.default_source_dirObject



39
40
41
42
# File 'lib/funicular/ssr.rb', line 39

def self.default_source_dir
  raise "source_dir is required outside Rails" unless defined?(Rails) && Rails.respond_to?(:root)
  Rails.root.join("app", "funicular")
end

.render(path:, state: {}, props: {}, source_dir: nil) ⇒ Object

Render the component mapped to ‘path` to an HTML string, seeding it with server-provided `state`. Returns a hash:

{ html:, state:, component: }

When no route matches, html is “” so the caller can fall back to plain client-side rendering (empty #app container).



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/funicular/ssr.rb', line 23

def self.render(path:, state: {}, props: {}, source_dir: nil)
  Runtime.boot!(source_dir || default_source_dir)

  router = Funicular.router
  raise "Funicular router is not configured; check app/funicular/initializer.rb" unless router

  component_class, params = router.match(path)
  return { html: "", state: {}, component: nil } unless component_class

  instance = component_class.new(symbolize_keys(params).merge(props))
  instance.seed_state(state)
  html = Funicular::VDOM::HTMLSerializer.serialize(instance.build_vdom)

  { html: html, state: state, component: component_class }
end

.symbolize_keys(hash) ⇒ Object



44
45
46
47
48
49
# File 'lib/funicular/ssr.rb', line 44

def self.symbolize_keys(hash)
  return {} unless hash
  out = {}
  hash.each { |k, v| out[k.to_sym] = v }
  out
end