hoodoo 1.12.4 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 612e215b33df39e4942acbf763c27af9c817f5a2
4
- data.tar.gz: 9159951c875326ec7bcdd5d696365563028c7d2d
3
+ metadata.gz: 7afd9823b45dd59e8031b83a8eafe26118d38240
4
+ data.tar.gz: 3b23639744e3b536f5f5192538e88d07cf7cccef
5
5
  SHA512:
6
- metadata.gz: c2c0eeb88c1f8950a3dda6725f1110b8e32b9767c30dfbe4a560c4dbacaaad5d459ccfe71d46de5b850031d459708853aa818cfcc06f241f3176ee4319874403
7
- data.tar.gz: b747857c2b8469936e728e81872b80d33016df0a68a2e15a6001794593d216a6c86ec6d30cae97d0c48e6f1d9fd22bb947307a8b29cea398a8b3829d886f6a38
6
+ metadata.gz: 79d4a1cc3b9217baecf9129b245e16fea79d80f0fd8cf1accc54724e43ec9d9ea0fc4988241ba5418d52c8c5f33b401fad4157db204e2adfcf8bac72ba69a9e6
7
+ data.tar.gz: 0bd7649d997eec9b349119464bcd6ea801ffd099bf0df21136e77a6401449cf045f8cd931d0a9846afcc5beb9be02f063643115bde2054de46ddbb5e8acd6766
@@ -22,7 +22,7 @@ module Hoodoo
22
22
  # Define a JSON object with the supplied name and options.
23
23
  #
24
24
  # +name+:: The JSON key.
25
- # +options+:: Optional +Hash+ of options, e.g. :required => true
25
+ # +options+:: Optional +Hash+ of options, e.g. <tt>:required => true</tt>
26
26
  # &block:: Block declaring the fields making up the nested object
27
27
  #
28
28
  # Example - mandatory JSON field "currencies" would lead to an object
@@ -51,19 +51,69 @@ module Hoodoo
51
51
  # Define a JSON array with the supplied name and options. If there is
52
52
  # a block provided, then more DSL calls inside the block define how each
53
53
  # array entry must look; otherwise array entries are not validated /
54
- # are undefined.
54
+ # are undefined unless the +:type+ option is specified (see below).
55
55
  #
56
- # When an array field uses +:required => true+, this only says that at
56
+ # When an array uses <tt>:required => true</tt>, this only says that at
57
57
  # least an empty array must be present, nothing more. If the array uses
58
58
  # a block with fields that themselves are required, then this is only
59
59
  # checked for if the array contains one or more entries (and is checked
60
60
  # for each of those entries).
61
61
  #
62
62
  # +name+:: The JSON key
63
- # +options+:: A +Hash+ of options, e.g. :required => true
63
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt>
64
64
  # &block:: Optional block declaring the fields of each array item
65
65
  #
