Class: HttpFake::HandlerContext
- Inherits:
-
Object
- Object
- HttpFake::HandlerContext
- Defined in:
- lib/httpfake/handler_context.rb
Overview
The ‘self` inside every route handler block. Provides the full DSL surface: respond, requires_body, validates, body, path_params, query_params, respond_sequence, raise_error.
Defined Under Namespace
Classes: ContractError
Instance Attribute Summary collapse
-
#path_params ⇒ Object
readonly
Returns the value of attribute path_params.
-
#query_params ⇒ Object
readonly
Returns the value of attribute query_params.
-
#request ⇒ Object
readonly
Returns the value of attribute request.
Instance Method Summary collapse
-
#body ⇒ Object
Lazily parsed request body — memoized.
-
#initialize(rack_request, path_params, call_index: 0) ⇒ HandlerContext
constructor
call_index: how many prior requests to this same (method, path) have been logged.
-
#raise_error(type) ⇒ Object
Simulate transport-level failures.
-
#requires_body(*keys) ⇒ Object
Contract assertion: raises ContractError if any key is absent from body.
-
#respond(status, json: nil, text: nil, headers: {}) ⇒ Object
Build and store the response tuple for this request.
-
#respond_sequence(*responses) ⇒ Object
Stateful sequence: call_index picks which response to use.
-
#response ⇒ Object
Internal: the built response tuple, or nil if none was set.
-
#validates(key, type: nil, min: nil, max: nil, inclusion: nil) ⇒ Object
Type / range / enum validation on a body field.
Constructor Details
#initialize(rack_request, path_params, call_index: 0) ⇒ HandlerContext
call_index: how many prior requests to this same (method, path) have been logged. Used by respond_sequence to pick the right entry without storing mutable state.
17 18 19 20 21 22 23 24 25 |
# File 'lib/httpfake/handler_context.rb', line 17 def initialize(rack_request, path_params, call_index: 0) @request = rack_request @path_params = path_params @query_params = Rack::Utils.parse_nested_query(rack_request.query_string.to_s) .transform_keys(&:to_sym) @call_index = call_index @_body = :unset @_response = nil end |
Instance Attribute Details
#path_params ⇒ Object (readonly)
Returns the value of attribute path_params.
13 14 15 |
# File 'lib/httpfake/handler_context.rb', line 13 def path_params @path_params end |
#query_params ⇒ Object (readonly)
Returns the value of attribute query_params.
13 14 15 |
# File 'lib/httpfake/handler_context.rb', line 13 def query_params @query_params end |
#request ⇒ Object (readonly)
Returns the value of attribute request.
13 14 15 |
# File 'lib/httpfake/handler_context.rb', line 13 def request @request end |
Instance Method Details
#body ⇒ Object
Lazily parsed request body — memoized.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/httpfake/handler_context.rb', line 28 def body return @_body unless @_body == :unset raw = @request.body&.read || "" @request.body&.rewind content_type = @request.content_type.to_s @_body = if content_type.include?("application/json") raw.empty? ? {} : JSON.parse(raw, symbolize_names: true) elsif content_type.include?("application/x-www-form-urlencoded") Rack::Utils.parse_nested_query(raw).transform_keys(&:to_sym) else raw end end |
#raise_error(type) ⇒ Object
Simulate transport-level failures.
82 83 84 85 86 87 88 89 |
# File 'lib/httpfake/handler_context.rb', line 82 def raise_error(type) case type when :timeout then raise Timeout::Error, "httpfake simulated timeout" when :reset then raise Errno::ECONNRESET, "httpfake simulated connection reset" when :refused then raise Errno::ECONNREFUSED, "httpfake simulated connection refused" else raise type.is_a?(Class) ? type : RuntimeError, type.to_s end end |
#requires_body(*keys) ⇒ Object
Contract assertion: raises ContractError if any key is absent from body.
62 63 64 65 66 67 |
# File 'lib/httpfake/handler_context.rb', line 62 def requires_body(*keys) keys.each do |key| present = body.is_a?(Hash) && (body.key?(key) || body.key?(key.to_s)) raise ContractError, "#{key} is required in request body" unless present end end |
#respond(status, json: nil, text: nil, headers: {}) ⇒ Object
Build and store the response tuple for this request.
45 46 47 48 49 |
# File 'lib/httpfake/handler_context.rb', line 45 def respond(status, json: nil, text: nil, headers: {}) body_str = json ? JSON.generate(resolve(json)) : text.to_s content_type = json ? "application/json" : "text/plain" @_response = [status.to_i, { "Content-Type" => content_type }.merge(headers), [body_str]] end |
#respond_sequence(*responses) ⇒ Object
Stateful sequence: call_index picks which response to use. Each entry is [status, { json: …, text: …, headers: … }]. Wraps around if more calls are made than entries defined.
54 55 56 57 58 59 |
# File 'lib/httpfake/handler_context.rb', line 54 def respond_sequence(*responses) entry = responses[@call_index % responses.length] status = entry[0] opts = entry[1] || {} respond(status, **opts) end |
#response ⇒ Object
Internal: the built response tuple, or nil if none was set.
92 93 94 |
# File 'lib/httpfake/handler_context.rb', line 92 def response @_response end |
#validates(key, type: nil, min: nil, max: nil, inclusion: nil) ⇒ Object
Type / range / enum validation on a body field.
70 71 72 73 74 75 76 77 78 79 |
# File 'lib/httpfake/handler_context.rb', line 70 def validates(key, type: nil, min: nil, max: nil, inclusion: nil) value = body.is_a?(Hash) ? (body[key] || body[key.to_s]) : nil raise ContractError, "#{key} must be a #{type}, got #{value.class}" if type && !value.is_a?(type) raise ContractError, "#{key} must be >= #{min}, got #{value.inspect}" if min && value < min raise ContractError, "#{key} must be <= #{max}, got #{value.inspect}" if max && value > max return unless inclusion && !inclusion.include?(value) raise ContractError, "#{key} must be one of #{inclusion.inspect}, got #{value.inspect}" end |