Class: Rooibos::Command::Http
- Inherits:
-
Object
- Object
- Rooibos::Command::Http
- Includes:
- Custom
- Defined in:
- lib/rooibos/command/http.rb
Overview
Performs HTTP requests and sends the response as a message.
Applications fetch data from APIs. Users expect responsive interfaces while requests complete. Managing HTTP connections, timeouts, and threading manually is error-prone.
This command executes HTTP requests off the main thread. The runtime dispatches it and routes the response back to your update function as a Message::HttpResponse.
Use it to fetch API data, post forms, or interact with web services.
Prefer the Command.http factory method for convenience. The constructor supports flexible DWIM (Do What I Mean) arity.
Example
# Using the factory method (recommended)
Command.http(:get, "/api/users", :users)
Command.http(get: "/api/users", envelope: :users)
Command.http(:post, "/api/users", '{"name":"Jo"}', :created)
# Using the class directly
Http.new(:get, "/api/users", :users)
# Pattern-match on the response
def update(, model)
case
in { type: :http, envelope: :users, status: 200, body: }
model.with(users: JSON.parse(body))
in { type: :http, envelope: :users, error: }
model.with(error:)
end
end
Class Method Summary collapse
-
.new(*args, method: nil, url: nil, envelope: nil, headers: nil, body: nil, timeout: nil, parser: nil, get: nil, post: nil, put: nil, patch: nil, delete: nil) ⇒ Object
Creates an HTTP request command.
Instance Method Summary collapse
-
#call(out, token) ⇒ Object
Executes the HTTP request and sends the response.
-
#rooibos_cancellation_grace_period ⇒ Object
Net::HTTP is blocking; no cooperative cancellation possible.
Methods included from Custom
#deconstruct_keys, #rooibos_command?
Class Method Details
.new(*args, method: nil, url: nil, envelope: nil, headers: nil, body: nil, timeout: nil, parser: nil, get: nil, post: nil, put: nil, patch: nil, delete: nil) ⇒ Object
Creates an HTTP request command.
Supports flexible DWIM arity for convenience:
Http.new("url")-
GET, URL as envelope
Http.new("url", :tag)-
GET, custom envelope
Http.new(:post, "url")-
POST, URL as envelope
Http.new(:post, "url", :tag)-
POST, custom envelope
Http.new(:post, "url", "body", :tag)-
POST with body
Http.new(get: "url")-
keyword shortcut
- method
-
HTTP method symbol:
:get,:post,:put,:patch, or:delete. - url
-
Request URL (String).
- envelope
-
Symbol to tag the response message.
- headers
-
Optional hash of HTTP headers.
- body
-
Optional request body (String).
- timeout
-
Optional timeout in seconds (default 10).
- parser
-
Optional callable to transform response body.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/rooibos/command/http.rb', line 75 def new(*args, method: nil, url: nil, envelope: nil, headers: nil, body: nil, timeout: nil, parser: nil, get: nil, post: nil, put: nil, patch: nil, delete: nil ) # Auto-splat single hash argument return new(**args.first) if args.size == 1 && args.first.is_a?(Hash) # Auto-spread single array argument return new(*args.first) if args.size == 1 && args.first.is_a?(Array) # DWIM: parse positional args and keyword method shortcuts method_keywords = { get:, post:, put:, patch:, delete: }.compact method, url, envelope, body = parse_dwim_args(args, method, url, envelope, body, method_keywords) # Ractor validation if RatatuiRuby::Debug.enabled? && !Ractor.shareable?(url) raise Rooibos::Error::Invariant, "URL is not Ractor-shareable: #{url.inspect}\n" \ "Use a frozen string or Ractor.make_shareable." end if RatatuiRuby::Debug.enabled? && headers && !Ractor.shareable?(headers) raise Rooibos::Error::Invariant, "Headers are not Ractor-shareable: #{headers.inspect}\n" \ "Use Ractor.make_shareable or freeze the hash and its contents." end if RatatuiRuby::Debug.enabled? && body && !Ractor.shareable?(body) raise Rooibos::Error::Invariant, "Body is not Ractor-shareable: #{body.inspect}\n" \ "Use a frozen string or Ractor.make_shareable." end if RatatuiRuby::Debug.enabled? && envelope && !Ractor.shareable?(envelope) raise Rooibos::Error::Invariant, "Envelope is not Ractor-shareable: #{envelope.inspect}\n" \ "Use a frozen string, symbol, or Ractor.make_shareable." end if RatatuiRuby::Debug.enabled? && timeout && !Ractor.shareable?(timeout) raise Rooibos::Error::Invariant, "Timeout is not Ractor-shareable: #{timeout.inspect}\n" \ "Use a number or Ractor.make_shareable." end # Parser validation if parser && !parser.respond_to?(:call) raise ArgumentError, "parser: must respond to :call" end if RatatuiRuby::Debug.enabled? && parser && !Ractor.shareable?(parser) raise Rooibos::Error::Invariant, "Parser is not Ractor-shareable: #{parser.inspect}\n" \ "Use a frozen Method object or Ractor.make_shareable." end # Method validation unless %i[get post put patch delete].include?(method) raise ArgumentError, "Unsupported HTTP method: #{method.inspect}" end instance = allocate instance.__send__(:initialize, method:, url:, envelope:, headers:, body:, timeout: timeout || 10, parser:) instance end |
Instance Method Details
#call(out, token) ⇒ Object
Executes the HTTP request and sends the response.
Sends Message::HttpResponse with status, body, and headers. On network errors, sends the same message type with error populated instead.
Note: Ruby’s Net::HTTP blocks until completion. Cancellation cannot interrupt a request in progress. The grace period is 0.
- out
-
Outlet for sending messages.
- token
-
Cancellation token from the runtime.
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/rooibos/command/http.rb', line 199 def call(out, token) return if token.canceled? uri = URI(url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = uri.scheme == "https" if timeout http.open_timeout = timeout http.read_timeout = timeout end klass = case method when :get then Net::HTTP::Get when :post then Net::HTTP::Post when :put then Net::HTTP::Put when :patch then Net::HTTP::Patch when :delete then Net::HTTP::Delete end request = klass.new(uri) request.body = body if body headers&.each { |key, value| request[key] = value } response = http.request(request) response_body = response.body.freeze response_headers = response.each_header.to_h.freeze response_status = response.code.to_i # Invoke parser with positional params if provided parsed_body = if parser parser.call(response_body, response_headers, response_status) else response_body end # Validate parsed body is Ractor-shareable in debug mode if RatatuiRuby::Debug.enabled? && parser && !Ractor.shareable?(parsed_body) raise Rooibos::Error::Invariant, "Parsed body is not Ractor-shareable: #{parsed_body.class}\n" \ "Parser must return frozen/shareable data. Use .freeze or Ractor.make_shareable." end out.put(Ractor.make_shareable(HttpResponse.new( envelope:, status: response_status, body: parsed_body, headers: response_headers, error: nil ))) rescue => e out.put(Ractor.make_shareable(HttpResponse.new( envelope:, status: nil, body: nil, headers: nil, error: e..freeze ))) end |
#rooibos_cancellation_grace_period ⇒ Object
Net::HTTP is blocking; no cooperative cancellation possible. Grace period = 0 means runtime will orphan the blocked thread immediately.
143 |
# File 'lib/rooibos/command/http.rb', line 143 def rooibos_cancellation_grace_period = 0 |