optify-from_hash 0.3.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 323fea346213dffe7e7b35edf19d78d2fa2f8ad7febff0dcb43d6982c48e6732
4
- data.tar.gz: c85f2f191f33306858d91855ae8a32ad0de373d565dca8227250f9c20d59e2fe
3
+ metadata.gz: 23bb51f6e184777cad52c5ee7555c00a74e981b13c34fbc13133e80e4dd8b447
4
+ data.tar.gz: 80d4b0194cabc3202e60199ee7473f176e00795c9d2d07906cafca0cf0d9efe9
5
5
  SHA512:
6
- metadata.gz: 274a651a061c8ac9840de8c75a46391e78ac599ba71135dbd9b748045567f7c957fe8e9ce87b3c43ddf969868d149aa6526327ed26f16204e3a0404413ff7a35
7
- data.tar.gz: 7b8793ab933e2478b798a06557d812bca70fd9c031394076b06790672162187261cb2f0801bf6f27320ec55b434bf1c827414de65f6804085acc529d3900ca3c
6
+ metadata.gz: d51c60abe1041073c0090b6759824963af316248b858608ce7be0f8cf101f74d8207e2d25dbaab274868548c34dd2f4f067cf7807d732f34d0c91fae157afd59
7
+ data.tar.gz: db3e5143e8ca29bf947043d9cfaffd6d840bf8dc7295d546daefa7a65373b53a0606c48ea770338aaa7079e2652e94b5f24cc4baf877a05b2b2cd3cac41e6d1d
@@ -13,6 +13,41 @@ module Optify
13
13
  extend T::Helpers
14
14
  abstract!
15
15
 
16
+ @key_to_type = {} #: Hash[Symbol, T::Types::Base]
17
+
18
+ class << self
19
+ #: Hash[Symbol, T::Types::Base]
20
+ attr_reader :key_to_type
21
+ end
22
+
23
+ #: [T < Optify::FromHashable] (Class[T]) -> void
24
+ def self.inherited(subclass)
25
+ super
26
+
27
+ # Trace the execution after the subclass finishes loading to capture its methods.
28
+ TracePoint.trace(:end) do |tp|
29
+ if tp.self == subclass
30
+ subclass.instance_variable_set(:@key_to_type, _build_key_to_type(subclass))
31
+ tp.disable
32
+ end
33
+ end
34
+ end
35
+
36
+ #: [Type < Optify::FromHashable] (Class[Type]) -> Hash[Symbol, T::Types::Base]
37
+ private_class_method def self._build_key_to_type(subclass)
38
+ result = {}
39
+
40
+ subclass.public_instance_methods(false).each do |method_name|
41
+ method = subclass.instance_method(method_name)
42
+ sig = T::Utils.signature_for_method(method)
43
+ next if sig.nil?
44
+
45
+ result[method_name] = sig.return_type
46
+ end
47
+
48
+ result.freeze
49
+ end
50
+
16
51
  # Create a new immutable instance of the class from a hash.
17
52
  #
18
53
  # @param hash The hash to create the instance from.
@@ -22,34 +57,43 @@ module Optify
22
57
  instance = new
23
58
 
24
59
  hash.each do |key, value|
25
- begin
26
- method = instance_method(key)
27
- rescue StandardError
28
- raise ArgumentError,
29
- "Error converting hash to `#{name}` because of key \"#{key}\". Perhaps \"#{key}\" is not a valid attribute for `#{name}`."
30
- end
31
-
32
- sig = T::Utils.signature_for_method(method)
33
- raise "A Sorbet signature is required for `#{name}.#{key}`." if sig.nil?
34
-
35
- sig_return_type = sig.return_type
36
- value = _convert_value(value, sig_return_type)
60
+ value_type = _get_value_type(key)
61
+ value = _convert_value(value, value_type)
37
62
  instance.instance_variable_set("@#{key}", value)
38
63
  end
39
64
 
40
65
  instance.freeze
41
66
  end
42
67
 
68
+ #: (untyped) -> T::Types::Base
69
+ private_class_method def self._get_value_type(key)
70
+ key = key.to_sym
71
+ @key_to_type.fetch(key) do
72
+ parent = superclass #: untyped
73
+ while parent != Object
74
+ if parent.respond_to?(:key_to_type)
75
+ result = parent.key_to_type[key]
76
+ return result if result
77
+ end
78
+ parent = parent.superclass
79
+ end
80
+ raise ArgumentError,
81
+ "Error converting hash to `#{name}` because no type was found for key \"#{key}\". " \
82
+ "Perhaps \"#{key}\" is not a valid attribute for `#{name}`. " \
83
+ "Types exist for #{@key_to_type.keys.sort!}"
84
+ end
85
+ end
86
+
43
87
  #: (Array[untyped], untyped) -> (Array[untyped] | Set[untyped])
