Class: Tina4::Response
- Inherits:
-
Object
- Object
- Tina4::Response
- Defined in:
- lib/tina4/response.rb
Constant Summary collapse
- MIME_TYPES =
{ ".html" => "text/html", ".htm" => "text/html", ".css" => "text/css", ".js" => "application/javascript", ".json" => "application/json", ".xml" => "application/xml", ".txt" => "text/plain", ".csv" => "text/csv", ".png" => "image/png", ".jpg" => "image/jpeg", ".jpeg" => "image/jpeg", ".gif" => "image/gif", ".svg" => "image/svg+xml", ".ico" => "image/x-icon", ".webp" => "image/webp", ".pdf" => "application/pdf", ".zip" => "application/zip", ".woff" => "font/woff", ".woff2" => "font/woff2", ".ttf" => "font/ttf", ".eot" => "application/vnd.ms-fontobject", ".mp3" => "audio/mpeg", ".mp4" => "video/mp4", ".webm" => "video/webm" }.freeze
- JSON_CONTENT_TYPE =
Pre-frozen header values
"application/json; charset=utf-8"- HTML_CONTENT_TYPE =
"text/html; charset=utf-8"- TEXT_CONTENT_TYPE =
"text/plain; charset=utf-8"- XML_CONTENT_TYPE =
"application/xml; charset=utf-8"
Instance Attribute Summary collapse
-
#body ⇒ Object
Returns the value of attribute body.
-
#cookies ⇒ Object
Returns the value of attribute cookies.
-
#headers ⇒ Object
Returns the value of attribute headers.
-
#status_code ⇒ Object
Returns the value of attribute status_code.
Class Method Summary collapse
- .auto_detect(result, response) ⇒ Object
-
.error_envelope(code, message, status = 400) ⇒ Object
Build a standard error envelope hash (class method).
Instance Method Summary collapse
- #add_cors_headers(origin: "*", methods: "GET, POST, PUT, PATCH, DELETE, OPTIONS", headers_list: "Content-Type, Authorization, Accept", credentials: false) ⇒ Object
- #add_header(key, value) ⇒ Object
-
#call(data = nil, status_code = 200, content_type = nil) ⇒ Object
Callable response — auto-detects content type from data.
-
#cookie(name, value, opts = {}) ⇒ Object
Chainable cookie setter.
- #csv(content, filename: "export.csv", status: 200) ⇒ Object
- #delete_cookie(name, path: "/") ⇒ Object
-
#error(code, message, status_code = 400) ⇒ Object
Standard error response envelope.
- #file(path, content_type: nil, download: false) ⇒ Object
-
#header(name, value = nil) ⇒ Object
Chainable header setter.
- #html(content, status_or_opts = nil, status: nil) ⇒ Object
-
#initialize ⇒ Response
constructor
A new instance of Response.
- #json(data, status_or_opts = nil, status: nil) ⇒ Object
- #redirect(url, status_or_opts = nil, status: nil) ⇒ Object
- #render(template_path, data = {}, status: 200, template_dir: nil) ⇒ Object
-
#send ⇒ Object
Flush / finalize – alias for to_rack for semantic clarity.
- #set_cookie(name, value, opts = {}) ⇒ Object
-
#status(code = nil) ⇒ Object
Chainable status setter.
-
#stream(content_type: "text/event-stream") {|Enumerator::Yielder| ... } ⇒ self
Stream response from a block for Server-Sent Events (SSE).
- #text(content, status_or_opts = nil, status: nil) ⇒ Object
- #to_rack ⇒ Object
- #xml(content, status: 200) ⇒ Object
Constructor Details
#initialize ⇒ Response
Returns a new instance of Response.
31 32 33 34 35 36 |
# File 'lib/tina4/response.rb', line 31 def initialize @status_code = 200 @headers = { "content-type" => HTML_CONTENT_TYPE } @body = "" @cookies = nil # Lazy -- most responses have no cookies end |
Instance Attribute Details
#body ⇒ Object
Returns the value of attribute body.
29 30 31 |
# File 'lib/tina4/response.rb', line 29 def body @body end |
#cookies ⇒ Object
Returns the value of attribute cookies.
29 30 31 |
# File 'lib/tina4/response.rb', line 29 def @cookies end |
#headers ⇒ Object
Returns the value of attribute headers.
29 30 31 |
# File 'lib/tina4/response.rb', line 29 def headers @headers end |
#status_code ⇒ Object
Returns the value of attribute status_code.
29 30 31 |
# File 'lib/tina4/response.rb', line 29 def status_code @status_code end |
Class Method Details
.auto_detect(result, response) ⇒ Object
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/tina4/response.rb', line 255 def self.auto_detect(result, response) case result when Tina4::Response result when Hash, Array response.json(result) when String if result.start_with?("<") response.html(result) else response.text(result) end when Integer response.status_code = result response.body = "" response when NilClass response.status_code = 204 response.body = "" response else response.json(result.respond_to?(:to_hash) ? result.to_hash : { data: result.to_s }) end end |
.error_envelope(code, message, status = 400) ⇒ Object
157 158 159 |
# File 'lib/tina4/response.rb', line 157 def self.error_envelope(code, , status = 400) { error: true, code: code, message: , status: status } end |
Instance Method Details
#add_cors_headers(origin: "*", methods: "GET, POST, PUT, PATCH, DELETE, OPTIONS", headers_list: "Content-Type, Authorization, Accept", credentials: false) ⇒ Object
198 199 200 201 202 203 204 205 206 |
# File 'lib/tina4/response.rb', line 198 def add_cors_headers(origin: "*", methods: "GET, POST, PUT, PATCH, DELETE, OPTIONS", headers_list: "Content-Type, Authorization, Accept", credentials: false) @headers["access-control-allow-origin"] = origin @headers["access-control-allow-methods"] = methods @headers["access-control-allow-headers"] = headers_list @headers["access-control-allow-credentials"] = "true" if credentials @headers["access-control-max-age"] = "86400" self end |
#add_header(key, value) ⇒ Object
193 194 195 196 |
# File 'lib/tina4/response.rb', line 193 def add_header(key, value) @headers[key] = value self end |
#call(data = nil, status_code = 200, content_type = nil) ⇒ Object
Callable response — auto-detects content type from data. Matches Python __call__ / PHP __invoke / Node response() pattern.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/tina4/response.rb', line 50 def call(data = nil, status_code = 200, content_type = nil) @status_code = status_code if content_type @headers["content-type"] = content_type @body = data.to_s elsif data.is_a?(Hash) || data.is_a?(Array) @headers["content-type"] = JSON_CONTENT_TYPE @body = JSON.generate(data) else @headers["content-type"] = HTML_CONTENT_TYPE @body = data.to_s end self end |
#cookie(name, value, opts = {}) ⇒ Object
Chainable cookie setter
172 173 174 |
# File 'lib/tina4/response.rb', line 172 def (name, value, opts = {}) (name, value, opts) end |
#csv(content, filename: "export.csv", status: 200) ⇒ Object
93 94 95 96 97 98 99 |
# File 'lib/tina4/response.rb', line 93 def csv(content, filename: "export.csv", status: 200) @status_code = status @headers["content-type"] = "text/csv" @headers["content-disposition"] = "attachment; filename=\"#{filename}\"" @body = content.to_s self end |
#delete_cookie(name, path: "/") ⇒ Object
189 190 191 |
# File 'lib/tina4/response.rb', line 189 def (name, path: "/") (name, "", max_age: 0, path: path) end |
#error(code, message, status_code = 400) ⇒ Object
Standard error response envelope.
Usage:
response.error("VALIDATION_FAILED", "Email is required", 400)
140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/tina4/response.rb', line 140 def error(code, , status_code = 400) @status_code = status_code @headers["content-type"] = JSON_CONTENT_TYPE @body = JSON.generate({ error: true, code: code, message: , status: status_code }) self end |
#file(path, content_type: nil, download: false) ⇒ Object
108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/tina4/response.rb', line 108 def file(path, content_type: nil, download: false) unless ::File.exist?(path) @status_code = 404 @body = "File not found" return self end ext = ::File.extname(path).downcase @headers["content-type"] = content_type || MIME_TYPES[ext] || "application/octet-stream" if download @headers["content-disposition"] = "attachment; filename=\"#{::File.basename(path)}\"" end @body = ::File.binread(path) self end |
#header(name, value = nil) ⇒ Object
Chainable header setter
162 163 164 165 166 167 168 169 |
# File 'lib/tina4/response.rb', line 162 def header(name, value = nil) if value.nil? @headers[name] else @headers[name] = value self end end |
#html(content, status_or_opts = nil, status: nil) ⇒ Object
72 73 74 75 76 77 |
# File 'lib/tina4/response.rb', line 72 def html(content, status_or_opts = nil, status: nil) @status_code = status || (status_or_opts.is_a?(Integer) ? status_or_opts : 200) @headers["content-type"] = HTML_CONTENT_TYPE @body = content.to_s self end |
#json(data, status_or_opts = nil, status: nil) ⇒ Object
65 66 67 68 69 70 |
# File 'lib/tina4/response.rb', line 65 def json(data, status_or_opts = nil, status: nil) @status_code = status || (status_or_opts.is_a?(Integer) ? status_or_opts : 200) @headers["content-type"] = JSON_CONTENT_TYPE @body = data.is_a?(String) ? data : JSON.generate(data) self end |
#redirect(url, status_or_opts = nil, status: nil) ⇒ Object
101 102 103 104 105 106 |
# File 'lib/tina4/response.rb', line 101 def redirect(url, status_or_opts = nil, status: nil) @status_code = status || (status_or_opts.is_a?(Integer) ? status_or_opts : 302) @headers["location"] = url @body = "" self end |
#render(template_path, data = {}, status: 200, template_dir: nil) ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/tina4/response.rb', line 123 def render(template_path, data = {}, status: 200, template_dir: nil) @status_code = status @headers["content-type"] = HTML_CONTENT_TYPE if template_dir frond = Tina4::Frond.new(template_dir: template_dir) @body = frond.render(template_path, data) else @body = Tina4::Template.render(template_path, data) end self end |
#send ⇒ Object
Flush / finalize – alias for to_rack for semantic clarity
235 236 237 |
# File 'lib/tina4/response.rb', line 235 def send to_rack end |
#set_cookie(name, value, opts = {}) ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/tina4/response.rb', line 176 def (name, value, opts = {}) = "#{name}=#{URI.encode_www_form_component(value)}" += "; Path=#{opts[:path] || '/'}" += "; HttpOnly" if opts.fetch(:http_only, true) += "; Secure" if opts[:secure] += "; SameSite=#{opts[:same_site] || 'Lax'}" += "; Max-Age=#{opts[:max_age]}" if opts[:max_age] += "; Expires=#{opts[:expires].httpdate}" if opts[:expires] @cookies ||= [] @cookies << self end |
#status(code = nil) ⇒ Object
Chainable status setter
39 40 41 42 43 44 45 46 |
# File 'lib/tina4/response.rb', line 39 def status(code = nil) if code.nil? @status_code else @status_code = code self end end |
#stream(content_type: "text/event-stream") {|Enumerator::Yielder| ... } ⇒ self
223 224 225 226 227 228 229 230 231 232 |
# File 'lib/tina4/response.rb', line 223 def stream(content_type: "text/event-stream", &block) @status_code = @status_code || 200 @headers["content-type"] = content_type @headers["cache-control"] = "no-cache" @headers["connection"] = "keep-alive" @headers["x-accel-buffering"] = "no" @_streaming = true @_stream_block = block self end |
#text(content, status_or_opts = nil, status: nil) ⇒ Object
79 80 81 82 83 84 |
# File 'lib/tina4/response.rb', line 79 def text(content, status_or_opts = nil, status: nil) @status_code = status || (status_or_opts.is_a?(Integer) ? status_or_opts : 200) @headers["content-type"] = TEXT_CONTENT_TYPE @body = content.to_s self end |
#to_rack ⇒ Object
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/tina4/response.rb', line 239 def to_rack final_headers = @headers.dup final_headers["set-cookie"] = @cookies.join("\n") if @cookies && !@cookies.empty? if @_streaming # Streaming mode — return an Enumerator as the body body = Enumerator.new do |yielder| @_stream_block.call(yielder) end return [@status_code, final_headers, body] end # Normal buffered response [@status_code, final_headers, [@body.to_s]] end |
#xml(content, status: 200) ⇒ Object
86 87 88 89 90 91 |
# File 'lib/tina4/response.rb', line 86 def xml(content, status: 200) @status_code = status @headers["content-type"] = XML_CONTENT_TYPE @body = content.to_s self end |