Class: Brut::FrontEnd::RequestContext

Inherits:
Object
  • Object
show all
Defined in:
lib/brut/front_end/request_context.rb

Overview

Container for request-specific information that serves as the source of what can be automaticall passed to various methods by Brut.

The intention for this class is to provide access to the 80% of stuff needed by most requests, to alleviate the need to have to dig into ‘env` or the Rack request. This also allows arbitrary information to be inserted and made available later.

Several methods of Brut objects take keyword arguments in their initializer or a particular method. The names of those keyword arguments correspond to values that are contained by this class. Thus, if you are creating, say, a Page subclass, and create an initializer for it that accepts the ‘clock:` keyword argument, the managed instance of Clock will be passed into it when Brut creates an instance of the class.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env:, session:, flash:, xhr:, body:, host:) ⇒ RequestContext

Create a new RequestContext based on some of the information provided by Rack

Parameters:

  • env (Hash)

    the Rack ‘env` object, as available to any middleware

  • session (Brut::FrontEnd::Session)

    the current session, noting that this is the Brut (or your app) session class and not the Rack session.

  • flash (Brut::FrontEnd::Flash)

    the current flash

  • xhr (true|false)

    true if this is an XHR request.

  • body (Object)

    the ‘request.body` as provided by Rack

  • host (URI)

    URI the ‘request.host` and `request.scheme`, and `request.port` as provided by Rack



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/brut/front_end/request_context.rb', line 32

def initialize(env:,session:,flash:,xhr:,body:,host:)
  @hash = {
    env:,
    session:,
    flash:,
    xhr:,
    body:,
    host:,
    csrf_token: Rack::Protection::AuthenticityToken.token(env["rack.session"]),
    clock: Clock.new(session.timezone),
  }
end

Class Method Details

.currentObject



12
13
14
# File 'lib/brut/front_end/request_context.rb', line 12

def self.current
  Thread.current.thread_variable_get(:request_context)
end

.inject(klass, request_params: nil) ⇒ Object

Create an instance of klass injected with the request context.



17
18
19
20
21
22
23
# File 'lib/brut/front_end/request_context.rb', line 17

def self.inject(klass, request_params: nil)
  self.current.then { |request_context|
    request_context.as_constructor_args(klass,request_params:)
  }.then { |constructor_args|
    klass.new(**constructor_args) 
  }
end

Instance Method Details

#[](key) ⇒ Object

Access a given value, returning ‘nil` if it’s not mapped or is ‘nil`

Parameters:

  • key (String|Symbol)

    the value to get

Returns:

  • (Object)

    the mapped value



76
77
78
# File 'lib/brut/front_end/request_context.rb', line 76

def [](key)
  @hash[key.to_sym]
end

#[]=(key, value) ⇒ Object

Set an arbitrary value that can be injected later

Parameters:

  • key (String|Symbol)

    the name of the value. This is converted to a symbol.

  • value (Object)

    the value to map. Should not be nil.



49
50
51
52
# File 'lib/brut/front_end/request_context.rb', line 49

def []=(key,value)
  key = key.to_sym
  @hash[key] = value
end

#as_constructor_args(klass, request_params:, route: nil, form: nil) ⇒ Hash

Based on ‘klass`’ constructor, returns a Hash that maps all keywords it requires to the values stored in this ‘RequestContext`. It is assumed that `request_params:` contains the query parameters so they can be injected. The Brut::FrontEnd::Routing::Route can also be injected to pass in.

Examples:

class SomeClass
  def initialize(flash:,clock:,date:)
    # ...
  end
end

hash = request_context.as_constructor_args(
  SomeClass,
  request_params: { date: "2024-11-11" }
)

# hash contains:
# {
#   flash: «Flash used to create the RequestContext»,
#   clock: «Clock used to create the RequestContext»,
#   date: "2024-11-11",
# }

object = SomeClass.new(**hash)

Parameters:

  • klass (Class)

    a class that is to be instantiated entirely by the contents of this ‘RequestContext`.

  • request_params (Hash)

    Query string parameters provided by Rack.

  • route (Brut::FrontEnd::Routing::Route) (defaults to: nil)

    the route that triggered the request.

  • form (Brut::FrontEnd::Form) (defaults to: nil)

    the form, if available

Returns:

  • (Hash)

    can be splatted to keyword arguments and passed to the constructor of ‘klass`

Raises:

  • (ArgumentError)

    if the constructor has any non-keyword arguments, or if any required keyword argument is not present in this ‘RequestContext`.



120
121
122
# File 'lib/brut/front_end/request_context.rb', line 120

def as_constructor_args(klass, request_params:, route:nil, form: nil)
  args_for_method(method: klass.instance_method(:initialize), request_params:, form: , route:)
end

#as_method_args(object, method_name, request_params:, form:, route: nil) ⇒ Hash

Based on ‘object`’ method, returns a Hash that maps all keywords it requires to the values stored in this ‘RequestContext`. It is assumed that `request_params:` contains the query parameters so they can be injected. It is also assumed that `form:` is the Form that is provided as part of the request. The Brut::FrontEnd::Routing::Route can also be injected to pass in.

Examples:

class SomeClass
  def doit(flash:,clock:,date:)
    # ...
  end
end

object = SomeClass.new

hash = request_context.as_method_args(
  object,
  :doit,
  request_params: { date: "2024-11-11" }
)

# hash contains:
# {
#   flash: «Flash used to create the RequestContext»,
#   clock: «Clock used to create the RequestContext»,
#   date: "2024-11-11",
# }

result = object.doit(**hash)

Parameters:

  • object (Class)

    an object whose method is to be called that requires some of the contents of this ‘RequestContext`.

  • method_name (Symbol)

    name of the method that will be called.

  • request_params (Hash)

    Query string parameters provided by Rack. Note that any parameter whose value is the empty string will be coerced to ‘nil`.

  • route (Brut::FrontEnd::Routing::Route) (defaults to: nil)

    the route that triggered the request.

  • form (Brut::FrontEnd::Form)

    the form that was submitted with this request. May be ‘nil`.

Returns:

  • (Hash)

    can be splatted to keyword arguments and passed to the constructor of ‘klass`

Raises:

  • (ArgumentError)

    if the method has any non-keyword arguments, or if any required keyword argument is not present in this ‘RequestContext`.



162
163
164
# File 'lib/brut/front_end/request_context.rb', line 162

def as_method_args(object, method_name, request_params:,form:,route:nil)
  args_for_method(method: object.method(method_name), request_params:, form:,route:)
end

#fetch(key) ⇒ Object

Access the given value, raising an exception if it has not been set or if it’s nil.

Parameters:

  • key (String|Symbol)

    the value to fetch.

Returns:

  • (Object)

    the mapped value

Raises:

  • (ArgumentError)

    if ‘key` was never mapped or maps to `nil`.



60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/brut/front_end/request_context.rb', line 60

def fetch(key)
  if self.key?(key)
    value = self[key]
    if value
      return value
    else
      raise ArgumentError,"No key '#{key}' in #{self.class}"
    end
  else
    raise ArgumentError,"Key '#{key}' is nil in #{self.class}"
  end
end

#key?(key) ⇒ true|false

Check if a given value has been mapped.

Parameters:

  • key (String|Symbol)

    the value to check

Returns:

  • (true|false)

    if the value is mapped. Note that if ‘nil` was injected, this method returns `true`.



83
84
85
# File 'lib/brut/front_end/request_context.rb', line 83

def key?(key)
  @hash.key?(key.to_sym)
end