Class: Rhales::Hydrator

Inherits:
Object
  • Object
show all
Defined in:
lib/rhales/hydration/hydrator.rb

Overview

Data Hydrator for RSFC client-side data injection

RSFC Security Model: Server-to-Client Security Boundary

The Hydrator enforces a critical security boundary between server and client:

Server Side (Template Rendering)

  • Templates have FULL server context access (like ERB/HAML)
  • Can access user objects, database connections, internal APIs
  • Can access secrets, configuration, authentication state
  • Can process sensitive business logic

Client Side (Data Hydration)

  • Only data declared in sections reaches the browser
  • Creates explicit allowlist like designing a REST API
  • For : Direct props serialization (no interpolation)
  • JSON serialization validates data structure

Process Flow (Schema-based, preferred)

  1. Backend provides fully-resolved props to render call
  2. Props are directly serialized as JSON
  3. Client receives only the declared props

Example (Schema-based)

```rue

const schema = z.object({ user_name: z.string(), theme: z.string() });

``` Backend: render(‘template’, user_name: user.name, theme: user.theme_preference)

Server template can access {user{user.admin?} and {internal_config}, but client only gets the declared user_name and theme values.

This creates an API-like boundary where data is serialized once and parsed once, enforcing the same security model as REST endpoints.

Note: With the new two-pass architecture, the Hydrator’s role is greatly simplified. All data merging happens server-side in the HydrationDataAggregator, so this class only handles JSON generation for individual templates (used during the aggregation phase).

Defined Under Namespace

Classes: HydrationError, JSONSerializationError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parser, context) ⇒ Hydrator

Returns a new instance of Hydrator.



58
59
60
61
62
# File 'lib/rhales/hydration/hydrator.rb', line 58

def initialize(parser, context)
  @parser           = parser
  @context          = context
  @window_attribute = parser.window_attribute || 'data'
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



56
57
58
# File 'lib/rhales/hydration/hydrator.rb', line 56

def context
  @context
end

#parserObject (readonly)

Returns the value of attribute parser.



56
57
58
# File 'lib/rhales/hydration/hydrator.rb', line 56

def parser
  @parser
end

#window_attributeObject (readonly)

Returns the value of attribute window_attribute.



56
57
58
# File 'lib/rhales/hydration/hydrator.rb', line 56

def window_attribute
  @window_attribute
end

Class Method Details

.generate_data_hash(parser, context) ⇒ Object

Generate data hash (for internal processing)



95
96
97
# File 'lib/rhales/hydration/hydrator.rb', line 95

def generate_data_hash(parser, context)
  new(parser, context).processed_data_hash
end

.generate_json(parser, context) ⇒ Object

Generate only JSON data (for testing or API endpoints)



90
91
92
# File 'lib/rhales/hydration/hydrator.rb', line 90

def generate_json(parser, context)
  new(parser, context).process_data_section
end

Instance Method Details

#process_data_sectionObject

Process section and return JSON string



65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rhales/hydration/hydrator.rb', line 65

def process_data_section
  # Check for schema section
  if @parser.schema_lang
    # Schema section: Direct props serialization
    JSONSerializer.dump(@context.client)
  else
    # No hydration section
    '{}'
  end
rescue JSON::ParserError => ex
  raise JSONSerializationError, "Invalid JSON in schema section: #{ex.message}"
end

#processed_data_hashObject

Get processed data as Ruby hash (for internal use)



79
80
81
82
83
84
# File 'lib/rhales/hydration/hydrator.rb', line 79

def processed_data_hash
  json_string = process_data_section
  JSONSerializer.parse(json_string)
rescue JSON::ParserError => ex
  raise JSONSerializationError, "Cannot parse processed data as JSON: #{ex.message}"
end