Class: ActionController::API

Inherits:
Object
  • Object
show all
Defined in:
runtime/action_controller/api.rb,
runtime/action_controller/api.rbs,
runtime/action_controller/current.rbs,
runtime/action_controller/http_authentication.rbs,
runtime/action_controller/current.rb,
runtime/action_controller/http_authentication.rb

Overview

The controller base class: per-request state (params) and response state (status, body, location). The action dispatch (process_action) and the before_action filter chain live here; subclasses define their actions and filter registrations.

Direct Known Subclasses

Snails::HealthController

Constant Summary collapse

BEFORE_FILTERS =

---- before_action filter chain -------------------------------------- Shaped by spinel's limits: no callback chain, no ancestor propagation of registration (the inherited hook is never called). The module-level constant Array below is the registry; a class ivar is not carried. Per-method contracts sit on before_action and run_before_filters.

[]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize ⇒ API

Returns a new instance of API.



25
# File 'runtime/action_controller/api.rbs', line 25

def initialize: () -> void

Instance Attribute Details

#accepts_any_format ⇒ Boolean

true when the request's Accept is a bare */*, an XMLHttpRequest or fetch that named none, which reads as "any format". The dispatcher sets it; false keeps every other request on the html path.

Returns:

  • (Boolean) —

    true when the request accepted any format



64
# File 'runtime/action_controller/api.rb', line 64

def accepts_any_format: () -> bool

#action_name ⇒ Symbol?

the current action: the run-time context the only:/except: gate reads. The action dispatch sets it before run_before_filters. Nil before any dispatch set it, and a conditional filter run without it raises rather than silently applying to every action (or none).

Parameters:

  • value (Symbol?) —

    the action to gate conditional filters against

Returns:

  • (Symbol?) —

    the action the controller is dispatching



58
# File 'runtime/action_controller/api.rb', line 58

def action_name: () -> Symbol?

#body ⇒ String (readonly)

Returns:



19
# File 'runtime/action_controller/api.rbs', line 19

def body: () -> String

#cache_control_max_age ⇒ Object (readonly)

