dry-types 0.15.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +547 -161
  3. data/LICENSE +17 -17
  4. data/README.md +15 -13
  5. data/dry-types.gemspec +27 -30
  6. data/lib/dry/types/any.rb +23 -12
  7. data/lib/dry/types/array/constructor.rb +32 -0
  8. data/lib/dry/types/array/member.rb +74 -15
  9. data/lib/dry/types/array.rb +18 -2
  10. data/lib/dry/types/builder.rb +118 -22
  11. data/lib/dry/types/builder_methods.rb +46 -16
  12. data/lib/dry/types/coercions/json.rb +43 -7
  13. data/lib/dry/types/coercions/params.rb +117 -32
  14. data/lib/dry/types/coercions.rb +76 -22
  15. data/lib/dry/types/compiler.rb +44 -21
  16. data/lib/dry/types/constrained/coercible.rb +36 -6
  17. data/lib/dry/types/constrained.rb +79 -31
  18. data/lib/dry/types/constraints.rb +18 -4
  19. data/lib/dry/types/constructor/function.rb +216 -0
  20. data/lib/dry/types/constructor/wrapper.rb +94 -0
  21. data/lib/dry/types/constructor.rb +110 -61
  22. data/lib/dry/types/container.rb +6 -1
  23. data/lib/dry/types/core.rb +34 -11
  24. data/lib/dry/types/decorator.rb +38 -17
  25. data/lib/dry/types/default.rb +61 -16
  26. data/lib/dry/types/enum.rb +36 -20
  27. data/lib/dry/types/errors.rb +74 -8
  28. data/lib/dry/types/extensions/maybe.rb +65 -17
  29. data/lib/dry/types/extensions/monads.rb +29 -0
  30. data/lib/dry/types/extensions.rb +7 -1
  31. data/lib/dry/types/fn_container.rb +6 -1
  32. data/lib/dry/types/hash/constructor.rb +17 -4
  33. data/lib/dry/types/hash.rb +32 -20
  34. data/lib/dry/types/inflector.rb +3 -1
  35. data/lib/dry/types/json.rb +18 -16
  36. data/lib/dry/types/lax.rb +75 -0
  37. data/lib/dry/types/map.rb +70 -32
  38. data/lib/dry/types/meta.rb +51 -0
  39. data/lib/dry/types/module.rb +16 -11
  40. data/lib/dry/types/nominal.rb +113 -22
  41. data/lib/dry/types/options.rb +12 -25
  42. data/lib/dry/types/params.rb +39 -25
  43. data/lib/dry/types/predicate_inferrer.rb +238 -0
  44. data/lib/dry/types/predicate_registry.rb +34 -0
  45. data/lib/dry/types/primitive_inferrer.rb +97 -0
  46. data/lib/dry/types/printable.rb +5 -1
  47. data/lib/dry/types/printer.rb +63 -57
  48. data/lib/dry/types/result.rb +29 -3
  49. data/lib/dry/types/schema/key.rb +62 -36
  50. data/lib/dry/types/schema.rb +201 -91
  51. data/lib/dry/types/spec/types.rb +99 -37
  52. data/lib/dry/types/sum.rb +75 -25
  53. data/lib/dry/types/type.rb +49 -0
  54. data/lib/dry/types/version.rb +3 -1
  55. data/lib/dry/types.rb +106 -48
  56. data/lib/dry-types.rb +3 -1
  57. metadata +55 -78
  58. data/.codeclimate.yml +0 -15
  59. data/.gitignore +0 -10
  60. data/.rspec +0 -2
  61. data/.rubocop.yml +0 -43
  62. data/.travis.yml +0 -28
  63. data/.yardopts +0 -5
  64. data/CONTRIBUTING.md +0 -29
  65. data/Gemfile +0 -23
  66. data/Rakefile +0 -20
  67. data/benchmarks/hash_schemas.rb +0 -51
  68. data/lib/dry/types/safe.rb +0 -61
  69. data/log/.gitkeep +0 -0
@@ -1,11 +1,18 @@
1
- require 'dry/types/decorator'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/equalizer"
4
+ require "dry/types/decorator"
2
5
 
3
6
  module Dry
