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.
- G
- I
- R
- S
- V
- W
Return the schema graph. See also get_schema_definition.
Source: show
# File lib/hoodoo/presenters/base.rb, line 299 def self.get_schema @schema end
Read back the block that defined the schema graph. See also get_schema.
Source: show
# File lib/hoodoo/presenters/base.rb, line 306 def self.get_schema_definition @schema_definition end
Does this presenter use internationalisation? Returns true
if
so, else false
.
Source: show
# File lib/hoodoo/presenters/base.rb, line 293 def self.is_internationalised? @schema.is_internationalised? end
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!
Source: show
# 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
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.
Source: show
# File lib/hoodoo/presenters/base.rb, line 202 def self.render_in( context, data, options = {} ) uuid = options[ :uuid ] created_at = options[ :created_at ] updated_at = options[ :updated_at ] created_by = options[ :created_by ] language = options[ :language ] || context.request.locale secured_with = options[ :secured_with ] embeds = options[ :embeds ] references = options[ :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, key_or_options | resource_field = if key_or_options.is_a?( ::Hash ) next if key_or_options[ :hide_from_resource ] == true key_or_options[ :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' ] = embeds.retrieve() unless embeds.nil? target[ '_reference' ] = references.retrieve() unless references.nil? return target end
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.
Source: show
# 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
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
.
Source: show
# 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 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.
Source: show
# File lib/hoodoo/presenters/base.rb, line 286 def self.walk( &block ) @schema.walk( &block ) end