Module: T::Props::Serializable
- Includes:
- Optional, Plugin, PrettyPrintable
- Included in:
- InexactStruct
- Defined in:
- lib/types/props/serializable.rb
Overview
typed: false
Defined Under Namespace
Modules: ClassMethods, DecoratorMethods
Constant Summary
Constants included from Helpers
Instance Method Summary collapse
-
#deserialize(hash, strict = false) ⇒ void
Populates the property values on this object with the values from a hash.
-
#required_prop_missing_from_deserialize(prop) ⇒ Object
Marks this property as missing during deserialize.
-
#required_prop_missing_from_deserialize?(prop) ⇒ T::Boolean
Was this property missing during deserialize?.
-
#serialize(strict = true) ⇒ Hash
Serializes this object to a hash, suitable for conversion to JSON/BSON.
-
#with(changed_props) ⇒ Object
with() will clone the old object to the new object and merge the specified props to the new object.
Methods included from PrettyPrintable
Methods included from Helpers
#abstract!, #final!, #interface!, #mixes_in_class_methods
Instance Method Details
#deserialize(hash, strict = false) ⇒ void
This method returns an undefined value.
Populates the property values on this object with the values from a hash. In general, prefer to use from_hash to construct a new instance, instead of loading into an existing instance.
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 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/types/props/serializable.rb', line 119 def deserialize(hash, strict=false) decorator = self.class.decorator matching_props = 0 decorator.props.each do |p, rules| hkey = rules[:serialized_form] val = hash[hkey] if val.nil? if T::Props::Utils.required_prop?(rules) val = decorator.get_default(rules, self.class) if val.nil? msg = "Tried to deserialize a required prop from a nil value. It's "\ "possible that a nil value exists in the database, so you should "\ "provide a `default: or factory:` for this prop (see go/optional "\ "for more details). If this is already the case, you probably "\ "omitted a required prop from the `fields:` option when doing a "\ "partial load." storytime = {prop: hkey, klass: self.class.name} # Notify the model owner if it exists, and always notify the API owner. begin if defined?(Opus) && defined?(Opus::Ownership) && decorator.decorated_class < Opus::Ownership T::Configuration.hard_assert_handler( msg, storytime: storytime, project: decorator.decorated_class.get_owner ) end ensure T::Configuration.hard_assert_handler(msg, storytime: storytime) end end elsif rules[:need_nil_read_check] self.required_prop_missing_from_deserialize(p) end matching_props += 1 if hash.key?(hkey) else if (subtype = rules[:serializable_subtype]) val = if rules[:type_is_array_of_serializable] if subtype.is_a?(T::Props::CustomType) val.map {|el| el && subtype.deserialize(el)} else val.map {|el| el && subtype.from_hash(el)} end elsif rules[:type_is_hash_of_serializable_values] && rules[:type_is_hash_of_custom_type_keys] key_subtype = subtype[:keys] values_subtype = subtype[:values] if values_subtype.is_a?(T::Props::CustomType) val.each_with_object({}) do |(key, value), result| result[key_subtype.deserialize(key)] = value && values_subtype.deserialize(value) end else val.each_with_object({}) do |(key, value), result| result[key_subtype.deserialize(key)] = value && values_subtype.from_hash(value) end end elsif rules[:type_is_hash_of_serializable_values] if subtype.is_a?(T::Props::CustomType) val.transform_values {|v| v && subtype.deserialize(v)} else val.transform_values {|v| v && subtype.from_hash(v)} end elsif rules[:type_is_hash_of_custom_type_keys] val.map do |key, value| [subtype.deserialize(key), value] end.to_h else subtype.from_hash(val) end elsif (needs_clone = rules[:type_needs_clone]) val = if needs_clone == :shallow val.dup else T::Props::Utils.deep_clone_object(val) end elsif rules[:type_is_custom_type] val = rules[:type].deserialize(val) end matching_props += 1 end self.instance_variable_set(rules[:accessor_key], val) # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess end # We compute extra_props this way specifically for performance if matching_props < hash.size pbsf = decorator.prop_by_serialized_forms h = hash.reject {|k, _| pbsf.key?(k)} if strict raise "Unknown properties for #{self.class.name}: #{h.keys.inspect}" else @_extra_props = h end end end |
#required_prop_missing_from_deserialize(prop) ⇒ Object
Returns Marks this property as missing during deserialize.
264 265 266 267 268 |
# File 'lib/types/props/serializable.rb', line 264 def required_prop_missing_from_deserialize(prop) @_required_props_missing_from_deserialize ||= Set[] @_required_props_missing_from_deserialize << prop nil end |
#required_prop_missing_from_deserialize?(prop) ⇒ T::Boolean
Returns Was this property missing during deserialize?.
258 259 260 261 |
# File 'lib/types/props/serializable.rb', line 258 def required_prop_missing_from_deserialize?(prop) return false if @_required_props_missing_from_deserialize.nil? @_required_props_missing_from_deserialize.include?(prop) end |
#serialize(strict = true) ⇒ Hash
Serializes this object to a hash, suitable for conversion to JSON/BSON.
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 |
# File 'lib/types/props/serializable.rb', line 18 def serialize(strict=true) decorator = self.class.decorator h = {} decorator.props.each do |prop, rules| hkey = rules[:serialized_form] val = decorator.get(self, prop, rules) if val.nil? && strict && !rules[:fully_optional] # If the prop was already missing during deserialization, that means the application # code already had to deal with a nil value, which means we wouldn't be accomplishing # much by raising here (other than causing an unnecessary breakage). if self.required_prop_missing_from_deserialize?(prop) T::Configuration.log_info_handler( "chalk-odm: missing required property in serialize", prop: prop, class: self.class.name, id: decorator.get_id(self) ) else raise T::Props::InvalidValueError.new("#{self.class.name}.#{prop} not set for non-optional prop") end end # Don't serialize values that are nil to save space (both the # nil value itself and the field name in the serialized BSON # document) next if rules[:dont_store] || val.nil? if rules[:serializable_subtype] if rules[:type_is_serializable] val = val.serialize(strict) elsif rules[:type_is_array_of_serializable] if (subtype = rules[:serializable_subtype]).is_a?(T::Props::CustomType) val = val.map {|el| el && subtype.serialize(el)} else val = val.map {|el| el && el.serialize(strict)} end elsif rules[:type_is_hash_of_serializable_values] && rules[:type_is_hash_of_custom_type_keys] key_subtype = rules[:serializable_subtype][:keys] value_subtype = rules[:serializable_subtype][:values] if value_subtype.is_a?(T::Props::CustomType) val = val.each_with_object({}) do |(key, value), result| result[key_subtype.serialize(key)] = value && value_subtype.serialize(value) end else val = val.each_with_object({}) do |(key, value), result| result[key_subtype.serialize(key)] = value && value.serialize(strict) end end elsif rules[:type_is_hash_of_serializable_values] value_subtype = rules[:serializable_subtype] if value_subtype.is_a?(T::Props::CustomType) val = val.transform_values {|v| v && value_subtype.serialize(v)} else val = val.transform_values {|v| v && v.serialize(strict)} end elsif rules[:type_is_hash_of_custom_type_keys] key_subtype = rules[:serializable_subtype] val = val.each_with_object({}) do |(key, value), result| result[key_subtype.serialize(key)] = value end end elsif rules[:type_is_custom_type] val = rules[:type].serialize(val) unless T::Props::CustomType.valid_serialization?(val, rules[:type]) msg = "#{rules[:type]} did not serialize to a valid scalar type. It became a: #{val.class}" if val.is_a?(Hash) msg += "\nIf you want to store a structured Hash, consider using a T::Struct as your type." end raise T::Props::InvalidValueError.new(msg) end end needs_clone = rules[:type_needs_clone] if needs_clone if needs_clone == :shallow val = val.dup else val = T::Props::Utils.deep_clone_object(val) end end h[hkey] = val end h.merge!(@_extra_props) if @_extra_props h end |
#with(changed_props) ⇒ Object
with() will clone the old object to the new object and merge the specified props to the new object.
222 223 224 |
# File 'lib/types/props/serializable.rb', line 222 def with(changed_props) with_existing_hash(changed_props, existing_hash: self.serialize) end |