4
7
  module Types
8
+ # Enum types can be used to define an enum on top of an existing type
9
+ #
10
+ # @api public
5
11
  class Enum
6
12
  include Type
7
- include Dry::Equalizer(:type, :options, :mapping, inspect: false)
13
+ include Dry::Equalizer(:type, :mapping, inspect: false, immutable: true)
8
14
  include Decorator
15
+ include Builder
9
16
 
10
17
  # @return [Array]
11
18
  attr_reader :values
@@ -19,7 +26,9 @@ module Dry
19
26
  # @param [Type] type
20
27
  # @param [Hash] options
21
28
  # @option options [Array] :values
22
- def initialize(type, options)
29
+ #
30
+ # @api private
31
+ def initialize(type, **options)
23
32
  super
24
33
  @mapping = options.fetch(:mapping).freeze
25
34
  @values = @mapping.keys.freeze
@@ -27,40 +36,45 @@ module Dry
27
36
  freeze
28
37
  end
29
38
 
30
- # @param [Object] input
31
39
  # @return [Object]
32
- def call(input = Undefined)
33
- type[map_value(input)]
40
+ #
41
+ # @api private
42
+ def call_unsafe(input)
43
+ type.call_unsafe(map_value(input))
44
+ end
45
+
46
+ # @return [Object]
47
+ #
48
+ # @api private
49
+ def call_safe(input, &block)
50
+ type.call_safe(map_value(input), &block)
34
51
  end
35
- alias_method :[], :call
36
52
 
37
- # @param [Object] input
38
- # @yieldparam [Failure] failure
39
- # @yieldreturn [Result]
40
- # @return [Logic::Result]
41
- # @return [Object] if coercion fails and a block is given
53
+ # @see Dry::Types::Constrained#try
54
+ #
55
+ # @api public
42
56
  def try(input)
43
57
  super(map_value(input))
44
58
  end
45
59
 
60
+ # @api private
46
61
  def default(*)
47
- raise '.enum(*values).default(value) is not supported. Call '\
48
- '.default(value).enum(*values) instead'
62
+ raise ".enum(*values).default(value) is not supported. Call "\
63
+ ".default(value).enum(*values) instead"
49
64
  end
50
65
 
51
66
  # Check whether a value is in the enum
52
67
  alias_method :include?, :valid?
53
68
 
54
- # @api public
55
- #
56
69
  # @see Nominal#to_ast
70
+ #
71
+ # @api public
57
72
  def to_ast(meta: true)
58
- [:enum, [type.to_ast(meta: meta),
59
- mapping,
60
- meta ? self.meta : EMPTY_HASH]]
73
+ [:enum, [type.to_ast(meta: meta), mapping]]
61
74
  end
62
75
 
63
76
  # @return [String]
77
+ #
64
78
  # @api public
65
79
  def to_s
66
80
  PRINTER.(self)
@@ -71,8 +85,10 @@ module Dry
71
85
 
72
86
  # Maps a value
73
87
  #
74
- # @param [Object]
88
+ # @param [Object] input
89
+ #
75
90
  # @return [Object]
91
+ #
76
92
  # @api private
77
93
  def map_value(input)
78
94
  if input.equal?(Undefined)
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Types
3
- extend Dry::Core::ClassAttributes
5
+ extend ::Dry::Core::ClassAttributes
4
6
 
5
7
  # @!attribute [r] namespace
6
8
  # @return [Container{String => Nominal}]
@@ -8,7 +10,62 @@ module Dry
8
10
 
9
11
  namespace self
10
12
 