66
- # Example - mandatory JSON field "currencies" would lead to an array
66
+ # Array entries are normally either unvalidated, or describe complex
67
+ # types via a block. For simple fields, pass a :type option to declare
68
+ # that array entries must be of supported types as follows:
69
+ #
70
+ # [:array]
71
+ # Hoodoo::Presenters::Array (see #array)
72
+ # [:boolean]
73
+ # Hoodoo::Presenters::Boolean (see #boolean)
74
+ # [:date]
75
+ # Hoodoo::Presenters::Date (see #date)
76
+ # [:date_time]
77
+ # Hoodoo::Presenters::DateTime (see #datetime)
78
+ # [:decimal]
79
+ # Hoodoo::Presenters::Decimal (see #decimal)
80
+ # [:enum]
81
+ # Hoodoo::Presenters::Enum (see #enum)
82
+ # [:float]
83
+ # Hoodoo::Presenters::Float (see #float)
84
+ # [:integer]
85
+ # Hoodoo::Presenters::Integer (see #integer)
86
+ # [:string]
87
+ # Hoodoo::Presenters::String (see #string)
88
+ # [:tags]
89
+ # Hoodoo::Presenters::Tags (see #tags)
90
+ # [:text]
91
+ # Hoodoo::Presenters::Text (see #text)
92
+ # [:uuid]
93
+ # Hoodoo::Presenters::UUID (see #uuid)
94
+ #
95
+ # Some of these types require additional parameters, such as
96
+ # +:precision+ for Hoodoo::Presenters::Decimal or +:from+ for
97
+ # Hoodoo::Presenters::Enum. For _any_ options that are to apply to the
98
+ # the new Array simple type fields, prefix the option with the string
99
+ # +field_+ - for example, <tt>:field_precision => 2</tt>.
100
+ #
101
+ # It does not make sense to attempt to apply field defaults to simple
102
+ # type array entries via +:field_default+; don't do this.
103
+ #
104
+ # In the case of <tt>:type => :array</tt>, the declaring Array is
105
+ # saying that its entries are themselves individually Arrays. This means
106
+ # that validation will ensure and rendering will assume that each of the
107
+ # parent Array entries are themselves Arrays, but will not validte the
108
+ # child Array contents any further. It is not possible to declare an
109
+ # Array with a child Array that has further children, or has child-level
110
+ # validation; instead you would need to use the block syntax, so that
111
+ # the child Array was associated to some named key in the arising
112
+ # Object/Hash making up each of the parent entries.
113
+ #
114
+ # == Block syntax example
115
+ #
116
+ # Mandatory JSON field "currencies" would lead to an array
67
117
  # where each array entry contains the fields defined by
68
118
  # Hoodoo::Data::Types::Currency along with an up-to-32 character string
69
119
  # with field name "notes", that field also being required. Whether or not
@@ -79,6 +129,41 @@ module Hoodoo
79
129
  # end
80
130
  # end
81
131
  #
132
+ # == Simple type syntax without field options
133
+ #
134
+ # An optional Array which consists of simple UUIDs as its entries:
135
+ #
136
+ # class UUIDCollection < Hoodoo::Presenters::Base
137
+ # schema do
138
+ # array :uuids, :type => :uuid
139
+ # end
140
+ # end
141
+ #
142
+ # # E.g.:
143
+ # #
144
+ # # {
145
+ # # "uuids" => [ "...uuid...", "...uuid...", ... ]
146
+ # # }
147
+ #
148
+ # Validation of data intended to be rendered through such a schema
149
+ # declaration would make sure that each array entry was UUID-like.
150
+ #
151
+ # == Simple type syntax with field options
152
+ #
153
+ # An optional Array which consists of Decimals with precision 2:
154
+ #
155
+ # class DecimalCollection < Hoodoo::Presenters::Base
156
+ # schema do
157
+ # array :numbers, :type => :decimal, :field_precision => 2
158
+ # end
159
+ # end
160
+ #
161
+ # # E.g.:
162
+ # #
163
+ # # {
164
+ # # "numbers" => [ BigDecimal.new( '2.2511' ) ]
165
+ # # }
166
+ #
82
167
  def array( name, options = {}, &block )
83
168
  ary = property( name, Hoodoo::Presenters::Array, options, &block )
84
169
  internationalised() if ary.is_internationalised?()
@@ -89,11 +174,13 @@ module Hoodoo
89
174
  # that the object may contain, in abstract terms.
90
175
  #
91
176
  # +name+:: The JSON key
92
- # +options+:: A +Hash+ of options, e.g. :required => true
177
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt>
93
178
  # &block:: Optional block declaring the fields making up the nested
94
179
  # hash
95
180
  #
96
- # == Example 1
181
+ # == Block-based complex type examples
182
+ #
183
+ # === Example 1
97
184
  #
98
185
  # A Hash where keys must be <= 16 characters long and values must match
99
186
  # a <tt>Hoodoo::Data::Types::Currency</tt> type (with the default
@@ -112,7 +199,7 @@ module Hoodoo
112
199
  #
