Class: Rhales::HydrationDataAggregator
- Inherits:
-
Object
- Object
- Rhales::HydrationDataAggregator
- Defined in:
- lib/rhales/hydration_data_aggregator.rb
Overview
HydrationDataAggregator traverses the ViewComposition and executes all sections to produce a single, merged JSON structure.
This class implements the server-side data aggregation phase of the two-pass rendering model, handling: - Traversal of the template dependency tree - Execution of sections with full server context - Merge strategies (deep, shallow, strict) - Collision detection and error reporting
The aggregator replaces the HydrationRegistry by performing all data merging in a single, coordinated pass.
Defined Under Namespace
Classes: JSONSerializationError
Instance Method Summary collapse
-
#aggregate(composition) ⇒ Object
Aggregate all hydration data from the view composition.
-
#build_template_path(parser) ⇒ Object
private
-
#deep_merge(target, source) ⇒ Object
private
-
#empty_data?(data) ⇒ Boolean
private
Check if data is considered empty for collision detection.
-
#initialize(context) ⇒ HydrationDataAggregator
constructor
A new instance of HydrationDataAggregator.
-
#merge_data(target, source, strategy, window_attr, template_path) ⇒ Object
private
-
#process_data_section(data_content, parser) ⇒ Object
private
-
#process_template(_template_name, parser) ⇒ Object
private
-
#shallow_merge(target, source, window_attr, template_path) ⇒ Object
private
-
#strict_merge(target, source, window_attr, template_path) ⇒ Object
private
Constructor Details
#initialize(context) ⇒ HydrationDataAggregator
Returns a new instance of HydrationDataAggregator.
23 24 25 26 27 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 23 def initialize(context) @context = context @window_attributes = {} @merged_data = {} end |
Instance Method Details
#aggregate(composition) ⇒ Object
Aggregate all hydration data from the view composition
30 31 32 33 34 35 36 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 30 def aggregate(composition) composition.each_document_in_render_order do |template_name, parser| process_template(template_name, parser) end @merged_data end |
#build_template_path(parser) ⇒ Object (private)
161 162 163 164 165 166 167 168 169 170 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 161 def build_template_path(parser) data_node = parser.section_node('data') line_number = data_node ? data_node.location.start_line : 1 if parser.file_path "#{parser.file_path}:#{line_number}" else "<inline>:#{line_number}" end end |
#deep_merge(target, source) ⇒ Object (private)
115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 115 def deep_merge(target, source) result = target.dup source.each do |key, value| result[key] = if result.key?(key) && result[key].is_a?(Hash) && value.is_a?(Hash) deep_merge(result[key], value) else value end end result end |
#empty_data?(data) ⇒ Boolean (private)
Check if data is considered empty for collision detection
173 174 175 176 177 178 179 180 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 173 def empty_data?(data) return true if data.nil? return true if data == {} return true if data == [] return true if data.respond_to?(:empty?) && data.empty? false end |
#merge_data(target, source, strategy, window_attr, template_path) ⇒ Object (private)
102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 102 def merge_data(target, source, strategy, window_attr, template_path) case strategy when 'deep' deep_merge(target, source) when 'shallow' shallow_merge(target, source, window_attr, template_path) when 'strict' strict_merge(target, source, window_attr, template_path) else raise ArgumentError, "Unknown merge strategy: #{strategy}" end end |
#process_data_section(data_content, parser) ⇒ Object (private)
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 84 def process_data_section(data_content, parser) # Create a JSON-aware context wrapper for data sections json_context = JsonAwareContext.new(@context) # Process template variables in the data section processed_content = TemplateEngine.render(data_content, json_context) # Parse as JSON begin JSON.parse(processed_content) rescue JSON::ParserError => ex template_path = build_template_path(parser) raise JSONSerializationError, "Invalid JSON in data section at #{template_path}: #{ex.}\n" \ "Processed content: #{processed_content[0..200]}..." end end |
#process_template(_template_name, parser) ⇒ Object (private)
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 74 75 76 77 78 79 80 81 82 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 40 def process_template(_template_name, parser) data_content = parser.section('data') return unless data_content window_attr = parser.window_attribute || 'data' merge_strategy = parser.merge_strategy # Build template path for error reporting template_path = build_template_path(parser) # Process the data section first to check if it's empty processed_data = process_data_section(data_content, parser) # Check for collisions only if the data is not empty if @window_attributes.key?(window_attr) && merge_strategy.nil? && !empty_data?(processed_data) existing = @window_attributes[window_attr] existing_data = @merged_data[window_attr] # Only raise collision error if existing data is also not empty unless empty_data?(existing_data) raise ::Rhales::HydrationCollisionError.new(window_attr, existing[:path], template_path) end end # Merge or set the data @merged_data[window_attr] = if @merged_data.key?(window_attr) merge_data( @merged_data[window_attr], processed_data, merge_strategy || 'deep', window_attr, template_path, ) else processed_data end # Track the window attribute @window_attributes[window_attr] = { path: template_path, merge_strategy: merge_strategy, } end |
#shallow_merge(target, source, window_attr, template_path) ⇒ Object (private)
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 129 def shallow_merge(target, source, window_attr, template_path) result = target.dup source.each do |key, value| if result.key?(key) raise ::Rhales::HydrationCollisionError.new( "#{window_attr}.#{key}", @window_attributes[window_attr][:path], template_path, ) end result[key] = value end result end |
#strict_merge(target, source, window_attr, template_path) ⇒ Object (private)
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/rhales/hydration_data_aggregator.rb', line 146 def strict_merge(target, source, window_attr, template_path) # In strict mode, any collision is an error target.each_key do |key| next unless source.key?(key) raise ::Rhales::HydrationCollisionError.new( "#{window_attr}.#{key}", @window_attributes[window_attr][:path], template_path, ) end target.merge(source) end |