11
- class SchemaError < TypeError
13
+ # Base class for coercion errors raise by dry-types
14
+ #
15
+ class CoercionError < ::StandardError
16
+ # @api private
17
+ def self.handle(exception, meta: Undefined)
18
+ if block_given?
19
+ yield
20
+ else
21
+ raise new(
22
+ exception.message,
23
+ meta: meta,
24
+ backtrace: exception.backtrace
25
+ )
26
+ end
27
+ end
28
+
29
+ # Metadata associated with the error
30
+ #
31
+ # @return [Object]
32
+ attr_reader :meta
33
+
34
+ # @api private
35
+ def initialize(message, meta: Undefined, backtrace: Undefined)
36
+ unless message.is_a?(::String)
37
+ raise ::ArgumentError, "message must be a string, #{message.class} given"
38
+ end
39
+
40
+ super(message)
41
+ @meta = Undefined.default(meta, nil)
42
+ set_backtrace(backtrace) unless Undefined.equal?(backtrace)
43
+ end
44
+ end
45
+
46
+ # Collection of multiple errors
47
+ #
48
+ class MultipleError < CoercionError
49
+ # @return [Array<CoercionError>]
50
+ attr_reader :errors
51
+
52
+ # @param [Array<CoercionError>] errors
53
+ def initialize(errors)
54
+ @errors = errors
55
+ end
56
+
57
+ # @return string
58
+ def message
59
+ errors.map(&:message).join(", ")
60
+ end
61
+
62
+ # @return [Array]
63
+ def meta
64
+ errors.map(&:meta)
65
+ end
66
+ end
67
+
68
+ class SchemaError < CoercionError
12
69
  # @param [String,Symbol] key
13
70
  # @param [Object] value
14
71
  # @param [String, #to_s] result
@@ -17,26 +74,34 @@ module Dry
17
74
  end
18
75
  end
19
76
 
20
- MapError = Class.new(TypeError)
77
+ MapError = ::Class.new(CoercionError)
21
78
 
22
- SchemaKeyError = Class.new(KeyError)
79
+ SchemaKeyError = ::Class.new(CoercionError)
23
80
  private_constant(:SchemaKeyError)
24
81
 
25
82
  class MissingKeyError < SchemaKeyError
83
+ # @return [Symbol]
84
+ attr_reader :key
85
+
26
86
  # @param [String,Symbol] key
27
87
  def initialize(key)
28
- super(":#{key} is missing in Hash input")
88
+ @key = key
89
+ super("#{key.inspect} is missing in Hash input")
29
90
  end
30
91
  end
31
92
 
32
93
  class UnknownKeysError < SchemaKeyError
94
+ # @return [Array<Symbol>]
95
+ attr_reader :keys
96
+
33
97
  # @param [<String, Symbol>] keys
34
- def initialize(*keys)
98
+ def initialize(keys)
99
+ @keys = keys
35
100
  super("unexpected keys #{keys.inspect} in Hash input")
36
101
  end
37
102
  end
38
103
 
39
- class ConstraintError < TypeError
104
+ class ConstraintError < CoercionError
40
105
  # @return [String, #to_s]
41
106
  attr_reader :result
42
107
  # @return [Object]
@@ -56,9 +121,10 @@ module Dry
56
121
  end
57
122
 
58
123
  # @return [String]
59
- def to_s
124
+ def message
60
125
  "#{input.inspect} violates constraints (#{result} failed)"
61
126
  end
127
+ alias_method :to_s, :message
62
128
  end
63
129
  end
64
130
  end
@@ -1,49 +1,83 @@
1
- require 'dry/monads/maybe'
2
- require 'dry/types/decorator'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/equalizer"
4
+ require "dry/monads/maybe"
5
+ require "dry/types/decorator"
3
6
 
4
7
  module Dry
5
8
  module Types
9
+ # Maybe extension provides Maybe types where values are wrapped using `Either` monad
10
+ #
11
+ # @api public
6
12
  class Maybe
7
13
  include Type
8
- include Dry::Equalizer(:type, :options, inspect: false)
14
+ include ::Dry::Equalizer(:type, :options, inspect: false, immutable: true)
9
15
  include Decorator
10
16
  include Builder
11
- include Dry::Monads::Maybe::Mixin
17
+ include Printable
18
+ include ::Dry::Monads::Maybe::Mixin
12
19
 
13
20
  # @param [Dry::Monads::Maybe, Object] input
21
+ #
14
22
  # @return [Dry::Monads::Maybe]
15
- def call(input = Undefined)
23
+ #
24
+ # @api private
25
+ def call_unsafe(input = Undefined)
16
26
  case input
17
- when Dry::Monads::Maybe
27
+ when ::Dry::Monads::Maybe
18
28
  input
19
29
  when Undefined
20
30
  None()