113
200
  # See Hoodoo::Presenters::Hash#keys for more information and examples.
114
201
  #
115
- # == Example 2
202
+ # === Example 2
116
203
  #
117
204
  # A Hash where keys must be 'one' or 'two', each with a value matching
118
205
  # the given schema. Here, the example assumes that a subclass of
@@ -137,68 +224,104 @@ module Hoodoo
137
224
  #
138
225
  # See Hoodoo::Presenters::Hash#key for more information and examples.
139
226
  #
140
- # == Limitations
227
+ # == Simple types
228
+ #
229
+ # As with #array, simple types can be declared for Hash key values by
230
+ # passing a +:type+ option to Hoodoo::Presenters::Hash#key or
231
+ # Hoodoo::Presenters::Hash#keys. See the #array documentation for a list
232
+ # of permitted types.
141
233
  #
142
- # The syntax cannot express simple value types. It always describes a
143
- # nested object. So, the following describes a Hash called +payload+
144
- # which has arbitrary keys each leading to a nested _object_ with
145
- # key/value pairs where the key is called +some_value+ and the value
146
- # is an arbitrary length String:
234
+ # For individual specific keys in Hoodoo::Presenters::Hash#key, it
235
+ # _does_ make sense sometimes to specify field defaults using either a
236
+ # +:default+ or +:field_default+ key (they are synonyms). For arbitrary
237
+ # keys via Hoodoo::Presenters::Hash#keys the situation is the same as
238
+ # with array entries and it does _not_ make sense to specify field
239
+ # defaults.
147
240
  #
148
- # class NotSoSimpleHash < Hoodoo::Presenters::Base
241
+ # === Simple type example
242
+ #
243
+ # class Person < Hoodoo::Presenters::Base
149
244
  # schema do
150
- # hash :payload do
151
- # keys do
152
- # text :some_value
153
- # end
245
+ # hash :name do
246
+ # key :first, :type => :text
247
+ # key :last, :type => :text
248
+ # end
249
+ #
250
+ # hash :address do
251
+ # keys :type => :text
252
+ # end
253
+ #
254
+ # hash :identifiers, :required => true do
255
+ # keys :length => 8, :type => :string, :field_length => 32
154
256
  # end
155
257
  # end
156
258
  # end
157
259
  #
158
- # This is a valid piece of Ruby input data for the above which will
159
- # render without changes and validate successfully:
260
+ # The optional Hash called +name+ has two optional keys which must be
261
+ # called +first+ or +last+ and have values that conform to
262
+ # Hoodoo::Presenters::Text.
160
263
  #
161
- # data = {
162
- # "payload" => {
163
- # "any_key_name" => { "some_value" => "Any string" },
164
- # "another_key_name" => { "some_value" => "Another string" },
165
- # }
166
- # }
264
+ # The optional Hash called +address+ has arbitrarily named unbounded
265
+ # length keys which where present must conform to
266
+ # Hoodoo::Presenters::Text.
167
267
  #
168
- # NotSoSimpleHash.validate( data )
169
- # # => []
268
+ # The required Hash called +identifiers+ hash arbitrarily named keys
269
+ # with a maximum length of 8 characters which must have values that
270
+ # conform to Hoodoo::Presenters::String and are each no more than
271
+ # 32 characters long.
170
272
  #
171
- # This is invalid because one of the values is not a String:
273
+ # Therefore the following payload is valid:
172
274
  #
173
275
  # data = {
174
- # "payload" => {
175
- # "any_key_name" => { "some_value" => "Any string" },
176
- # "another_key_name" => { "some_value" => 22 },
276
+ # "name" => {
277
+ # "first" => "Test",
278
+ # "last" => "Testy"
279
+ # },
280
+ # "address" => {
281
+ # "road" => "1 Test Street",
282
+ # "city" => "Testville",
283
+ # "post_code" => "T01 C41"
284
+ # },
285
+ # "identifiers" => {
286
+ # "primary" => "9759c77d188f4bfe85959738dc6f8505",
287
+ # "postgres" => "1442"
177
288
  # }
