Class: Rhales::View
- Inherits:
-
Object
- Object
- Rhales::View
- Defined in:
- lib/rhales/view.rb
Overview
Complete RSFC view implementation
Single public interface for RSFC template rendering that handles:
-
Context creation (with pluggable context classes)
-
Template loading and parsing
-
Template rendering with Rhales
-
Data hydration and injection
## Context and Data Boundaries
Views implement a two-phase security model:
### Server Templates: Full Context Access Templates have complete access to all server-side data:
-
All props passed to View.new
-
Data from .rue file’s <data> section (processed server-side)
-
Runtime data (CSRF tokens, nonces, request metadata)
-
Computed data (authentication status, theme classes)
-
User objects, configuration, internal APIs
### Client Data: Explicit Allowlist Only data declared in <data> sections reahas_role?ches the browser:
-
Creates a REST API-like boundary
-
Server-side variable interpolation processes secrets safely
-
JSON serialization validates data structure
-
No accidental exposure of sensitive server data
Example:
# Server template has full access:
{{user.admin?}} {{csrf_token}} {{internal_config}}
# Client only gets declared data:
{ "user_name": "{{user.name}}", "theme": "{{user.theme}}" }
See docs/CONTEXT_AND_DATA_BOUNDARIES.md for complete details.
Subclasses can override context_class to use different context implementations.
Defined Under Namespace
Classes: RenderError, TemplateNotFoundError
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#cust ⇒ Object
readonly
Returns the value of attribute cust.
-
#locale ⇒ Object
readonly
Returns the value of attribute locale.
-
#props ⇒ Object
readonly
Returns the value of attribute props.
-
#req ⇒ Object
readonly
Returns the value of attribute req.
-
#rsfc_context ⇒ Object
readonly
Returns the value of attribute rsfc_context.
-
#sess ⇒ Object
readonly
Returns the value of attribute sess.
Class Method Summary collapse
-
.default_template_name ⇒ Object
Get default template name based on class name.
-
.render_with_data(req, sess, cust, locale, template_name: nil, config: nil, **props) ⇒ Object
Render template with props.
-
.with_data(req, sess, cust, locale, config: nil, **props) ⇒ Object
Create view instance with props.
Instance Method Summary collapse
-
#data_hash(template_name = nil) ⇒ Object
Get processed data as hash (for API endpoints or testing).
-
#initialize(req, sess = nil, cust = nil, locale_override = nil, props: {}, config: nil) ⇒ View
constructor
A new instance of View.
-
#render(template_name = nil) ⇒ Object
Render RSFC template with hydration using two-pass architecture.
-
#render_hydration_only(template_name = nil) ⇒ Object
Generate only the data hydration HTML.
-
#render_template_only(template_name = nil) ⇒ Object
Render only the template section (without data hydration).
Constructor Details
#initialize(req, sess = nil, cust = nil, locale_override = nil, props: {}, config: nil) ⇒ View
Returns a new instance of View.
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/rhales/view.rb', line 59 def initialize(req, sess = nil, cust = nil, locale_override = nil, props: {}, config: nil) @req = req @sess = sess @cust = cust @locale = locale_override @props = props @config = config || Rhales.configuration # Create context using the specified context class @rsfc_context = create_context end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
57 58 59 |
# File 'lib/rhales/view.rb', line 57 def config @config end |
#cust ⇒ Object (readonly)
Returns the value of attribute cust.
57 58 59 |
# File 'lib/rhales/view.rb', line 57 def cust @cust end |
#locale ⇒ Object (readonly)
Returns the value of attribute locale.
57 58 59 |
# File 'lib/rhales/view.rb', line 57 def locale @locale end |
#props ⇒ Object (readonly)
Returns the value of attribute props.
57 58 59 |
# File 'lib/rhales/view.rb', line 57 def props @props end |
#req ⇒ Object (readonly)
Returns the value of attribute req.
57 58 59 |
# File 'lib/rhales/view.rb', line 57 def req @req end |
#rsfc_context ⇒ Object (readonly)
Returns the value of attribute rsfc_context.
57 58 59 |
# File 'lib/rhales/view.rb', line 57 def rsfc_context @rsfc_context end |
#sess ⇒ Object (readonly)
Returns the value of attribute sess.
57 58 59 |
# File 'lib/rhales/view.rb', line 57 def sess @sess end |
Class Method Details
.default_template_name ⇒ Object
Get default template name based on class name
391 392 393 394 395 396 397 398 |
# File 'lib/rhales/view.rb', line 391 def default_template_name # Convert ClassName to class_name name.split('::').last .gsub(/([A-Z])/, '_\1') .downcase .sub(/^_/, '') .sub(/_view$/, '') end |
.render_with_data(req, sess, cust, locale, template_name: nil, config: nil, **props) ⇒ Object
Render template with props
401 402 403 404 |
# File 'lib/rhales/view.rb', line 401 def render_with_data(req, sess, cust, locale, template_name: nil, config: nil, **props) view = new(req, sess, cust, locale, props: props, config: config) view.render(template_name) end |
.with_data(req, sess, cust, locale, config: nil, **props) ⇒ Object
Create view instance with props
407 408 409 |
# File 'lib/rhales/view.rb', line 407 def with_data(req, sess, cust, locale, config: nil, **props) new(req, sess, cust, locale, props: props, config: config) end |
Instance Method Details
#data_hash(template_name = nil) ⇒ Object
Get processed data as hash (for API endpoints or testing)
119 120 121 122 123 124 125 126 |
# File 'lib/rhales/view.rb', line 119 def data_hash(template_name = nil) template_name ||= self.class.default_template_name # Build composition and aggregate data composition = build_view_composition(template_name) aggregator = HydrationDataAggregator.new(@rsfc_context) aggregator.aggregate(composition) end |
#render(template_name = nil) ⇒ Object
Render RSFC template with hydration using two-pass architecture
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/rhales/view.rb', line 72 def render(template_name = nil) template_name ||= self.class.default_template_name # Phase 1: Build view composition and aggregate data composition = build_view_composition(template_name) aggregator = HydrationDataAggregator.new(@rsfc_context) merged_hydration_data = aggregator.aggregate(composition) # Phase 2: Render HTML with pre-computed data # Render template content template_html = render_template_with_composition(composition, template_name) # Generate hydration HTML with merged data hydration_html = generate_hydration_from_merged_data(merged_hydration_data) # Set CSP header if enabled set_csp_header_if_enabled # Combine template and hydration inject_hydration_into_template(template_html, hydration_html) rescue StandardError => ex raise RenderError, "Failed to render template '#{template_name}': #{ex.}" end |
#render_hydration_only(template_name = nil) ⇒ Object
Generate only the data hydration HTML
106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/rhales/view.rb', line 106 def render_hydration_only(template_name = nil) template_name ||= self.class.default_template_name # Build composition and aggregate data composition = build_view_composition(template_name) aggregator = HydrationDataAggregator.new(@rsfc_context) merged_hydration_data = aggregator.aggregate(composition) # Generate hydration HTML generate_hydration_from_merged_data(merged_hydration_data) end |
#render_template_only(template_name = nil) ⇒ Object
Render only the template section (without data hydration)
97 98 99 100 101 102 103 |
# File 'lib/rhales/view.rb', line 97 def render_template_only(template_name = nil) template_name ||= self.class.default_template_name # Build composition for consistent behavior composition = build_view_composition(template_name) render_template_with_composition(composition, template_name) end |