Module: Hoodoo::Presenters::BaseDSL
Overview
A mixin to be used by any presenter that wants to support the Hoodoo::Presenters family of schema DSL methods. See e.g. Hoodoo::Presenters::Base. Mixed in by e.g. Hoodoo::Presenters::Object so that an instance can nest definitions of fields inside itself using this DSL.
Instance Method Summary collapse
-
#array(name, options = {}, &block) ⇒ Object
Define a JSON array with the supplied name and options.
-
#boolean(name, options = {}) ⇒ Object
Define a JSON boolean with the supplied name and options.
-
#date(name, options = {}) ⇒ Object
Define a JSON date with the supplied name and options.
-
#datetime(name, options = {}) ⇒ Object
Define a JSON datetime with the supplied name and options.
-
#decimal(name, options = {}) ⇒ Object
Define a JSON decimal with the supplied name and options.
-
#enum(name, options = {}) ⇒ Object
Define a JSON string which can only have a restricted set of exactly matched values, with the supplied name and options.
-
#float(name, options = {}) ⇒ Object
Define a JSON float with the supplied name and options.
-
#hash(name, options = {}, &block) ⇒ Object
Define a JSON object with the supplied name and optional constraints on properties (like hash keys) and property values (like hash values) that the object may contain, in abstract terms.
-
#integer(name, options = {}) ⇒ Object
Define a JSON integer with the supplied name and options.
-
#internationalised(options = nil) ⇒ Object
Declares that this Type or Resource contains fields which will may carry human-readable data subject to platform interntionalisation rules.
-
#is_internationalised? ⇒ Boolean
An enquiry method related to, but not part of the DSL; returns
true
if the schema instance is internationalised, elsefalse
. -
#object(name, options = {}, &block) ⇒ Object
Define a JSON object with the supplied name and options.
-
#resource(resource_info, options = nil) ⇒ Object
Declare that a resource of a given name is included at this point.
-
#string(name, options = {}) ⇒ Object
Define a JSON string with the supplied name and options.
-
#tags(field_name, options = nil) ⇒ Object
Declares that this Type or Resource has a string field of unlimited length that contains comma-separated tag strings.
-
#text(name, options = {}) ⇒ Object
Define a JSON string of unlimited length with the supplied name and options.
-
#type(type_info, options = nil) ⇒ Object
Declare that a nested type of a given name is included at this point.
-
#uuid(field_name, options = nil) ⇒ Object
Declares that this Type or Resource _refers to_ another Resource instance via its UUID.
Instance Method Details
#array(name, options = {}, &block) ⇒ Object
Define a JSON array with the supplied name and options. If there is a block provided, then more DSL calls inside the block define how each array entry must look; otherwise array entries are not validated / are undefined.
When an array field uses :required => true, this only says that at least an empty array must be present, nothing more. If the array uses a block with fields that themselves are required, then this is only checked for if the array contains one or more entries (and is checked for each of those entries).
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true - &block
-
Optional block declaring the fields of each array item
Example - mandatory JSON field “currencies” would lead to an array where each array entry contains the fields defined by Hoodoo::Data::Types::Currency along with an up-to-32 character string with field name “notes”, that field also being required. Whether or not the fields of the referenced Currency type are needed is up to the definition of that type. See #type for more information.
class VeryWealthy < Hoodoo::Presenters::Base
schema do
array :currencies, :required => true do
type :Currency
string :notes, :required => true, :length => 32
end
end
end
82 83 84 85 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 82 def array( name, = {}, &block ) ary = property( name, Hoodoo::Presenters::Array, , &block ) internationalised() if ary.is_internationalised?() end |
#boolean(name, options = {}) ⇒ Object
Define a JSON boolean with the supplied name and options.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true
177 178 179 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 177 def boolean( name, = {} ) property( name, Hoodoo::Presenters::Boolean, ) end |
#date(name, options = {}) ⇒ Object
Define a JSON date with the supplied name and options.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true
186 187 188 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 186 def date( name, = {} ) property( name, Hoodoo::Presenters::Date, ) end |
#datetime(name, options = {}) ⇒ Object
Define a JSON datetime with the supplied name and options.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true
195 196 197 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 195 def datetime( name, = {} ) property( name, Hoodoo::Presenters::DateTime, ) end |
#decimal(name, options = {}) ⇒ Object
Define a JSON decimal with the supplied name and options.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true, :precision => 10
168 169 170 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 168 def decimal( name, = {} ) property( name, Hoodoo::Presenters::Decimal, ) end |
#enum(name, options = {}) ⇒ Object
Define a JSON string which can only have a restricted set of exactly matched values, with the supplied name and options.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true and mandatory :from => [array-of-allowed-strings-or-symbols]
216 217 218 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 216 def enum( name, = {} ) property( name, Hoodoo::Presenters::Enum, ) end |
#float(name, options = {}) ⇒ Object
Define a JSON float with the supplied name and options.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true
159 160 161 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 159 def float( name, = {} ) property( name, Hoodoo::Presenters::Float, ) end |
#hash(name, options = {}, &block) ⇒ Object
Define a JSON object with the supplied name and optional constraints on properties (like hash keys) and property values (like hash values) that the object may contain, in abstract terms.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true - &block
-
Optional block declaring the fields making up the nested hash
Example 1 - a Hash where keys must be <= 16 characters long and values
must match the Hoodoo::Data::Types::Currency type.
class CurrencyHash < Hoodoo::Presenters::Base
schema do
hash :currencies do
keys :length => 16 do
type :Currency
end
end
end
end
See Hoodoo::Presenters::Hash#keys for more information and examples.
Example 2 - a Hash where keys must be ‘one’ or ‘two’, each with a
value matching the given schema.
class AltCurrencyHash < Hoodoo::Presenters::Base
schema do
hash :currencies do
key :one do
type :Currency
end
key :two do
text :title
text :description
end
end
end
end
See Hoodoo::Presenters::Hash#key for more information and examples.
131 132 133 134 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 131 def hash( name, = {}, &block ) hash = property( name, Hoodoo::Presenters::Hash, , &block ) internationalised() if hash.is_internationalised?() end |
#integer(name, options = {}) ⇒ Object
Define a JSON integer with the supplied name and options.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true
141 142 143 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 141 def integer( name, = {} ) property( name, Hoodoo::Presenters::Integer, ) end |
#internationalised(options = nil) ⇒ Object
Declares that this Type or Resource contains fields which will may carry human-readable data subject to platform interntionalisation rules. A Resource which is internationalised automatically gains a language
field (part of the Platform API’s Common Fields) used in resource representations. A Type which is internationalised gains nothing until it is cross-referenced by a Resource definion, at which point the cross-referencing resource becomes itself implicitly internationalised (so it “taints” the resource). For cross-referencing, see #type.
options
-
Optional options hash. No options currently defined.
Example - a Member resource with internationalised fields such as the member’s name:
class Member < Hoodoo::Presenters::Base
schema do
# Say that Member will contain at least one field that holds
# human readable data, causing the Member to be subject to
# internationalisation rules.
internationalised
# Declare fields as normal, for example...
text :name
end
end
485 486 487 488 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 485 def internationalised( = nil ) ||= {} @internationalised = true end |
#is_internationalised? ⇒ Boolean
An enquiry method related to, but not part of the DSL; returns true
if the schema instance is internationalised, else false
.
493 494 495 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 493 def is_internationalised? !! @internationalised end |
#object(name, options = {}, &block) ⇒ Object
Define a JSON object with the supplied name and options.
name
-
The JSON key.
options
-
Optional
Hash
of options, e.g. :required => true - &block
-
Block declaring the fields making up the nested object
Example - mandatory JSON field “currencies” would lead to an object which had the same fields as Hoodoo::Data::Types::Currency along with an up-to-32 character string with field name “notes”, that field also being required. Whether or not the fields of the referenced Currency type are needed is up to the definition of that type. See #type for more information.
class Wealthy < Hoodoo::Presenters::Base
schema do
object :currencies, :required => true do
type :Currency
string :notes, :required => true, :length => 32
end
end
end
44 45 46 47 48 49 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 44 def object( name, = {}, &block ) raise ArgumentError.new( 'Hoodoo::Presenters::Base#Object must have block' ) unless block_given? obj = property( name, Hoodoo::Presenters::Object, , &block ) internationalised() if obj.is_internationalised?() end |
#resource(resource_info, options = nil) ⇒ Object
Declare that a resource of a given name is included at this point. This is only normally done within the description of the schema for an interface. The fields of the given named resource are considered to be defined inline at the point of declaration - essentially, it’s macro expansion.
resource_info
-
The Hoodoo::Presenters::Base subclass for the Resource in question, e.g.
Product
. The deprecated form of this interface takes the name of the type to nest as a symbol, e.g.:Product
, in which case the Resource must be declared within nested modules “Hoodoo::Data::Types”. options
-
Optional options hash. No options currently defined.
Example - an iterface takes an Outlet
resource in its create action.
class Outlet < Hoodoo::Presenters::Base
schema do
internationalised
text :name
uuid :participant_id, :resource => :Participant, :required => true
uuid :calculator_id, :resource => :Calculator
end
end
class OutletInterface < Hoodoo::Services::Interface
to_create do
resource Outlet
end
end
It doesn’t make sense to mark a resource
‘field’ as :required
in the options since the declaration just expands to the contents of the referenced resource and it is the definition of that resource that determines whether or not its various field(s) are optional / required. That is, the following two declarations behave identically:
resource Outlet
resource Outlet, :required => true # Pointless option!
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 438 def resource( resource_info, = nil ) ||= {} if resource_info.is_a?( Class ) && resource_info < Hoodoo::Presenters::Base klass = resource_info else begin klass = Hoodoo::Data::Resources.const_get( resource_info ) rescue raise "Hoodoo::Presenters::Base\#resource: Unrecognised resource name '#{ resource_info }'" end end self.instance_exec( &klass.get_schema_definition() ) end |
#string(name, options = {}) ⇒ Object
Define a JSON string with the supplied name and options.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true, :length => 10
150 151 152 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 150 def string( name, = {} ) property( name, Hoodoo::Presenters::String, ) end |
#tags(field_name, options = nil) ⇒ Object
Declares that this Type or Resource has a string field of unlimited length that contains comma-separated tag strings.
field_name
-
Name of the field that will hold the tags.
options
-
Optional options hash. See Hoodoo::Presenters::BaseDSL.
Example - a Product resource which supports product tagging:
class Product < Hoodoo::Presenters::Base
schema do
internationalised
text :name
text :description
string :sku, :length => 64
:tags
end
end
239 240 241 242 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 239 def ( field_name, = nil ) ||= {} property( field_name, Hoodoo::Presenters::Tags, ) end |
#text(name, options = {}) ⇒ Object
Define a JSON string of unlimited length with the supplied name and options.
name
-
The JSON key
options
-
A
Hash
of options, e.g. :required => true
205 206 207 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 205 def text( name, = {} ) property( name, Hoodoo::Presenters::Text, ) end |
#type(type_info, options = nil) ⇒ Object
Declare that a nested type of a given name is included at this point. This is only normally done within an array
or object
declaration. The fields of the given named type are considered to be defined inline at the point of declaration - essentially, it’s macro expansion.
type_info
-
The Hoodoo::Presenters::Base subclass for the Type in question, e.g.
BasketItem
. The deprecated form of this interface takes the name of the type to nest as a symbol, e.g.:BasketItem
, in which case the Type must be declared within nested modules “Hoodoo::Data::Types”. options
-
Optional options hash. No options currently defined.
It doesn’t make sense to mark a type
‘field’ as :required
in the options since the declaration just expands to the contents of the referenced type and it is the definition of that type that determines whether or not its various field(s) are optional or required.
Example 1 - a basket includes an array of the Type described by class BasketItem
:
class Basket < Hoodoo::Presenters::Base
schema do
array :items do
type BasketItem
end
end
end
A fragment of JSON for a basket might look like this:
{
"items": [
{
// (First BasketItem's fields)
},
{
// (First BasketItem's fields)
},
// etc.
]
}
Example 2 - a basket item refers to a product description by having its fields inline. So suppose we have this:
class Product < Hoodoo::Presenters::Base
schema do
internationalised
text :name
text :description
end
end
class BasketItem < Hoodoo::Presenters::Base
schema do
object :product_data do
type Product
end
end
end
…then this would be a valid BasketItem fragment of JSON:
{
"product_data": {
"name": "Washing powder",
"description": "Washes whiter than white!"
}
}
It is also possible to use this mechanism for inline expansions when you have, say, a Resource defined entirely in terms of something reused elsewhere as a Type. For example, suppose the product/basket information from above included information on a Currency that was used for payment. It might reuse a Type; meanwhile we might have a resource for managing Currencies, defined entirely through that Type:
class Currency < Hoodoo::Presenters::Base
schema do
string :curency_code, :required => true, :length => 8
string :symbol, :length => 16
integer :multiplier, :default => 100
array :qualifiers do
string :qualifier, :length => 32
end
end
end
resource :Currency do
schema do
type Currency # Fields are *inline*
end
end
This means that the Resource of Currency
has exactly the same fields as the Type of Currency. The Resource could define other fields too, though this would be risky as the Type might gain same-named fields in future, leading to undefined behaviour. At such a time, a degree of cut-and-paste and removing the type
call from the Resource definition would probably be wise.
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 379 def type( type_info, = nil ) ||= {} if type_info.is_a?( Class ) && type_info < Hoodoo::Presenters::Base klass = type_info else begin klass = Hoodoo::Data::Types.const_get( type_info ) rescue raise "Hoodoo::Presenters::Base\#type: Unrecognised type name '#{ type_info }'" end end self.instance_exec( &klass.get_schema_definition() ) end |
#uuid(field_name, options = nil) ⇒ Object
Declares that this Type or Resource _refers to_ another Resource instance via its UUID. There’s no need to declare the presence of the UUID field _for the instance itself_ on all resource definitions as that’s implicit; this #uuid method is just for relational information (AKA associations).
field_name
-
Name of the field that will hold the UUID.
options
-
Options hash. See below.
In addition to standard options from Hoodoo::Presenters::BaseDSL, extra option keys and values are:
:resource
-
The name of a resource (as a symbol, e.g.
:Product
) that the UUID should refer to. Implementations may use this to validate that the resource, where a UUID is provided, really is for a Product instance and not something else. Optional.
Example - a basket item that refers to an integer quantity of some specific Product resource instance:
class BasketItem < Hoodoo::Presenters::Base
schema do
integer :quantity, :required => true
uuid :product_id, :resource => :Product
end
end
272 273 274 275 |
# File 'lib/hoodoo/presenters/base_dsl.rb', line 272 def uuid( field_name, = nil ) ||= {} property(field_name, Hoodoo::Presenters::UUID, ) end |