178
289
  # }
179
290
  #
180
- # NotSoSimpleHash.validate( data )
181
- # # => [{"code"=>"generic.invalid_string",
182
- # # "message"=>"Field `payload.another_key_name.some_value` is an invalid string",
183
- # # "reference"=>"payload.another_key_name.some_value"}]
291
+ # Person.validate( data )
292
+ # # => []
184
293
  #
185
- # This is invalid because the DSL cannot express a simple String value
186
- # for the keys:
294
+ # The following example contains numerous mistakes:
187
295
  #
188
296
  # data = {
189
- # "payload" => {
190
- # "any_key_name" => "Any string",
191
- # "another_key_name" => "Another string",
297
+ # "name" => {
298
+ # "first" => "Test",
299
+ # "surname" => "Testy" # Invalid key name
300
+ # },
301
+ # "address" => {
302
+ # "road" => "1 Test Street",
303
+ # "city" => "Testville",
304
+ # "zip" => 90421 # Integer, not Text
305
+ # },
306
+ # "identifiers" => {
307
+ # "primary" => "9759c77d188f4bfe85959738dc6f8505_441", # Value too long
308
+ # "postgresql" => "1442" # Key name too long
192
309
  # }
193
310
  # }
194
311
  #
195
- # NotSoSimpleHash.validate( data )
196
- # # => [{"code"=>"generic.invalid_object",
197
- # # "message"=>"Field `payload.any_key_name` is an invalid object",
198
- # # "reference"=>"payload.any_key_name"},
199
- # # {"code"=>"generic.invalid_object",
200
- # # "message"=>"Field `payload.another_key_name` is an invalid object",
201
- # # "reference"=>"payload.another_key_name"}]
312
+ # Person.validate( data )
313
+ # # => [{"code"=>"generic.invalid_hash",
314
+ # # "message"=>"Field `name` is an invalid hash due to unrecognised keys `surname`",
315
+ # # "reference"=>"name"},
316
+ # # {"code"=>"generic.invalid_string",
317
+ # # "message"=>"Field `address.zip` is an invalid string",
318
+ # # "reference"=>"address.zip"},
319
+ # # {"code"=>"generic.invalid_string",
320
+ # # "message"=>"Field `identifiers.primary` is longer than maximum length `32`",
321
+ # # "reference"=>"identifiers.primary"},
322
+ # # {"code"=>"generic.invalid_string",
323
+ # # "message"=>"Field `identifiers.postgresql` is longer than maximum length `8`",
324
+ # # "reference"=>"identifiers.postgresql"}]
202
325
  #
203
326
  def hash( name, options = {}, &block )
204
327
  hash = property( name, Hoodoo::Presenters::Hash, options, &block )
@@ -207,8 +330,8 @@ module Hoodoo
207
330
 
208
331
  # Define a JSON integer with the supplied name and options.
209
332
  #
210
- # +name+:: The JSON key
211
- # +options+:: A +Hash+ of options, e.g. :required => true
333
+ # +name+:: The JSON key
334
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt>
212
335
  #
213
336
  def integer( name, options = {} )
214
337
  property( name, Hoodoo::Presenters::Integer, options )
@@ -216,8 +339,9 @@ module Hoodoo
216
339
 
217
340
  # Define a JSON string with the supplied name and options.
218
341
  #
219
- # +name+:: The JSON key
220
- # +options+:: A +Hash+ of options, e.g. :required => true, :length => 10
342
+ # +name+:: The JSON key
343
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt> and
344
+ # mandatory <tt>:length => [max-length-in-chars]</tt>
221
345
  #
222
346
  def string( name, options = {} )
223
347
  property( name, Hoodoo::Presenters::String, options )
@@ -225,8 +349,8 @@ module Hoodoo
225
349
 
226
350
  # Define a JSON float with the supplied name and options.
227
351
  #
