Module: Sinatra::Inertia::Helpers
- Defined in:
- lib/sinatra/inertia/helpers.rb
Overview
Sinatra helpers exposed to route handlers. Mounted by the ‘Sinatra::Inertia` extension.
Instance Method Summary collapse
-
#csrf_token ⇒ Object
CSRF token for the current request.
-
#current_inertia_shared ⇒ Object
—————————————————————— Shared props — runtime accessors (the ‘inertia_share` class DSL is in extension.rb, this is the per-request resolver).
-
#current_inertia_version ⇒ Object
—————————————————————— Asset version.
-
#inertia(component, props: {}, layout: nil) ⇒ Object
Render an Inertia response.
- #inertia_clear_history! ⇒ Object
- #inertia_encrypt_history!(flag = true) ⇒ Object
-
#inertia_errors(payload = nil) ⇒ Object
—————————————————————— Errors / flash session sweep (per Inertia validation pattern).
- #inertia_errors_payload ⇒ Object
- #inertia_request? ⇒ Boolean
-
#render(*args, **kwargs, &block) ⇒ Object
Rails-flavored alias: ‘render inertia: ’Component’, props: …‘ We must preserve Sinatra’s ‘render(engine, data = nil, options = {}, locals = {}, &block)` signature for the non-inertia path, so we forward *args/**kwargs.
- #sweep_inertia_session! ⇒ Object
Instance Method Details
#csrf_token ⇒ Object
CSRF token for the current request. Mounted by CSRFMiddleware (‘set :inertia_csrf_protection, true` by default). Pair this with `inertia_share { { csrfToken: csrf_token } }` so the React/Vue client picks it up automatically — but note that when `Sinatra::Inertia::CSRFMiddleware` is active, the cookie + header exchange is already handled by the Inertia client; this helper is mainly for hidden-field forms or non-XHR submissions.
100 101 102 |
# File 'lib/sinatra/inertia/helpers.rb', line 100 def csrf_token request.env['sinatra.inertia.csrf_token'] end |
#current_inertia_shared ⇒ Object
Shared props — runtime accessors (the ‘inertia_share` class DSL is in extension.rb, this is the per-request resolver).
107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/sinatra/inertia/helpers.rb', line 107 def current_inertia_shared blocks = settings.inertia_share_blocks || [] merged = {} blocks.each do |b| v = instance_exec(&b) if v.is_a?(Hash) merged = deep_merge(merged, v) end end merged end |
#current_inertia_version ⇒ Object
Asset version
121 122 123 124 |
# File 'lib/sinatra/inertia/helpers.rb', line 121 def current_inertia_version v = settings.respond_to?(:inertia_version) ? settings.inertia_version : nil v.respond_to?(:call) ? v.call.to_s : v.to_s end |
#inertia(component, props: {}, layout: nil) ⇒ Object
Render an Inertia response.
inertia 'Todos/Index', props: { todos: -> { Todo.all } }
Layout selection: the configured layout (default ‘:layout`) is rendered for full HTML responses. The view receives `@page_json` (an HTML-escaped JSON string ready to drop into a `data-page` attribute) and `@page` (the underlying Hash, useful for SSR or custom rendering).
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/sinatra/inertia/helpers.rb', line 20 def inertia(component, props: {}, layout: nil) layout = settings.respond_to?(:inertia_layout) ? settings.inertia_layout : :layout if layout.nil? version = current_inertia_version shared = current_inertia_shared encrypt = if !@inertia_encrypt_history_override.nil? @inertia_encrypt_history_override == true elsif settings.respond_to?(:inertia_encrypt_history) settings.inertia_encrypt_history == true else false end clear = @inertia_clear_history == true # Set the protocol response headers BEFORE we touch any async # `to_h` resolution. Under Opal, `Response#to_h` is an `async` # function (it `await`s any Proc-returned JS Promise), so the # rest of this method runs after a JS-level suspend. Setting # `content_type` / `X-Inertia` before the suspend guarantees # Sinatra's dispatch sees them when it finalises the response, # regardless of how the underlying runtime schedules the # awaited continuation. if inertia_request? content_type 'application/json; charset=utf-8' headers 'X-Inertia' => 'true', 'Vary' => 'X-Inertia' end # Read errors *before* sweeping so the response carries them, then # sweep immediately so the next request sees a clean slate. The # sweep must happen before any further session writes that the # framework might serialise on commit. errors_payload = inertia_errors_payload sweep_inertia_session! response_obj = Sinatra::Inertia::Response.new( component: component, props: props, request: request, version: version, url: request.fullpath, encrypt_history: encrypt, clear_history: clear, shared: shared, errors: errors_payload ) page_hash = response_obj.to_h page_json = page_hash.to_json return page_json if inertia_request? @page = page_hash @page_json = ::Rack::Utils.escape_html(page_json) erb layout, layout: false end |
#inertia_clear_history! ⇒ Object
140 141 142 |
# File 'lib/sinatra/inertia/helpers.rb', line 140 def inertia_clear_history! @inertia_clear_history = true end |
#inertia_encrypt_history!(flag = true) ⇒ Object
144 145 146 |
# File 'lib/sinatra/inertia/helpers.rb', line 144 def inertia_encrypt_history!(flag = true) @inertia_encrypt_history_override = flag end |
#inertia_errors(payload = nil) ⇒ Object
Errors / flash session sweep (per Inertia validation pattern). Consumers call ‘inertia_errors(field: ’message’)‘ before redirecting to a form route; the next request renders the form with errors and sweeps them out of the session.
131 132 133 134 135 136 137 138 |
# File 'lib/sinatra/inertia/helpers.rb', line 131 def inertia_errors(payload = nil) if payload.nil? (session[:_inertia_errors] || {}).dup else session[:_inertia_errors] = payload payload end end |
#inertia_errors_payload ⇒ Object
148 149 150 151 152 153 |
# File 'lib/sinatra/inertia/helpers.rb', line 148 def inertia_errors_payload errors = session[:_inertia_errors] return nil if errors.nil? return nil if errors.respond_to?(:empty?) && errors.empty? errors end |
#inertia_request? ⇒ Boolean
89 90 91 |
# File 'lib/sinatra/inertia/helpers.rb', line 89 def inertia_request? request.env['HTTP_X_INERTIA'] == 'true' end |
#render(*args, **kwargs, &block) ⇒ Object
Rails-flavored alias: ‘render inertia: ’Component’, props: …‘ We must preserve Sinatra’s ‘render(engine, data = nil, options = {}, locals = {}, &block)` signature for the non-inertia path, so we forward *args/**kwargs.
78 79 80 81 82 83 84 85 86 87 |
# File 'lib/sinatra/inertia/helpers.rb', line 78 def render(*args, **kwargs, &block) first = args.first if args.length == 1 && first.is_a?(Hash) && first.key?(:inertia) inertia(first[:inertia], props: first[:props] || {}, layout: first[:layout]) elsif kwargs.key?(:inertia) && args.empty? inertia(kwargs[:inertia], props: kwargs[:props] || {}, layout: kwargs[:layout]) else super(*args, **kwargs, &block) end end |
#sweep_inertia_session! ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/sinatra/inertia/helpers.rb', line 155 def sweep_inertia_session! # Rack::Session::Cookie tracks writes by hash mutation. On some # session backends (e.g. the JSON-coder cookie store homura uses # under Cloudflare Workers) `delete` is a no-op for the *backing # cookie* — the change isn't serialised back. Force a write by # assigning nil instead, which the JSON encoder still emits as # `null` and makes `inertia_errors_payload` treat the field as # absent on the next visit. if session.respond_to?(:[]=) session[:_inertia_errors] = nil end end |