Class: Rhales::Middleware::JsonResponder
- Inherits:
-
Object
- Object
- Rhales::Middleware::JsonResponder
- Defined in:
- lib/rhales/middleware/json_responder.rb
Overview
Rack middleware that returns hydration data as JSON when Accept: application/json
When a request has Accept: application/json header, this middleware intercepts the response and returns just the hydration data as JSON instead of rendering the full HTML template.
This enables: - API clients to fetch data from the same endpoints - Testing hydration data without parsing HTML - Development inspection of data flow - Mobile/native clients using the same routes
Instance Method Summary collapse
-
#accepts_json?(env) ⇒ Boolean
private
Check if request accepts JSON.
-
#call(env) ⇒ Array
Process the Rack request.
-
#extract_body(body) ⇒ String
private
Extract response body as string.
-
#extract_hydration_data(html) ⇒ Hash
private
Extract hydration JSON blocks from HTML.
-
#html_response?(headers) ⇒ Boolean
private
Check if response is HTML.
-
#initialize(app, options = {}) ⇒ JsonResponder
constructor
Initialize the middleware.
-
#json_response(data, env) ⇒ Array
private
Build JSON response.
Constructor Details
#initialize(app, options = {}) ⇒ JsonResponder
Initialize the middleware
56 57 58 59 60 |
# File 'lib/rhales/middleware/json_responder.rb', line 56 def initialize(app, = {}) @app = app @enabled = .fetch(:enabled, true) @include_metadata = .fetch(:include_metadata, false) end |
Instance Method Details
#accepts_json?(env) ⇒ Boolean (private)
Check if request accepts JSON
Parses Accept header and checks for application/json. Handles weighted preferences (e.g., “application/json;q=0.9”)
109 110 111 112 113 114 115 116 117 118 |
# File 'lib/rhales/middleware/json_responder.rb', line 109 def accepts_json?(env) accept = env['HTTP_ACCEPT'] return false unless accept # Check if application/json is requested # Handle weighted preferences (e.g., "application/json;q=0.9") accept.split(',').any? do |type| type.strip.start_with?('application/json') end end |
#call(env) ⇒ Array
Process the Rack request
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/rhales/middleware/json_responder.rb', line 66 def call(env) return @app.call(env) unless @enabled return @app.call(env) unless accepts_json?(env) # Get the response from the app status, headers, body = @app.call(env) # Only process successful HTML responses return [status, headers, body] unless status == 200 return [status, headers, body] unless html_response?(headers) # Extract hydration data from HTML html_body = extract_body(body) hydration_data = extract_hydration_data(html_body) # Return empty object if no hydration data found if hydration_data.empty? return json_response({}, env) end # Build response data response_data = if @include_metadata { template: env['rhales.template_name'], data: hydration_data } else # Flatten if single window, or return all windows hydration_data.size == 1 ? hydration_data.values.first : hydration_data end json_response(response_data, env) end |
#extract_body(body) ⇒ String (private)
Extract response body as string
Handles different Rack body types (Array, IO, String)
136 137 138 139 140 141 142 143 144 |
# File 'lib/rhales/middleware/json_responder.rb', line 136 def extract_body(body) if body.respond_to?(:each) body.each.to_a.join elsif body.respond_to?(:read) body.read else body.to_s end end |
#extract_hydration_data(html) ⇒ Hash (private)
Extract hydration JSON blocks from HTML
Looks for
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/rhales/middleware/json_responder.rb', line 153 def extract_hydration_data(html) hydration_blocks = {} # Match script tags with data-window attribute html.scan(/<script[^>]*type=["']application\/json["'][^>]*data-window=["']([^"']+)["'][^>]*>(.*?)<\/script>/m) do |window_var, json_content| begin hydration_blocks[window_var] = JSONSerializer.parse(json_content.strip) rescue JSON::ParserError => e # Skip malformed JSON blocks warn "Rhales::JsonResponder: Failed to parse hydration JSON for window.#{window_var}: #{e.}" end end hydration_blocks end |
#html_response?(headers) ⇒ Boolean (private)
Check if response is HTML
124 125 126 127 128 |
# File 'lib/rhales/middleware/json_responder.rb', line 124 def html_response?(headers) # Support both uppercase and lowercase header names for compatibility content_type = headers['content-type'] || headers['Content-Type'] content_type && content_type.include?('text/html') end |
#json_response(data, env) ⇒ Array (private)
Build JSON response
174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/rhales/middleware/json_responder.rb', line 174 def json_response(data, env) json_body = JSONSerializer.dump(data) [ 200, { 'content-type' => 'application/json', 'content-length' => json_body.bytesize.to_s, 'cache-control' => 'no-cache' }, [json_body] ] end |