228
- # +name+:: The JSON key
229
- # +options+:: A +Hash+ of options, e.g. :required => true
352
+ # +name+:: The JSON key
353
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt>
230
354
  #
231
355
  def float( name, options = {} )
232
356
  property( name, Hoodoo::Presenters::Float, options )
@@ -234,8 +358,9 @@ module Hoodoo
234
358
 
235
359
  # Define a JSON decimal with the supplied name and options.
236
360
  #
237
- # +name+:: The JSON key
238
- # +options+:: A +Hash+ of options, e.g. :required => true, :precision => 10
361
+ # +name+:: The JSON key
362
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt> and
363
+ # mandatory <tt>:precision => [decimal-precision-number]</tt>
239
364
  #
240
365
  def decimal( name, options = {} )
241
366
  property( name, Hoodoo::Presenters::Decimal, options )
@@ -243,8 +368,8 @@ module Hoodoo
243
368
 
244
369
  # Define a JSON boolean with the supplied name and options.
245
370
  #
246
- # +name+:: The JSON key
247
- # +options+:: A +Hash+ of options, e.g. :required => true
371
+ # +name+:: The JSON key
372
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt>
248
373
  #
249
374
  def boolean( name, options = {} )
250
375
  property( name, Hoodoo::Presenters::Boolean, options )
@@ -252,8 +377,8 @@ module Hoodoo
252
377
 
253
378
  # Define a JSON date with the supplied name and options.
254
379
  #
255
- # +name+:: The JSON key
256
- # +options+:: A +Hash+ of options, e.g. :required => true
380
+ # +name+:: The JSON key
381
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt>
257
382
  #
258
383
  def date( name, options = {} )
259
384
  property( name, Hoodoo::Presenters::Date, options )
@@ -261,8 +386,8 @@ module Hoodoo
261
386
 
262
387
  # Define a JSON datetime with the supplied name and options.
263
388
  #
264
- # +name+:: The JSON key
265
- # +options+:: A +Hash+ of options, e.g. :required => true
389
+ # +name+:: The JSON key
390
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt>
266
391
  #
267
392
  def datetime( name, options = {} )
268
393
  property( name, Hoodoo::Presenters::DateTime, options )
@@ -271,8 +396,8 @@ module Hoodoo
271
396
  # Define a JSON string of unlimited length with the supplied name
272
397
  # and options.
273
398
  #
274
- # +name+:: The JSON key
275
- # +options+:: A +Hash+ of options, e.g. :required => true
399
+ # +name+:: The JSON key
400
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt>
276
401
  #
277
402
  def text( name, options = {} )
278
403
  property( name, Hoodoo::Presenters::Text, options )
@@ -281,9 +406,10 @@ module Hoodoo
281
406
  # Define a JSON string which can only have a restricted set of exactly
282
407
  # matched values, with the supplied name and options.
283
408
  #
284
- # +name+:: The JSON key
285
- # +options+:: A +Hash+ of options, e.g. :required => true and mandatory
286
- # :from => [array-of-allowed-strings-or-symbols]
409
+ # +name+:: The JSON key
410
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt> and
411
+ # mandatory
412
+ # <tt>:from => [array-of-allowed-strings-or-symbols]</tt>
287
413
  #
288
414
  def enum( name, options = {} )
289
415
  property( name, Hoodoo::Presenters::Enum, options )
@@ -293,7 +419,7 @@ module Hoodoo
293
419
  # length that contains comma-separated tag strings.
294
420
  #
295
421
  # +field_name+:: Name of the field that will hold the tags.
296
- # +options+:: Optional options hash. See Hoodoo::Presenters::BaseDSL.
422
+ # +options+:: Optional options hash. See Hoodoo::Presenters::BaseDSL.
297
423
  #
298
424
  # Example - a Product resource which supports product tagging:
299
425
  #
@@ -573,7 +699,7 @@ module Hoodoo
573
699
  #
574
700
  # +name+:: The JSON key
