File: hash.rb

package info (click to toggle)
ruby-dry-types 1.2.2-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 504 kB
  • sloc: ruby: 3,059; makefile: 4
file content (143 lines) | stat: -rw-r--r-- 3,787 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# frozen_string_literal: true

require 'dry/types/hash/constructor'

module Dry
  module Types
    # Hash types can be used to define maps and schemas
    #
    # @api public
    class Hash < Nominal
      NOT_REQUIRED = { required: false }.freeze

      # @overload schema(type_map, meta = EMPTY_HASH)
      #   @param [{Symbol => Dry::Types::Nominal}] type_map
      #   @param [Hash] meta
      #   @return [Dry::Types::Schema]
      #
      # @overload schema(keys)
      #   @param [Array<Dry::Types::Schema::Key>] key List of schema keys
      #   @param [Hash] meta
      #   @return [Dry::Types::Schema]
      #
      # @api public
      def schema(keys_or_map, meta = EMPTY_HASH)
        if keys_or_map.is_a?(::Array)
          keys = keys_or_map
        else
          keys = build_keys(keys_or_map)
        end

        Schema.new(primitive, keys: keys, **options, meta: self.meta.merge(meta))
      end

      # Build a map type
      #
      # @param [Type] key_type
      # @param [Type] value_type
      #
      # @return [Map]
      #
      # @api public
      def map(key_type, value_type)
        Map.new(
          primitive,
          key_type: resolve_type(key_type),
          value_type: resolve_type(value_type),
          meta: meta
        )
      end

      # @api private
      def weak(*)
        raise 'Support for old hash schemas was removed, please refer to the CHANGELOG '\
              'on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md'
      end
      alias_method :permissive, :weak
      alias_method :strict, :weak
      alias_method :strict_with_defaults, :weak
      alias_method :symbolized, :weak

      # Injects a type transformation function for building schemas
      #
      # @param [#call,nil] proc
      # @param [#call,nil] block
      #
      # @return [Hash]
      #
      # @api public
      def with_type_transform(proc = nil, &block)
        fn = proc || block

        raise ArgumentError, 'a block or callable argument is required' if fn.nil?

        handle = Dry::Types::FnContainer.register(fn)
        with(type_transform_fn: handle)
      end

      # @api private
      def constructor_type
        ::Dry::Types::Hash::Constructor
      end

      # Whether the type transforms types of schemas created by {Dry::Types::Hash#schema}
      #
      # @return [Boolean]
      #
      # @api public
      def transform_types?
        !options[:type_transform_fn].nil?
      end

      # @param meta [Boolean] Whether to dump the meta to the AST
      #
      # @return [Array] An AST representation
      #
      # @api public
      def to_ast(meta: true)
        opts = if RUBY_VERSION >= '2.5'
                 options.slice(:type_transform_fn)
               else
                 options.select { |k, _| k == :type_transform_fn }
               end

        [:hash, [opts, meta ? self.meta : EMPTY_HASH]]
      end

      private

      # @api private
      def build_keys(type_map)
        type_fn = options.fetch(:type_transform_fn, Schema::NO_TRANSFORM)
        type_transform = Dry::Types::FnContainer[type_fn]

        type_map.map do |map_key, type|
          name, options = key_name(map_key)
          key = Schema::Key.new(resolve_type(type), name, **options)
          type_transform.(key)
        end
      end

      # @api private
      def resolve_type(type)
        case type
        when Type then type
        when ::Class, ::String then Types[type]
        else type
        end
      end

      # @api private
      def key_name(key)
        if key.to_s.end_with?('?')
          [key.to_s.chop.to_sym, NOT_REQUIRED]
        else
          [key, EMPTY_HASH]
        end
      end
    end
  end
end

require 'dry/types/schema/key'
require 'dry/types/schema'