Class: Hoodoo::Presenters::Base
- Inherits:
-
Object
- Object
- Hoodoo::Presenters::Base
- Defined in:
- lib/hoodoo/presenters/base.rb
Overview
Base functionality for JSON validation and presenter (rendering) layers. Subclass this to define a schema against which validation of inbound data or rendering of outbound data can be performed. Call #schema in the subclass to declare, via the DSL, the shape of the schema.
Direct Known Subclasses
Data::Resources::Caller, Data::Resources::Errors, Data::Resources::Log, Data::Resources::Session, Data::Types::ErrorPrimitive, Data::Types::Permissions, Data::Types::PermissionsDefaults, Data::Types::PermissionsFull, Data::Types::PermissionsResources, CommonResourceFields
Class Method Summary collapse
-
.get_schema ⇒ Object
Return the schema graph.
-
.get_schema_definition ⇒ Object
Read back the block that defined the schema graph.
-
.is_internationalised? ⇒ Boolean
Does this presenter use internationalisation? Returns
true
if so, elsefalse
. -
.render(data, uuid = nil, created_at = nil, language = 'en-nz', created_by = nil, updated_at = nil) ⇒ Object
Given some data that should conform to the subclass presenter’s schema, render it to go from the input Ruby Hash, to an output Ruby Hash which will include default values - if any - present in the schema and will drop input fields not present in that schema.
-
.render_in(context, data, options = {}) ⇒ Object
A higher level version of ::render, typically called from Hoodoo services in their resource implementation code.
-
.schema(&block) ⇒ Object
Define the JSON schema for validation.
-
.validate(data, as_resource = false) ⇒ Object
Is the given rendering of a resource valid? Returns an array of Error Primitive types (as hashes); this will be empty if the data given is valid.
-
.walk(&block) ⇒ Object
Walk the schema graph and invoke the given block on each field within it, passing the field instances to the block for each call.
Class Method Details
.get_schema ⇒ Object
Return the schema graph. See also #get_schema_definition.
299 300 301 |
# File 'lib/hoodoo/presenters/base.rb', line 299 def self.get_schema @schema end |
.get_schema_definition ⇒ Object
Read back the block that defined the schema graph. See also #get_schema.
306 307 308 |
# File 'lib/hoodoo/presenters/base.rb', line 306 def self.get_schema_definition @schema_definition end |
.is_internationalised? ⇒ Boolean
Does this presenter use internationalisation? Returns true
if so, else false
.
293 294 295 |
# File 'lib/hoodoo/presenters/base.rb', line 293 def self.is_internationalised? @schema.is_internationalised? end |
.render(data, uuid = nil, created_at = nil, language = 'en-nz', created_by = nil, updated_at = nil) ⇒ Object
Given some data that should conform to the subclass presenter’s schema, render it to go from the input Ruby Hash, to an output Ruby Hash which will include default values - if any - present in the schema and will drop input fields not present in that schema. In essence, this takes data which may have been programatically generated and sanitises it to produce valid, with-defaults guaranteed valid output.
Field kind
is taken from the class name. If concerned about clashes between resource names and ActiveRecord model names, put your resource classes inside a module for namespacing - for example:
module Resources
class Product < Hoodoo::Presenters::Base
schema do
...
end
end
end
Only the class “leaf” name is used to infer the resource kind - in the above case, that’s Product
.
Any field with a schema giving a default value will only appear should a value for that field be omitted in the input data. If the data provides, for example, an explicit nil
value then a corresponding explicit nil
will be rendered, regardless of defaults.
For belt-and-braces, unless subsequent profiling shows performance issues, callers should call #validate first to self-check their internal data against the schema prior to rendering. That way, coding errors will be discovered immediately, rather than hidden / obscured by the rendered sanitisation.
Since rendering top-level nil
is not valid JSON, should nil
be provided as input, it’ll be treated as an empty hash (“{}”) instead.
This is quite a low-level call. For a higher level renderer which Hoodoo service resource implementations will probably want to use for returning resource representations in responses, see ::render_in.
data
-
Hash to be represented. Data within this is compared against the schema being called to ensure that correct information is returned and unknown data is ignored.
uuid
-
Unique ID of the resource instance that is to be represented. If nil / omitted, this is assumed to be a rendering of a type or other non-resource like item. Otherwise the field is mandatory.
created_at
-
Date/Time of instance creation. Only required if UUID has been provided. This is a Ruby DateTime instance or similar, NOT a string!
language
-
Optional language. If the type/resource being rendered is internationalised but this is omitted, then a value of “en-nz” is used as a default.
created_by
-
Optional fingerprint of the Caller whose credentials were used to create the Session under which the resource instance was created. Absent if omitted.
updated_at
-
Optional Date/Time of instance update. This is a Ruby DateTime instance or similar, NOT a string!
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 139 140 |
# File 'lib/hoodoo/presenters/base.rb', line 96 def self.render( data, uuid = nil, created_at = nil, language = 'en-nz', created_by = nil, updated_at = nil ) target = {} data = data || {} @schema.render( data, target ) # Common fields are added after rendering the data in case there are # any same-named field collisions - platform defaults should take # precedence, overwriting previous definitions intentionally. unless ( uuid.nil? ) raise "Can't render a Resource with a nil 'created_at'" if created_at.nil? # Field "kind" is taken from the class name; this is a class method # so "self.name" yields "Hoodoo::Data::Resources::..." or similar. # We could just use "split", but that creates an intermediate Array # which uses more RAM and is about half the speed (or worse) of the # following alternative (it turns out ActiveSupport 4's #demodulize # uses much the same approach). name = self.name index = ( name.rindex( '::' ) || -2 ) + 2 kind = name[ index .. -1 ] target.merge!( { 'id' => uuid, 'kind' => kind, 'created_at' => Hoodoo::Utilities.standard_datetime( created_at.to_datetime ) } ) target[ 'updated_at' ] = Hoodoo::Utilities.standard_datetime( updated_at.to_datetime ) unless updated_at.nil? target[ 'created_by' ] = created_by unless created_by.nil? target[ 'language' ] = language if self.is_internationalised?() end return target end |
.render_in(context, data, options = {}) ⇒ Object
A higher level version of ::render, typically called from Hoodoo services in their resource implementation code.
As with ::render, data is rendered according to the schema of the object the ::render_in message is sent to. Options specify things like UUID and created-at date. Language information for internationalised fields can be given, but if omitted comes from the given request context data.
Additional facilites exist over and above ::render - security scoping information in the resource via its secured_with
field is made available through options (see below), along with support for embedded or referenced resource information.
context
-
A Hoodoo::Services::Context instance, which is usually the value passed to a service implementation in calls like Hoodoo::Services::Implementation#list or Hoodoo::Services::Implementation#show.
data
-
Hash to be represented. Data within this is compared against the schema being called to ensure that correct information is returned and unknown data is ignored.
options
-
Options hash, see below.
The options keys are Symbols, used as follows:
uuid
-
Same as the
uuid
parameter to ::render, except mandatory. created_at
-
Same as the
created_at
parameter to ::render, except mandatory. updated_at
-
Optional value expressing the time the resource was last updated.
created_by
-
Optional fingerprint of the Caller whose credentials were used to create the Session under which the resource instance was created.
language
-
Optional value for resource’s
language
field; taken from thecontext
parameter if omitted. embeds
-
A Hoodoo::Presenters::Embedding::Embeds instance that contains (fully rendered) resources which are to be embedded in this rendered representation. Optional.
references
-
A Hoodoo::Presenters::Embedding::References instance that contains UUIDs which are to be embedded in this rendered representation as references. Optional.
secured_with
-
An ActiveRecord::Base subclass instance where the model class includes a
secure_with
declaration. As per documentation for Hoodoo::ActiveRecord::Secure::ClassMethods#secure and Hoodoo::ActiveRecord::Secure::ClassMethods#secure_with, this leads (potentially) to the generation of thesecured_with
field and object value in the rendered resource data.
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 |
# File 'lib/hoodoo/presenters/base.rb', line 202 def self.render_in( context, data, = {} ) uuid = [ :uuid ] created_at = [ :created_at ] updated_at = [ :updated_at ] created_by = [ :created_by ] language = [ :language ] || context.request.locale secured_with = [ :secured_with ] = [ :embeds ] references = [ :references ] target = self.render( data, uuid, created_at, language, created_by, updated_at ) if defined?( ::ActiveRecord ) && secured_with.is_a?( ::ActiveRecord::Base ) result_hash = {} extra_scope_map = secured_with.class.secured_with() extra_scope_map.each do | model_field_name, | resource_field = if .is_a?( ::Hash ) next if [ :hide_from_resource ] == true [ :resource_field_name ] || model_field_name else model_field_name end result_hash[ resource_field.to_s ] = secured_with.send( model_field_name ) end unless extra_scope_map.nil? target[ 'secured_with' ] = result_hash unless result_hash.empty? end target[ '_embed' ] = .retrieve() unless .nil? target[ '_reference' ] = references.retrieve() unless references.nil? return target end |
.schema(&block) ⇒ Object
Define the JSON schema for validation.
- &block
-
Block that makes calls to the DSL defined in Hoodoo::Presenters::BaseDSL in order to define the schema.
26 27 28 29 30 |
# File 'lib/hoodoo/presenters/base.rb', line 26 def self.schema( &block ) @schema = Hoodoo::Presenters::Object.new @schema.instance_eval( &block ) @schema_definition = block end |
.validate(data, as_resource = false) ⇒ Object
Is the given rendering of a resource valid? Returns an array of Error Primitive types (as hashes); this will be empty if the data given is valid.
data
-
Ruby Hash representation of JSON data that is to be validated against ‘this’ schema. Keys must be Strings, not Symbols.
as_resource
-
Check Resource common fields -
id
,kind
,created_at
and (for an internationalised resource)language
. Otherwise, only basic data schema is examined. Optional; default isfalse
.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/hoodoo/presenters/base.rb', line 250 def self.validate( data, as_resource = false ) errors = @schema.validate( data ) if as_resource common_fields = { 'id' => data[ :id ], 'created_at' => data[ :created_at ], 'kind' => data[ :kind ] } created_by = data[ :created_by ] common_fields[ 'created_by' ] = created_by unless created_by.nil? updated_at = data[ :updated_at ] common_fields[ 'updated_at' ] = updated_at unless updated_at.nil? if self.is_internationalised? common_fields[ 'internationalised' ] = data[ 'internationalised' ] Hoodoo::Presenters::CommonResourceFields.get_schema.properties[ 'language' ].required = true end errors.merge!( Hoodoo::Presenters::CommonResourceFields.validate( data, false ) ) Hoodoo::Presenters::CommonResourceFields.get_schema.properties[ 'language' ].required = false end return errors end |
.walk(&block) ⇒ Object
Walk the schema graph and invoke the given block on each field within it, passing the field instances to the block for each call.
All fields including the top-level “root” property (which has an empty string for a name) will be passed to the block in order of definition, recursing into nested objects, arrays and so-on as each is encountered.
286 287 288 |
# File 'lib/hoodoo/presenters/base.rb', line 286 def self.walk( &block ) @schema.walk( &block ) end |