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!, #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.
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 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 |
# File 'lib/types/props/serializable.rb', line 110 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::Utils::Props.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 T::Props::Utils.need_nil_read_check?(rules) 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.
255 256 257 258 259 |
# File 'lib/types/props/serializable.rb', line 255 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?.
249 250 251 252 |
# File 'lib/types/props/serializable.rb', line 249 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 |
# File 'lib/types/props/serializable.rb', line 18 def serialize(strict=true) decorator = self.class.decorator h = {} decorator.props.keys.each do |prop| rules = decorator.prop_rules(prop) hkey = rules[:serialized_form] val = decorator.get(self, prop) if strict && val.nil? && T::Props::Utils.need_nil_write_check?(rules) # 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 decorator.prop_dont_store?(prop) || val.nil? 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 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 h[hkey] = T::Props::Utils.deep_clone_object(val) end extra_props = decorator.extra_props(self) 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.
213 214 215 |
# File 'lib/types/props/serializable.rb', line 213 def with(changed_props) with_existing_hash(changed_props, existing_hash: self.serialize) end |