44
- def self._convert_array(value, unwrapped_type)
88
+ private_class_method def self._convert_array(value, unwrapped_type)
45
89
  inner_type = unwrapped_type.type
46
90
  return value.map { |v| _convert_value(v, inner_type) }.freeze if unwrapped_type.is_a?(T::Types::TypedArray)
47
91
 
48
- Set.new(value.map { |v| _convert_value(v, inner_type) }).freeze
92
+ value.each_with_object(Set.new) { |v, set| set.add(_convert_value(v, inner_type)) }.freeze
49
93
  end
50
94
 
51
95
  #: (untyped, T::Types::Base) -> untyped
52
- def self._convert_value(value, type)
96
+ private_class_method def self._convert_value(value, type)
53
97
  if type.is_a?(T::Types::Untyped)
54
98
  # No preferred type is given, so return the value as is.
55
99
  return value
@@ -84,7 +128,7 @@ module Optify
84
128
  end
85
129
 
86
130
  #: (Hash[untyped, untyped], T::Types::Base) -> untyped
87
- def self._convert_hash(hash, type)
131
+ private_class_method def self._convert_hash(hash, type)
88
132
  if type.respond_to?(:raw_type)
89
133
  # There is an object for the hash.
90
134
  # It could be a custom class, a String, or maybe something else.
@@ -94,15 +138,14 @@ module Optify
94
138
  elsif type.is_a?(T::Types::TypedHash)
95
139
  # The hash should be a hash, but the values might be objects to convert.
96
140
  type_for_keys = type.keys
141
+ type_for_values = type.values
97
142
 
98
- convert_key = if type_for_keys.is_a?(T::Types::Simple) && type_for_keys.raw_type == Symbol
99
- lambda(&:to_sym)
100
- else
101
- lambda(&:itself)
102
- end
143
+ result = hash
144
+ .transform_values { |v| _convert_value(v, type_for_values) }
103
145
 
104
- type_for_values = type.values
105
- return hash.map { |k, v| [convert_key.call(k), _convert_value(v, type_for_values)] }.to_h
146
+ return result.transform_keys!(&:to_sym) if type_for_keys.is_a?(T::Types::Simple) && type_for_keys.raw_type == Symbol
147
+
148
+ return result
106
149
  end
107
150
 
108
151
  raise TypeError, "Could not convert hash #{hash} to `#{type}`."
@@ -110,7 +153,7 @@ module Optify
110
153
 
111
154
  # Unwrap `T.nilable(...)` to get the inner type, or return the type as-is.
112
155
  #: (T::Types::Base) -> T::Types::Base
113
- def self._unwrap_nilable(type)
156
+ private_class_method def self._unwrap_nilable(type)
114
157
  if type.respond_to?(:unwrap_nilable)
115
158
  type #: as untyped
116
159
  .unwrap_nilable
@@ -119,8 +162,6 @@ module Optify
119
162
  end
120
163
  end
121
164
 
122
- private_class_method :_convert_array, :_convert_hash, :_convert_value, :_unwrap_nilable
123
-
124
165
  # Compare this object with another object for equality.
125
166
  # @param other The object to compare.
126
167
  # @return [Boolean] true if the objects are equal; otherwise, false.
@@ -177,9 +218,9 @@ module Optify
177
218
  end
178
219
 
179
220
  #: (untyped) -> untyped
180
- def self._convert_value_for_to_h(value)
221
+ private_class_method def self._convert_value_for_to_h(value)
181
222
  case value
182
- # Treat sets like arrays for JSON serialization.
223
+ # Treat sets like arrays for JSON serialization; otherwise, the elements are not shown.
183
224
  when Array, Set
184
225
  value.map { |v| _convert_value_for_to_h(v) }
185
226
  when Hash
@@ -194,7 +235,5 @@ module Optify
194
235
  end
195
236
  end
196
237
  end
197
-
198
- private_class_method :_convert_value_for_to_h
199
238
  end
200
239
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: optify-from_hash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin D. Harris