21
31
  else
22
- Maybe(type[input])
32
+ Maybe(type.call_unsafe(input))
33
+ end
34
+ end
35
+
36
+ # @param [Dry::Monads::Maybe, Object] input
37
+ #
38
+ # @return [Dry::Monads::Maybe]
39
+ #
40
+ # @api private
41
+ def call_safe(input = Undefined)
42
+ case input
43
+ when ::Dry::Monads::Maybe
44
+ input
45
+ when Undefined
46
+ None()
47
+ else
48
+ Maybe(type.call_safe(input) { |output = input| return yield(output) })
23
49
  end
24
50
  end
25
- alias_method :[], :call
26
51
 
27
52
  # @param [Object] input
53
+ #
28
54
  # @return [Result::Success]
55
+ #
56
+ # @api public
29
57
  def try(input = Undefined)
30
- res = if input.equal?(Undefined)
31
- None()
32
- else
33
- Maybe(type[input])
34
- end
58
+ result = type.try(input)
35
59
 
36
- Result::Success.new(res)
60
+ if result.success?
61
+ Result::Success.new(Maybe(result.input))
62
+ else
63
+ result
64
+ end
37
65
  end
38
66
 
39
67
  # @return [true]
68
+ #
69
+ # @api public
40
70
  def default?
41
71
  true
42
72
  end
43
73
 
44
74
  # @param [Object] value
75
+ #
45
76
  # @see Dry::Types::Builder#default
77
+ #
46
78
  # @raise [ArgumentError] if nil provided as default value
79
+ #
80
+ # @api public
47
81
  def default(value)
48
82
  if value.nil?
49
83
  raise ArgumentError, "nil cannot be used as a default of a maybe type"
@@ -54,25 +88,39 @@ module Dry
54
88
  end
55
89
 
56
90
  module Builder
91
+ # Turn a type into a maybe type
92
+ #
57
93
  # @return [Maybe]
94
+ #
95
+ # @api public
96
+ def maybe
97
+ Maybe.new(Types["nil"] | self)
98
+ end
99
+ end
100
+
101
+ # @api private
102
+ class Schema::Key
103
+ # @api private
58
104
  def maybe
59
- Maybe.new(Types['strict.nil'] | self)
105
+ __new__(type.maybe)
60
106
  end
61
107
  end
62
108
 
109
+ # @api private
63
110
  class Printer
64
111
  MAPPING[Maybe] = :visit_maybe
65
112
 
113
+ # @api private
66
114
  def visit_maybe(maybe)
67
115
  visit(maybe.type) do |type|
68
- yield "Maybe<#{ type }>"
116
+ yield "Maybe<#{type}>"
69
117
  end
70
118
  end
71
119
  end
72
120
 
73
121
  # Register non-coercible maybe types
74
122
  NON_NIL.each_key do |name|
75
- register("maybe.strict.#{name}", self["strict.#{name}"].maybe)
123
+ register("maybe.strict.#{name}", self[name.to_s].maybe)
76
124
  end
77
125
 
78
126
  # Register coercible maybe types
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/monads/result"
4
+
5
+ module Dry
6
+ module Types
7
+ # Monad extension for Result
8
+ #
9
+ # @api public
10
+ class Result
11
+ include Dry::Monads::Result::Mixin
12
+
13
+ # Turn result into a monad
14
+ #
15
+ # This makes result objects work with dry-monads (or anything with a compatible interface)
16
+ #
17
+ # @return [Dry::Monads::Success,Dry::Monads::Failure]
18
+ #
19
+ # @api public
20
+ def to_monad
21
+ if success?
22
+ Success(input)
23
+ else
24
+ Failure([error, input])
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Dry::Types.register_extension(:maybe) do
2
- require 'dry/types/extensions/maybe'
4
+ require "dry/types/extensions/maybe"
5
+ end
6
+
7
+ Dry::Types.register_extension(:monads) do
8
+ require "dry/types/extensions/monads"
3
9
  end
@@ -1,7 +1,12 @@
1
- require 'dry/types/container'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/types/container"
2
4
 
3
5
  module Dry
4
6
  module Types
