class ValueSemantics::Attribute

Represents a single attribute of a value class

Constants

NOT_SPECIFIED
NO_DEFAULT_GENERATOR

Attributes

coercer[R]
default_generator[R]
instance_variable[R]
name[R]
optional[R]
optional?[R]
validator[R]

Public Class Methods

define( name, validator=Anything, default: NOT_SPECIFIED, default_generator: nil, coerce: nil ) click to toggle source
# File lib/value_semantics/attribute.rb, line 30
def self.define(
  name,
  validator=Anything,
  default: NOT_SPECIFIED,
  default_generator: nil,
  coerce: nil
)
  # TODO: change how defaults are specified:
  #
  #  - default: either a value, or a callable
  #  - default_value: always a value
  #  - default_generator: always a callable
  #
  # This would not be a backwards compatible change.
  generator = begin
    if default_generator && !default.equal?(NOT_SPECIFIED)
      raise ArgumentError, "Attribute `#{name}` can not have both a `:default` and a `:default_generator`"
    elsif default_generator
      default_generator
    elsif !default.equal?(NOT_SPECIFIED)
      ->{ default }
    else
      NO_DEFAULT_GENERATOR
    end
  end

  new(
    name: name,
    validator: validator,
    default_generator: generator,
    coercer: coerce,
  )
end
new( name:, default_generator: NO_DEFAULT_GENERATOR, validator: Anything, coercer: nil ) click to toggle source
# File lib/value_semantics/attribute.rb, line 15
def initialize(
  name:,
  default_generator: NO_DEFAULT_GENERATOR,
  validator: Anything,
  coercer: nil
)
  @name = name.to_sym
  @default_generator = default_generator
  @validator = validator
  @coercer = coercer
  @instance_variable = '@' + name.to_s.chomp('!').chomp('?')
  @optional = !default_generator.equal?(NO_DEFAULT_GENERATOR)
  freeze
end

Public Instance Methods

coerce(attr_value, value_class) click to toggle source
# File lib/value_semantics/attribute.rb, line 82
def coerce(attr_value, value_class)
  return attr_value unless coercer # coercion not enabled

  if coercer.equal?(true)
    value_class.public_send(coercion_method, attr_value)
  else
    coercer.call(attr_value)
  end
end
coercion_method() click to toggle source
# File lib/value_semantics/attribute.rb, line 96
def coercion_method
  "coerce_#{name}"
end
determine_from!(attr_hash, value_class) click to toggle source

@deprecated Use a combination of the other instance methods instead

# File lib/value_semantics/attribute.rb, line 65
def determine_from!(attr_hash, value_class)
  raw_value = attr_hash.fetch(name) do
    if default_generator.equal?(NO_DEFAULT_GENERATOR)
      raise MissingAttributes, "Attribute `#{value_class}\##{name}` has no value"
    else
      default_generator.call
    end
  end

  coerced_value = coerce(raw_value, value_class)
  if validate?(coerced_value)
    [name, coerced_value]
  else
    raise InvalidValue, "Attribute `#{value_class}\##{name}` is invalid: #{coerced_value.inspect}"
  end
end
validate?(value) click to toggle source
# File lib/value_semantics/attribute.rb, line 92
def validate?(value)
  validator === value
end