lib/aws-record/record/attribute.rb (56 lines of code) (raw):
# frozen_string_literal: true
module Aws
module Record
# This class provides helper methods for +Aws::Record+ attributes. These
# include marshalers for type casting of item attributes, the Amazon
# DynamoDB type for use in certain table and item operation calls, and the
# ability to define a database name that is separate from the name used
# within the model class and item instances.
class Attribute
attr_reader :name, :database_name, :dynamodb_type
# @param [Symbol] name Name of the attribute. It should be a name that is
# safe to use as a method.
# @param [Hash] options
# @option options [Marshaler] :marshaler The marshaler for this attribute.
# So long as you provide a marshaler which implements +#type_cast+ and
# +#serialize+ that consume raw values as expected, you can bring your
# own marshaler type.
# @option options [String] :database_attribute_name Optional attribute
# used to specify a different name for database persistence than the
# `name` parameter. Must be unique (you can't have overlap between
# database attribute names and the names of other attributes).
# @option options [String] :dynamodb_type Generally used for keys and
# index attributes, one of "S", "N", "B", "BOOL", "SS", "NS", "BS",
# "M", "L". Optional if this attribute will never be used for a key or
# secondary index, but most convenience methods for setting attributes
# will provide this.
# @option options [Boolean] :persist_nil Optional attribute used to
# indicate whether nil values should be persisted. If true, explicitly
# set nil values will be saved to DynamoDB as a "null" type. If false,
# nil values will be ignored and not persisted. By default, is false.
# @option options [Object] :default_value Optional attribute used to
# define a "default value" to be used if the attribute's value on an
# item is nil or not set at persistence time. Additionally, lambda can
# be used as a default value.
def initialize(name, options = {})
@name = name
@database_name = (options[:database_attribute_name] || name).to_s
@dynamodb_type = options[:dynamodb_type]
@marshaler = options[:marshaler] || DefaultMarshaler
@persist_nil = options[:persist_nil]
@default_value_or_lambda = if options.key?(:default_value)
dv = options[:default_value]
_is_lambda?(dv) ? dv : type_cast(dv)
end
end
# Attempts to type cast a raw value into the attribute's type. This call
# will forward the raw value to this attribute's marshaler class.
#
# @return [Object] the type cast object. Return type is dependent on the
# marshaler used. See your attribute's marshaler class for details.
def type_cast(raw_value)
cast_value = @marshaler.type_cast(raw_value)
cast_value = default_value if cast_value.nil?
cast_value
end
# Attempts to serialize a raw value into the attribute's serialized
# storage type. This call will forward the raw value to this attribute's
# marshaler class.
#
# @return [Object] the serialized object. Return type is dependent on the
# marshaler used. See your attribute's marshaler class for details.
def serialize(raw_value)
cast_value = type_cast(raw_value)
cast_value = default_value if cast_value.nil?
@marshaler.serialize(cast_value)
end
# @return [Boolean] +true+ if this attribute will actively persist nil
# values, +false+ otherwise. Default: +false+
def persist_nil?
@persist_nil ? true : false
end
# @api private
def extract(dynamodb_item)
dynamodb_item[@database_name]
end
# @api private
def default_value
if _is_lambda?(@default_value_or_lambda)
type_cast(@default_value_or_lambda.call)
else
_deep_copy(@default_value_or_lambda)
end
end
private
def _deep_copy(obj)
Marshal.load(Marshal.dump(obj))
end
def _is_lambda?(obj)
obj.respond_to?(:call)
end
end
# This is an identity marshaler, which performs no changes for type casting
# or serialization. It is generally not recommended for use.
module DefaultMarshaler
def self.type_cast(raw_value, _options = {})
raw_value
end
def self.serialize(raw_value, _options = {})
raw_value
end
end
end
end