7
+ # Internal container for constructor functions used by the built-in types
8
+ #
9
+ # @api private
5
10
  class FnContainer
6
11
  # @api private
7
12
  def self.container
@@ -1,7 +1,12 @@
1
- require 'dry/types/constructor'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/types/constructor"
2
4
 
3
5
  module Dry
4
6
  module Types
7
+ # Hash type exposes additional APIs for working with schema hashes
8
+ #
9
+ # @api public
5
10
  class Hash < Nominal
6
11
  class Constructor < ::Dry::Types::Constructor
7
12
  # @api private
@@ -9,10 +14,18 @@ module Dry
9
14
  ::Dry::Types::Hash::Constructor
10
15
  end
11
16
 
12
- private
17
+ # @return [Lax]
18
+ #
19
+ # @api public
20
+ def lax
21
+ type.lax.constructor(fn, meta: meta)
22
+ end
13
23
 
14
- def composable?(value)
15
- super && !value.is_a?(Schema::Key)
24
+ # @see Dry::Types::Array#of
25
+ #
26
+ # @api public
27
+ def schema(*args)
28
+ type.schema(*args).constructor(fn, meta: meta)
16
29
  end
17
30
  end
18
31
  end
@@ -1,18 +1,26 @@
1
- require 'dry/types/hash/constructor'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/types/hash/constructor"
2
4
 
3
5
  module Dry
4
6
  module Types
7
+ # Hash types can be used to define maps and schemas
8
+ #
9
+ # @api public
5
10
  class Hash < Nominal
6
- NOT_REQUIRED = { required: false }.freeze
11
+ NOT_REQUIRED = {required: false}.freeze
7
12
 
8
- # @overload schmea(type_map, meta = EMPTY_HASH)
13
+ # @overload schema(type_map, meta = EMPTY_HASH)
9
14
  # @param [{Symbol => Dry::Types::Nominal}] type_map
10
15
  # @param [Hash] meta
11
16
  # @return [Dry::Types::Schema]
17
+ #
12
18
  # @overload schema(keys)
13
19
  # @param [Array<Dry::Types::Schema::Key>] key List of schema keys
14
20
  # @param [Hash] meta
15
21
  # @return [Dry::Types::Schema]
22
+ #
23
+ # @api public
16
24
  def schema(keys_or_map, meta = EMPTY_HASH)
17
25
  if keys_or_map.is_a?(::Array)
18
26
  keys = keys_or_map
@@ -27,7 +35,10 @@ module Dry
27
35
  #
28
36
  # @param [Type] key_type
29
37
  # @param [Type] value_type
38
+ #
30
39
  # @return [Map]
40
+ #
41
+ # @api public
31
42
  def map(key_type, value_type)
32
43
  Map.new(
33
44
  primitive,
@@ -37,8 +48,7 @@ module Dry
37
48
  )
38
49
  end
39
50
 
40
- # @param [{Symbol => Nominal}] type_map
41
- # @return [Schema]
51
+ # @api private
42
52
  def weak(*)
43
53
  raise "Support for old hash schemas was removed, please refer to the CHANGELOG "\
44
54
  "on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md"
@@ -49,15 +59,17 @@ module Dry
49
59
  alias_method :symbolized, :weak
50
60
 
51
61
  # Injects a type transformation function for building schemas
62
+ #
52
63
  # @param [#call,nil] proc
53
64
  # @param [#call,nil] block
65
+ #
54
66
  # @return [Hash]
67
+ #
68
+ # @api public
55
69
  def with_type_transform(proc = nil, &block)
56
70
  fn = proc || block
57
71
 
58
- if fn.nil?
59
- raise ArgumentError, "a block or callable argument is required"
60
- end
72
+ raise ArgumentError, "a block or callable argument is required" if fn.nil?
61
73
 
62
74
  handle = Dry::Types::FnContainer.register(fn)
63
75
  with(type_transform_fn: handle)
@@ -69,22 +81,21 @@ module Dry
69
81
  end
70
82
 
71
83
  # Whether the type transforms types of schemas created by {Dry::Types::Hash#schema}
84
+ #
72
85
  # @return [Boolean]
86
+ #
73
87
  # @api public
74
88
  def transform_types?