Cache-Control as two typed readers rather than one mixed Hash: an Integer and a boolean in one container leaves every subscript with a union type for the compiler to infer. The test harness re-assembles the Hash shape (ActionResponse#cache_control), the only reader that wants the subscript spelling.



77
# File 'runtime/action_controller/api.rb', line 77

def cache_control_max_age: () -> Integer

#cache_control_public ⇒ Object (readonly)

Cache-Control as two typed readers rather than one mixed Hash: an Integer and a boolean in one container leaves every subscript with a union type for the compiler to infer. The test harness re-assembles the Hash shape (ActionResponse#cache_control), the only reader that wants the subscript spelling.



77
# File 'runtime/action_controller/api.rb', line 77

def cache_control_public: () -> bool

#content_type ⇒ String (readonly)

Returns:



21
# File 'runtime/action_controller/api.rbs', line 21

def content_type: () -> String

#location ⇒ String? (readonly)

Returns:



20
# File 'runtime/action_controller/api.rbs', line 20

def location: () -> String?

#path_parameters ⇒ Hash{String => String}

the matched route's own segments ({"length" => "1y"} on /top/1y), not the query string. The dispatcher assigns it; a caller building a URL from options reads it back to fill a segment the options leave out.

Returns:



70
# File 'runtime/action_controller/api.rb', line 70

def path_parameters: () -> Hash[String, String]

#request ⇒ ActionDispatch::Request?

the request object the dispatcher wires before the action runs.

Parameters:

Returns:



23
# File 'runtime/action_controller/current.rb', line 23

def request: () -> ActionDispatch::Request?

#request_format ⇒ Symbol

Returns:

  • (Symbol)


9
# File 'runtime/action_controller/api.rbs', line 9

def request_format: () -> Symbol

#request_method ⇒ String

Returns:



5
# File 'runtime/action_controller/api.rbs', line 5

def request_method: () -> String

#request_path ⇒ String

Returns:



7
# File 'runtime/action_controller/api.rbs', line 7

def request_path: () -> String

#status ⇒ Integer (readonly)

Returns:



18
# File 'runtime/action_controller/api.rbs', line 18

def status: () -> Integer

Class Method Details

.before_action(*names, only: nil, except: nil) ⇒ Object

register before-filters for the receiving controller class. Each name is a Symbol method the chain dispatches with send at run time — the method may live on the controller or a parent, and private is fine (send reaches both). Registration records the owner class: a filter runs only on instances of the class that registered it — the inherited hook is never called, so the registration itself does not propagate to subclasses (declare it there again, or share the method on a parent).

Examples:

before_action :authenticate_user!
before_action :mark_create, only: :create
before_action :mark_all, except: [:show, :index]

Parameters:

  • names (Array[Symbol]) —

    filter method names, run in registration order

  • only (Symbol, Array[Symbol], nil) (defaults to: nil) —

    run the filter only on these actions; conditional filters need the action context (action_name=) at run time and raise without it

  • except (Symbol, Array[Symbol], nil) (defaults to: nil) —

    skip the filter on these actions — same context requirement as only:

Raises:

  • (ArgumentError) —

    on a non-Symbol name — a String would miss every method at request time, far from the typo



140
# File 'runtime/action_controller/api.rb', line 140

def self.before_action: (*Symbol names, ?only: (Symbol | Array[Symbol])?, ?except: (Symbol | Array[Symbol])?) -> void

Instance Method Details

#authenticate_or_request_with_http_basic(realm = "Application", message = nil) ⇒ Object?

yield the presented credentials to the block. A falsy answer, or no Basic credentials at all, sends a 401 with the Basic challenge and marks the response performed, which halts the filter chain.

Parameters:

  • realm (String) (defaults to: "Application") —

    realm the 401 challenge states

  • message (String, nil) (defaults to: nil) —

    the 401 body; nil sends the default text

Returns:

  • (Object, nil) —

    the block's answer; nil when no credentials arrived



87
# File 'runtime/action_controller/http_authentication.rb', line 87

def authenticate_or_request_with_http_basic: (?String realm, ?String? message) { (String username, String password) -> untyped } -> untyped

#authenticate_or_request_with_http_token(realm = "Application", message = nil) ⇒ Object?

yield the presented token and its options to the block. A falsy answer, or no token at all, sends a 401 with the Token challenge and marks the response performed, which halts the filter chain.

The block is yielded to directly rather than forwarded to authenticate_with_http_token: an explicit &block parameter is a Proc value, which the AOT path types far less precisely than a yield it can see the caller's block for.

Parameters:

  • realm (String) (defaults to: "Application") —

    realm the 401 challenge states

  • message (String, nil) (defaults to: nil) —

    the 401 body; nil sends the default text

Returns:

  • (Object, nil) —

    the block's answer; nil when no token arrived



45
# File 'runtime/action_controller/http_authentication.rb', line 45

def authenticate_or_request_with_http_token: (?String realm, ?String? message) { (String token, Hash[String, String] options) -> untyped } -> untyped

#authenticate_with_http_basic {|pair[0], pair[1]| ... } ⇒ Object?

yield the presented Basic credentials to the block. No challenge goes out: a header without Basic credentials answers nil, and the caller decides what to do about it.

Yields:

  • (pair[0], pair[1])

Returns:

  • (Object, nil) —

    the block's answer; nil when the request carried no Basic credentials



72
# File 'runtime/action_controller/http_authentication.rb', line 72

def authenticate_with_http_basic: () { (String username, String password) -> untyped } -> untyped

#authenticate_with_http_token {|token, token_options(HttpAuthentication.authorization(self))| ... } ⇒ Object?

yield the presented token and its options to the block. No challenge goes out: a header without a Token or Bearer credential answers nil, and the caller decides what to do about it.

Yields:

Returns:

  • (Object, nil) —

    the block's answer; nil when the request carried no token



27
# File 'runtime/action_controller/http_authentication.rb', line 27

def authenticate_with_http_token: () { (String token, Hash[String, String] options) -> untyped } -> untyped

#expires_in(seconds, public: false, stale_while_revalidate: 0) ⇒ void

the Cache-Control writer: it stores the two facts and writes no header (see headers).

Seconds, not a Duration: the lowering unwraps 1.year at the call site, so the signature stays Integer. stale_while_revalidate: 0 means "not stated", which keeps it Integer rather than nullable; the value itself is not stored.

Parameters:

  • seconds (Integer) —

    the max-age to store, in seconds

  • public (Boolean) (defaults to: false) —

    store the response as publicly cacheable

  • stale_while_revalidate (Integer) (defaults to: 0) —

    seconds, or 0 for "not stated"



363
# File 'runtime/action_controller/api.rb', line 363

def expires_in: (Integer seconds, ?public: bool, ?stale_while_revalidate: Integer) -> void

#fresh_when(subject) ⇒ void

the conditional-GET hook: it takes the subject a caller would send validators for and stores nothing, because this runtime never answers a 304 and always renders.

The body is empty, not a bare nil: a nil-only body gives the compiler an optional type with nothing to infer its parameter from, where an empty body is a plain void.

Parameters:

  • subject (Object) —

    the record the validators describe — named subject rather than record because a lowering passes the controller as an implicit first argument called record, and a same-named parameter of our own collides with it



339
# File 'runtime/action_controller/api.rb', line 339

def fresh_when: (untyped subject) -> void

#head(status, content_type: nil, location: nil) ⇒ void

empty body, status only. content_type is optional: omit it and the default text/html stands, while an explicit value costs nothing — clients may still parse the header.

location: records the resource a 201 created (head :created, location: ...). It is not a redirect — that is a 3xx status — so a location beside a 201 is only a stated URL.

Parameters:

  • status (Symbol, Integer) —

    symbolic status (resolved through Snails::Utils::SYMBOL_TO_STATUS_CODE) or an integer code

  • content_type (String, nil) (defaults to: nil) —

    overrides the response content type

  • location (String, nil) (defaults to: nil) —

    sets the Location header value



293
# File 'runtime/action_controller/api.rb', line 293

def head: (Symbol | Integer status, ?content_type: String?, ?location: String?) -> void

#headers ⇒ Hash[String, String]

Returns:



52
# File 'runtime/action_controller/api.rbs', line 52

def headers: () -> Hash[String, String]

#params ⇒ Hash{String => untyped}

the request params an action reads (params["id"]). The value lives in @_params, outside the app-subclass namespace: an app-side @params is a mistake, not a second handle on request state. The scaffold contract (verify-scaffold) rejects @params in app code, and reading through the accessor lets the value change shape later without touching call sites.

The values are the request's scalars, arrays, hashes, and uploaded files; a key carried without a value is nil. The tree is a mutable plain Hash, so the accessor stays untyped.

Examples:

params["id"]   # => "42"

Returns:

  • (Hash{String => untyped}) —

    the request params



42
# File 'runtime/action_controller/api.rb', line 42

def params: () -> Hash[String, untyped]

#params=(value) ⇒ Hash[String, untyped]

Parameters:

Returns:



4
# File 'runtime/action_controller/api.rbs', line 4

def params=: (Hash[String, untyped]) -> Hash[String, untyped]

#performed? ⇒ Boolean

true once render/redirect_to/head has produced a response. Two gates read it: the action dispatch skips the action after a filter that responded (the halting rule), and send_action skips the implicit 204 once anything — filter or action — has answered.

Returns:

  • (Boolean) —

    true once a response has been produced



107
# File 'runtime/action_controller/api.rb', line 107

def performed?: () -> bool

#process_action(action_name) ⇒ void

dispatch one matched action: set the action context, run the controller's registered before-filters, then hand off to send_action.

A filter that responds (render/redirect/head) marks the response performed and the action is skipped — the halting rule. A name with no method raises rather than answering a silent empty 200. Subclasses override only to wrap this preamble (e.g. a locale around-block); an override that replaces the dispatch entirely must call send_action (not bare send) in its place or its actions lose the implicit 204.

Examples:

process_action(:show)

Parameters:

  • action_name (Symbol) —

    the action to dispatch

Raises:

  • (NoMethodError) —

    no method carries the action's name



189
# File 'runtime/action_controller/api.rb', line 189

def process_action: (Symbol action_name) -> void

#redirect_to(path, status: :found) ⇒ void

sets location and status. The default is 302 (Found); a PATCH/DELETE response that must not be replayed passes status: :see_other (303).

Parameters:

  • status (Symbol, Integer) (defaults to: :found) —

    symbolic status (resolved through Snails::Utils::SYMBOL_TO_STATUS_CODE) or an integer code



275
# File 'runtime/action_controller/api.rb', line 275

def redirect_to: (String path, ?status: Symbol | Integer) -> void

#render(body = nil, status: :ok, content_type: nil, location: nil, json: JSON_NOT_GIVEN) ⇒ void

sets @body, @status, and optionally @content_type / @location. The location: kwarg sets @location so the response ships a Location header alongside the rendered body — the render :show, status: :created, location: @article idiom for POST 201 responses. Distinct from redirect_to (which uses a 3xx status); main.rb dispatches on status, not on @location nil-ness.

The json: keyword is the payload form: render json: value encodes the value with ActionController::JsonRender and answers JSON_CONTENT_TYPE unless content_type: names another. It takes a payload, not encoded text — render json: "hi" answers "hi" with quotes; already-encoded JSON belongs in the positional body, which is what render(JsonRender.encode(v), content_type: ...) keeps producing. Exactly one of the two bodies must be given: a caller that passes neither forgot the argument, and a caller that passes both left two answerable meanings, so both raise instead of guessing. json: carries nil fine — it encodes to null, and the JSON_NOT_GIVEN sentinel is what keeps that apart from an omitted keyword.

Examples:

render("denied", status: :unauthorized)
render(json: { error: "not found" }, status: 404)
render(json: nil)

Parameters:

  • body (String, nil) (defaults to: nil) —

    the pre-rendered response body

  • status (Symbol, Integer) (defaults to: :ok) —

    symbolic status (resolved through Snails::Utils::SYMBOL_TO_STATUS_CODE) or an integer code

  • content_type (String, nil) (defaults to: nil) —

    overrides the response content type

  • location (String, nil) (defaults to: nil) —

    sets @location for the Location header

  • json (Object, nil) (defaults to: JSON_NOT_GIVEN) —

    the payload to encode as the JSON response body

Raises:

  • (ArgumentError) —

    when neither or both of body/json: is given



253
# File 'runtime/action_controller/api.rb', line 253

def render: (

#request_http_basic_authentication(realm = "Application", message = nil) ⇒ void

answer the request with a 401 whose WWW-Authenticate carries the Basic challenge. The render marks the response performed, so the filter chain stops here.

Parameters:

  • realm (String) (defaults to: "Application") —

    the realm the challenge states

  • message (String, nil) (defaults to: nil) —

    the 401 body; nil sends the default text



105
# File 'runtime/action_controller/http_authentication.rb', line 105

def request_http_basic_authentication: (?String realm, ?String? message) -> void

#request_http_token_authentication(realm = "Application", message = nil) ⇒ void

answer the request with a 401 whose WWW-Authenticate carries the Token challenge. The render marks the response performed, so the filter chain stops here.

Parameters:

  • realm (String) (defaults to: "Application") —

    the realm the challenge states

  • message (String, nil) (defaults to: nil) —

    the 401 body; nil sends the default text



60
# File 'runtime/action_controller/http_authentication.rb', line 60

def request_http_token_authentication: (?String realm, ?String? message) -> void

#resolve_status(s) ⇒ Integer

a status to its HTTP integer code. Both forms are accepted: a Symbol resolves through Snails::Utils::SYMBOL_TO_STATUS_CODE, and an Integer is used as given within 100..599. Anything else raises — a value the caller mistyped (status: 2000, status: :okay) must not fall through to the 200 default and read as a normal success. The message names both accepted forms so the caller can fix the line from the raise.

Parameters:

  • s (Symbol, Integer) —

    the symbolic status name or the integer code

Returns:

  • (Integer) —

    the HTTP status code

Raises:

  • (RuntimeError) —

    the value is neither a known symbol nor an integer in 100..599



395
# File 'runtime/action_controller/api.rb', line 395

def resolve_status: (Symbol | Integer s) -> Integer

#response ⇒ API

the surface app actions write headers through (response.headers["Expires"] = ...). The controller is its own buffered response, so response returns self and headers the extra-header hash. Those headers are buffered but not sent yet; they tune caching, not content.

Returns:

  • (API) —

    the controller itself



308
# File 'runtime/action_controller/api.rb', line 308

def response: () -> API

#run_before_filters ⇒ void

run this controller's registered before-filters in registration order, each gated by its only:/except: lists. The chain stops at the first filter that has produced a response: a filter that renders/redirects/heads skips the remaining filters and, at the call site, the action body.

Examples:

self.action_name = :create
run_before_filters

Raises:

  • (RuntimeError) —

    a filter carrying only:/except: ran without the action context — set self.action_name = before calling; silently applying the filter to every action (or none) would hide a mis-configured guard



162
# File 'runtime/action_controller/api.rb', line 162

def run_before_filters: () -> void

#send_action(action_name) ⇒ void

call the action by name, then answer 204 No Content when it produced no response. The implicit-render half of dispatch: an action with nothing to return still owes the caller a definite status, and the constructor's empty 200 says "success with a body" while carrying neither — a failure class that slips past any :success assertion. 204 is the definite answer for "done, nothing to return" (DELETE acknowledgements and other fire-and-forget writes are the common shape).

performed? is the gate on purpose: a response already produced — by the action body, or by a before-filter that halted the chain — is the answer and is never overwritten (head would clobber a rendered status and body). Wrapping callers (a preamble that runs the filters itself) call this rather than send so their actions keep the implicit render.

Examples:

send_action(:destroy)

Parameters:

  • action_name (Symbol) —

    the action to call

Raises:

  • (NoMethodError) —

    no method carries the action's name



215
# File 'runtime/action_controller/api.rb', line 215

def send_action: (Symbol action_name) -> void

#send_data(data, type: "application/octet-stream", disposition: "attachment") ⇒ void

a binary response body, buffered like render. disposition is accepted but not written out: the Content-Disposition header stays buffered like the ones in headers until the response ships.

Parameters:

  • disposition (String) (defaults to: "attachment") —

    accepted for the Content-Disposition header, which is not written yet



379
# File 'runtime/action_controller/api.rb', line 379

def send_data: (String data, ?type: String, ?disposition: String) -> void

#stale?(etag: nil) ⇒ Boolean

always true: this controller never answers a 304, so the validator argument is accepted and ignored.

Parameters:

  • etag (String, nil) (defaults to: nil) —

    accepted for the call shape; never read

Returns:

  • (Boolean) —

    always true



347
# File 'runtime/action_controller/api.rb', line 347

def stale?: (?etag: untyped) -> bool