dry-types 0.15.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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