75
89
  !options[:type_transform_fn].nil?
76
90
  end
77
91
 
78
92
  # @param meta [Boolean] Whether to dump the meta to the AST
93
+ #
79
94
  # @return [Array] An AST representation
95
+ #
96
+ # @api public
80
97
  def to_ast(meta: true)
81
- if RUBY_VERSION >= "2.5"
82
- opts = options.slice(:type_transform_fn)
83
- else
84
- opts = options.select { |k, _| k == :type_transform_fn }
85
- end
86
-
87
- [:hash, [opts, meta ? self.meta : EMPTY_HASH]]
98
+ [:hash, [options.slice(:type_transform_fn), meta ? self.meta : EMPTY_HASH]]
88
99
  end
89
100
 
90
101
  private
@@ -96,7 +107,7 @@ module Dry
96
107
 
97
108
  type_map.map do |map_key, type|
98
109
  name, options = key_name(map_key)
99
- key = Schema::Key.new(resolve_type(type), name, options)
110
+ key = Schema::Key.new(resolve_type(type), name, **options)
100
111
  type_transform.(key)
101
112
  end
102
113
  end
@@ -104,14 +115,15 @@ module Dry
104
115
  # @api private
105
116
  def resolve_type(type)
106
117
  case type
107
- when String, Class then Types[type]
118
+ when Type then type
119
+ when ::Class, ::String then Types[type]
108
120
  else type
109
121
  end
110
122
  end
111
123
 
112
124
  # @api private
113
125
  def key_name(key)
114
- if key.to_s.end_with?('?')
126
+ if key.to_s.end_with?("?")
115
127
  [key.to_s.chop.to_sym, NOT_REQUIRED]
116
128
  else
117
129
  [key, EMPTY_HASH]
@@ -121,5 +133,5 @@ module Dry
121
133
  end
122
134
  end
123
135
 
124
- require 'dry/types/schema/key'
125
- require 'dry/types/schema'
136
+ require "dry/types/schema/key"
137
+ require "dry/types/schema"
@@ -1,4 +1,6 @@
1
- require 'dry/inflector'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/inflector"
2
4
 
3
5
  module Dry
4
6
  module Types
@@ -1,33 +1,35 @@
1
- require 'dry/types/coercions/json'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/types/coercions/json"
2
4
 
3
5
  module Dry
4
6
  module Types
5
- register('json.nil') do
6
- self['nominal.nil'].constructor(Coercions::JSON.method(:to_nil))
7
+ register("json.nil") do
8
+ self["nominal.nil"].constructor(Coercions::JSON.method(:to_nil))
7
9
  end
8
10
 
9
- register('json.date') do
10
- self['nominal.date'].constructor(Coercions::JSON.method(:to_date))
11
+ register("json.date") do
12
+ self["nominal.date"].constructor(Coercions::JSON.method(:to_date))
11
13
  end
12
14
 
13
- register('json.date_time') do
14
- self['nominal.date_time'].constructor(Coercions::JSON.method(:to_date_time))
15
+ register("json.date_time") do
16
+ self["nominal.date_time"].constructor(Coercions::JSON.method(:to_date_time))
15
17
  end
16
18
 
17
- register('json.time') do
18
- self['nominal.time'].constructor(Coercions::JSON.method(:to_time))
19
+ register("json.time") do
20
+ self["nominal.time"].constructor(Coercions::JSON.method(:to_time))
19
21
  end
20
22
 
21
- register('json.decimal') do
22
- self['nominal.decimal'].constructor(Coercions::JSON.method(:to_decimal))
23
+ register("json.decimal") do
24
+ self["nominal.decimal"].constructor(Coercions::JSON.method(:to_decimal))
23
25
  end
24
26
 
25
- register('json.array') do
26
- self['nominal.array'].safe
27
+ register("json.symbol") do
28
+ self["nominal.symbol"].constructor(Coercions::JSON.method(:to_symbol))
27
29
  end
28
30
 
29
- register('json.hash') do
30
- self['nominal.hash'].safe
31
- end
31
+ register("json.array") { self["array"] }
32
+
33
+ register("json.hash") { self["hash"] }
32
34
  end
33
35
  end