Class: Hoodoo::Client::Headers
- Inherits:
-
Object
- Object
- Hoodoo::Client::Headers
- Defined in:
- lib/hoodoo/client/headers.rb
Overview
Hoodoo::Client and related software such as Hoodoo::Services::Middleware need common access to information about special processing headers defined by Hoodoo and the Hoodoo API. This class is just a container - pretty much a namespaced library - holding that kind of information and support methods.
Constant Summary collapse
- UUID_PROPERTY_PROC =
Used by HEADER_TO_PROPERTY; this Proc when called with some non-nil value from an HTTP header representing a UUID, evaluates to either the UUID as a String or
nil
if the value appeared to not be a UUID. -> ( value ) { value = Hoodoo::UUID.valid?( value ) && value value || nil # => 'value' if 'value' is truthy, 'nil' if 'value' falsy }
- UUID_HEADER_PROC =
Used by HEADER_TO_PROPERTY; this Proc when called with some UUID evaluates to the input value coerced to a String and no other changes.
-> ( value ) { value }
- KVP_PROPERTY_PROC =
Used by HEADER_TO_PROPERTY; this Proc when called with some non-nil value from an HTTP header containing URL-encoded simple key/value pair data returns a decoded Hash of key/value pairs. Use URL encoding in the HTTP header value as per:
www.w3.org/TR/html5/forms.html#url-encoded-form-data
Invalid input will produce unusual results, e.g. an empty Hash or a Hash where certain keys may have empty string values.
-> ( value ) { Hash[ URI.decode_www_form( value ) ] }
- KVP_HEADER_PROC =
Used by HEADER_TO_PROPERTY; this Proc when called with some non-nested Hash evaluates to a URL-encoded form data String as per:
-> ( value ) { URI.encode_www_form( value ) }
- DATETIME_IN_PAST_ONLY_PROPERTY_PROC =
Used by HEADER_TO_PROPERTY; this Proc when called with some non-nil value from an HTTP header representing a Date/Time in a supported format, evaluates to either a parsed DateTime instance or
nil
if the value appeared to not be in a supported format. -> ( value ) { value = Hoodoo::Utilities.valid_iso8601_subset_datetime?( value ) value = nil if value && Hoodoo::Utilities.is_in_future?( value ) value || nil # => 'value' if 'value' is truthy, 'nil' if 'value' falsy }
- DATETIME_WRITER_PROC =
Used by HEADER_TO_PROPERTY; this Proc is called with a Time, Date, DateTime or DateTime-parseable String and returns a DateTime. It is used for a custom write accessor for the property associated with a header entry and works independently of the validation mechanism for inbound String-only from-header data.
-> ( value ) { Hoodoo::Utilities.rationalise_datetime( value ) }
- DATETIME_HEADER_PROC =
Used by HEADER_TO_PROPERTY; this Proc when called with a DateTime instance evaluates to a String representing the DateTime as an ISO 8601 subset value given to nanosecond precision.
-> ( value ) { Hoodoo::Utilities.nanosecond_iso8601( value ) }
- BOOLEAN_PROPERTY_PROC =
Used by HEADER_TO_PROPERTY; this Proc when called with some non-nil value from an HTTP header representing a Boolean as “yes” or “no”, evaluates to either
true
for “yes” orfalse
for any other value. Case insensitive. -> ( value ) { value.to_s.downcase == 'yes' || value == true ? true : false }
- BOOLEAN_HEADER_PROC =
Used by HEADER_TO_PROPERTY; this Proc when called with
true
orfalse
evaluates to String “yes” fortrue
or “no” for any other value. -> ( value ) { value == true ? 'yes' : 'no' }
- HEADER_TO_PROPERTY =
Various “X-Foo”-style HTTP headers specified in the Hoodoo API Specification have special meanings and values for those need to be set up in request data and Hoodoo::Client endpoints. Processing around these is data driven by this mapping Hash.
Keys are the HTTP header names in Rack (upper case, “HTTP_”-prefix) format. Values are options bundles as follows:
property
-
The property name to be associated with the header, as a Symbol.
property_proc
-
A Proc that’s called to both validate and clean up the raw value from the HTTP header. It evaluates to
nil
if the value is invalid, or non-nil
for any other case. Note that there is no way for an HTTP header to explicitly convey a corresponding value internally ofnil
as a result, by design; instead the relevant header would simply be omitted by the caller (and/or change your header design!). writer_proc
-
If a property has a possible amigbuity of input data types when set externally, independently of any validation etc. from the
property_proc
option, then this optional entry contains a Proc that is used for a custom write accessor and canonicalises assumed-valid but possibly not canonical input for writing. An example would be the conversion of String or Time instances to a DateTime so that a property always reads back with a DateTime instance. header
-
For speed in lookups where it’s needed, this is the “real” (not Rack format) HTTP header name.
header_proc
-
A Proc that’s called to convert a cleaned-up value set in the
property
by itsproperty_proc
. It is called with this value and returns an equivalent appropriate value for use with the HTTP header given inheader
. This MUST always be a String. secured
-
Optional, default
nil
. Iftrue
, marks that this header and its associated value can only be processed if there is a Session with a Caller that has anauthorised_http_headers
entry for this header. auto_transfer
-
Optional, default
nil
. Only relevant to inter-resource call scenarios. Iftrue
, when one resource calls another, the value of this property is automatically transferred to the downstream resource. Otherwise, it is not, and the downstream resource will operate under whatever defaults are present. An inter-resource call endpoint which inherits an auto-transfer property can always have this property explicitly overwritten before any calls are made through it.
An additional key of
:property_writer
will be set up automatically which contains the value of the:property
key with an “=” sign added, resulting in the name of a write accessor method for that property. { # Take care not to define any property name which clashes with an # option in any other part of this entire system where these "other # options" get merged in. A project search for # 'HEADER_TO_PROPERTY' in comments should find those. 'HTTP_X_RESOURCE_UUID' => { :property => :resource_uuid, :property_proc => UUID_PROPERTY_PROC, :header => 'X-Resource-UUID', :header_proc => UUID_HEADER_PROC, :secured => true, }, 'HTTP_X_ASSUME_IDENTITY_OF' => { :property => :assume_identity_of, :property_proc => KVP_PROPERTY_PROC, :header => 'X-Assume-Identity-Of', :header_proc => KVP_HEADER_PROC, :secured => true, :auto_transfer => true, }, 'HTTP_X_DATED_AT' => { :property => :dated_at, :property_proc => DATETIME_IN_PAST_ONLY_PROPERTY_PROC, :writer_proc => DATETIME_WRITER_PROC, :header => 'X-Dated-At', :header_proc => DATETIME_HEADER_PROC, :auto_transfer => true, }, 'HTTP_X_DATED_FROM' => { :property => :dated_from, :property_proc => DATETIME_IN_PAST_ONLY_PROPERTY_PROC, :writer_proc => DATETIME_WRITER_PROC, :header => 'X-Dated-From', :header_proc => DATETIME_HEADER_PROC, :auto_transfer => true, }, 'HTTP_X_DEJA_VU' => { :property => :deja_vu, :property_proc => BOOLEAN_PROPERTY_PROC, :header => 'X-Deja-Vu', :header_proc => BOOLEAN_HEADER_PROC, }, 'HTTP_X_DISABLE_DOWNSTREAM_SYNC' => { :property => :disable_downstream_sync, :property_proc => BOOLEAN_PROPERTY_PROC, :header => 'X-Disable-Downstream-Sync', :header_proc => BOOLEAN_HEADER_PROC, }, }
Class Method Summary collapse
-
.define_accessors_for_header_equivalents(klass) ⇒ Object
Define a series of read and custom write accessors according to the HTTP_HEADER_OPTIONS_MAP.
-
.x_header_to_options(hashlike_source) ⇒ Object
From a Hash-like source where keys are HTTP header names and values are the corresponding HTTP header values, extract interesting values and return a Hash of options as described below.
Class Method Details
.define_accessors_for_header_equivalents(klass) ⇒ Object
Define a series of read and custom write accessors according to the HTTP_HEADER_OPTIONS_MAP. For example, a property of “dated_at” results in a dated_at
reader, a dated_at=
writer which calls Hoodoo::Utilities.rationalise_datetime to clean up the input value and sets the result into the @dated_at
instance variable which the read accessor will be expecting to use.
klass
-
The Class to which the instance methods will be added.
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/hoodoo/client/headers.rb', line 240 def self.define_accessors_for_header_equivalents( klass ) klass.class_eval do HEADER_TO_PROPERTY.each do | rack_header, description | attr_reader( description[ :property ] ) custom_writer = description[ :writer_proc ] if custom_writer.nil? attr_writer( description[ :property ] ) else define_method( "#{ description[ :property ] }=" ) do | parameter | instance_variable_set( "@#{ description[ :property ] }", description[ :writer_proc ].call( parameter ) ) result = instance_variable_get("@#{ description[ :property ] }") end end end end end |
.x_header_to_options(hashlike_source) ⇒ Object
From a Hash-like source where keys are HTTP header names and values are the corresponding HTTP header values, extract interesting values and return a Hash of options as described below.
Any X-Foo
header is extracted, including core Hoodoo extension headers such as X-Interaction-ID
, which is present in any response. The “X-” is stripped, the rest converted to lower case and hyphens converted to underscores. The interaction ID, therefore, would be set as an interaction_id
option. X-Foo
would be set as a foo
option - and so-on.
The header matcher accepts headers from the Hash-like source in upper or lower case with hyphens or underscores inside; extracted headers can therefore start with any of X_
, x_
, X-
or x-
. The Hash-like source must support the each
operator yielding a key and value to the block on each iteration.
Header values are not translated at all, so (unless something very unsual is going on) the option values will be Strings.
If the same header is encountered more than once, only the first one encountered (in enumeration order, whatever that might be) is stored.
Parameters:
hashlike_source
-
Hash-like source containing HTTP headers/values.
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/hoodoo/client/headers.rb', line 289 def self.( hashlike_source ) hashlike_source ||= {} = {} hashlike_source.each do | key, value | next unless ( key[ 0 ] == 'x' || key[ 0 ] == 'X' ) && ( key[ 1 ] == '-' || key[ 1 ] == '_' ) entry = key.to_s.downcase.gsub( '-', '_' )[ 2..-1 ] unless entry == '' || .has_key?( entry ) [ entry ] = value end end return end |