575
701
  # +type+:: A +Class+ for validation
576
- # +options+:: A +Hash+ of options, e.g. :required => true
702
+ # +options+:: A +Hash+ of options, e.g. <tt>:required => true</tt>
577
703
  #
578
704
  def property( name, type, options = {}, &block )
579
705
  name = name.to_s
@@ -586,6 +712,94 @@ module Hoodoo
586
712
  return inst
587
713
  end
588
714
 
715
+ # Given a <tt>:type</tt> option key, take the value (which must be a
716
+ # Symbol) and convert it to a class variable for known cases, else
717
+ # raise an exception. Used as a back-end for methods such as
718
+ # Hoodoo::Presenters::Hash#key which support value type validation.
719
+ #
720
+ # +type+:: Required class type expressed as a Symbol (see below).
721
+ #
722
+ # Supported values for the +type+ parameter are:
723
+ #
724
+ # [nil]
725
+ # Hoodoo::Presenters::Field generic class
726
+ # [:array]
727
+ # Hoodoo::Presenters::Array
728
+ # [:boolean]
729
+ # Hoodoo::Presenters::Boolean
730
+ # [:date]
731
+ # Hoodoo::Presenters::Date
732
+ # [:date_time]
733
+ # Hoodoo::Presenters::DateTime
734
+ # [:decimal]
735
+ # Hoodoo::Presenters::Decimal
736
+ # [:enum]
737
+ # Hoodoo::Presenters::Enum
738
+ # [:float]
739
+ # Hoodoo::Presenters::Float
740
+ # [:integer]
741
+ # Hoodoo::Presenters::Integer
742
+ # [:string]
743
+ # Hoodoo::Presenters::String
744
+ # [:tags]
745
+ # Hoodoo::Presenters::Tags
746
+ # [:text]
747
+ # Hoodoo::Presenters::Text
748
+ # [:uuid]
749
+ # Hoodoo::Presenters::UUID
750
+ #
751
+ def type_option_to_class( type )
752
+ case type
753
+ when nil
754
+ Hoodoo::Presenters::Field
755
+ when :array
756
+ Hoodoo::Presenters::Array
757
+ when :boolean
758
+ Hoodoo::Presenters::Boolean
759
+ when :date
760
+ Hoodoo::Presenters::Date
761
+ when :date_time
762
+ Hoodoo::Presenters::DateTime
763
+ when :decimal
764
+ Hoodoo::Presenters::Decimal
765
+ when :enum
766
+ Hoodoo::Presenters::Enum
767
+ when :float
768
+ Hoodoo::Presenters::Float
769
+ when :integer
770
+ Hoodoo::Presenters::Integer
771
+ when :string
772
+ Hoodoo::Presenters::String
773
+ when :tags
774
+ Hoodoo::Presenters::Tags
775
+ when :text
776
+ Hoodoo::Presenters::Text
777
+ when :uuid
778
+ Hoodoo::Presenters::UUID
779
+ else
780
+ raise "Unsupported 'type' option value of '#{ type }' in Hoodoo::Presenters::BaseDSL"
781
+ end
782
+ end
783
+
784
+ # Given a Hash with keys as Symbols or Strings prefixed by the string
785
+ # "field_", return a Hash with those items kept but keys renamed without
786
+ # that "field_" prefix and with all other items removed. The keys in the
787
+ # returned Hash are all coerced to Symbols regardless of input class.
788
+ #
789
+ # +options+:: Hash from which to extract field-specific data.
790
+ #
791
+ def extract_field_prefix_options_from( options )
792
+ options.inject( {} ) do | hash, key_value_pair_array |
793
+ key = key_value_pair_array[ 0 ].to_s
794
+
795
+ if key.start_with?( 'field_' )
796
+ hash[ key.sub( /^field_/, '' ).to_sym ] = key_value_pair_array[ 1 ]
797
+ end
798
+
799
+ hash
800
+ end
801
+ end
802
+
589
803
  end
590
804
  end
591
805
  end