module T::Props::Serializable

typed: false

Public Instance Methods

deserialize(hash, strict=false) click to toggle source

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.

@param hash [Hash<String, Object>] The hash to take property

values from.

@param strict [T::Boolean] (false) If true, raise an exception if

the hash contains keys that do not correspond to any known
props on this instance.

@return [void]

# File lib/types/props/serializable.rb, line 59
def deserialize(hash, strict=false)
  begin
    hash_keys_matching_props = __t_props_generated_deserialize(hash)
  rescue => e
    msg = self.class.decorator.message_with_generated_source_context(
      e,
      :__t_props_generated_deserialize,
      :generate_deserialize_source
    )
    if msg
      begin
        raise e.class.new(msg)
      rescue ArgumentError
        raise TypeError.new(msg)
      end
    else
      raise
    end
  end

  if hash.size > hash_keys_matching_props
    serialized_forms = self.class.decorator.prop_by_serialized_forms
    extra = hash.reject {|k, _| serialized_forms.key?(k)}

    # `extra` could still be empty here if the input matches a `dont_store` prop;
    # historically, we just ignore those
    if !extra.empty?
      if strict
        raise "Unknown properties for #{self.class.name}: #{extra.keys.inspect}"
      else
        @_extra_props = extra
      end
    end
  end
end
serialize(strict=true) click to toggle source

Serializes this object to a hash, suitable for conversion to JSON/BSON.

@param strict [T::Boolean] (true) If false, do not raise an

exception if this object has mandatory props with missing
values.

@return [Hash] A serialization of this object.

# File lib/types/props/serializable.rb, line 18
def serialize(strict=true)
  begin
    h = __t_props_generated_serialize(strict)
  rescue => e
    msg = self.class.decorator.message_with_generated_source_context(
      e,
      :__t_props_generated_serialize,
      :generate_serialize_source
    )
    if msg
      begin
        raise e.class.new(msg)
      rescue ArgumentError
        raise TypeError.new(msg)
      end
    else
      raise
    end
  end

  h.merge!(@_extra_props) if @_extra_props
  h
end
with(changed_props) click to toggle source

with() will clone the old object to the new object and merge the specified props to the new object.

# File lib/types/props/serializable.rb, line 103
def with(changed_props)
  with_existing_hash(changed_props, existing_hash: self.serialize)
end

Private Instance Methods

__t_props_generated_deserialize(hash) click to toggle source
# File lib/types/props/serializable.rb, line 95
        def __t_props_generated_deserialize(hash)
  # No-op; will be overridden if there are any props.
  #
  # To see the definition for class `Foo`, run `Foo.decorator.send(:generate_deserialize_source)`
  0
end
__t_props_generated_serialize(strict) click to toggle source
# File lib/types/props/serializable.rb, line 42
        def __t_props_generated_serialize(strict)
  # No-op; will be overridden if there are any props.
  #
  # To see the definition for class `Foo`, run `Foo.decorator.send(:generate_serialize_source)`
  {}
end
raise_deserialization_error(prop_name, value, orig_error) click to toggle source
# File lib/types/props/serializable.rb, line 160
        def raise_deserialization_error(prop_name, value, orig_error)
  T::Configuration.soft_assert_handler(
    'Deserialization error (probably unexpected stored type)',
    storytime: {
      klass: self.class,
      prop: prop_name,
      value: value,
      error: orig_error.message,
      notify: 'djudd'
    }
  )
end
recursive_stringify_keys(obj) click to toggle source
# File lib/types/props/serializable.rb, line 107
        def recursive_stringify_keys(obj)
  if obj.is_a?(Hash)
    new_obj = obj.class.new
    obj.each do |k, v|
      new_obj[k.to_s] = recursive_stringify_keys(v)
    end
  elsif obj.is_a?(Array)
    new_obj = obj.map {|v| recursive_stringify_keys(v)}
  else
    new_obj = obj
  end
  new_obj
end
required_prop_missing_from_deserialize(prop) click to toggle source

Marks this property as missing during deserialize

# File lib/types/props/serializable.rb, line 154
        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_serialize(prop) click to toggle source

Asserts if this property is missing during strict serialize

# File lib/types/props/serializable.rb, line 139
        def required_prop_missing_from_serialize(prop)
  if @_required_props_missing_from_deserialize&.include?(prop)
    # 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).
    T::Configuration.log_info_handler(
      "chalk-odm: missing required property in serialize",
      prop: prop, class: self.class.name, id: self.class.decorator.get_id(self)
    )
  else
    raise TypeError.new("#{self.class.name}.#{prop} not set for non-optional prop")
  end
end
with_existing_hash(changed_props, existing_hash:) click to toggle source
# File lib/types/props/serializable.rb, line 121
        def with_existing_hash(changed_props, existing_hash:)
  serialized = existing_hash
  new_val = self.class.from_hash(serialized.merge(recursive_stringify_keys(changed_props)))
  old_extra = self.instance_variable_get(:@_extra_props)
  new_extra = new_val.instance_variable_get(:@_extra_props)
  if old_extra != new_extra
    difference =
      if old_extra
        new_extra.reject {|k, v| old_extra[k] == v}
      else
        new_extra
      end
    raise ArgumentError.new("Unexpected arguments: input(#{changed_props}), unexpected(#{difference})")
  end
  new_val
end