class ContractValueObject

Constants

VERSION

Attributes

error_presenter[W]

Public Class Methods

attributes(attributes = nil) click to toggle source
# File lib/contract_value_object.rb, line 11
def attributes(attributes = nil)
  if attributes.nil?
    return @attributes if instance_variable_defined?(:@attributes)
    raise ArgumentError, 'Calling for attributes without having defined them.'
  end

  attr_accessor(*attributes.keys)
  attr_writers = attributes.keys.map { |attribute| :"#{attribute}=" }
  private *attr_writers

  @attributes = attributes
end
defaults(defaults = nil) click to toggle source
# File lib/contract_value_object.rb, line 25
def defaults(defaults = nil)
  return @defaults ||= {} if defaults.nil?

  unexpected_defaults = defaults.keys - attributes.keys
  unless unexpected_defaults.empty?
    raise ArgumentError, "Unexpected defaults are set: #{unexpected_defaults.map(&:to_s).join(', ')}"
  end

  non_optional = defaults.select do |attribute, _|
    !attributes.fetch(attribute).is_a?(Contracts::Optional)
  end
  raise ArgumentError, "Unexpected non-optional defaults: #{non_optional.keys.join(', ')}" unless non_optional.empty?

  @defaults = defaults
end
error_presenter() click to toggle source
# File lib/contract_value_object.rb, line 44
def error_presenter
  @error_presenter ||= self::ErrorFormatter.new
end
new(**attributes) click to toggle source
# File lib/contract_value_object.rb, line 50
def initialize(**attributes)
  error_presenter = self.class.error_presenter
  class_attributes = self.class.attributes
  defaults = self.class.defaults
  error_message_class = DefinitionError::ErrorMessage

  # determine attributes that were not passed in but should have been
  missing_attributes = class_attributes.keys - attributes.keys - defaults.keys
  missing_attribute_errors = missing_attributes.flat_map do |attribute|
    next([]) if class_attributes[attribute].is_a?(Contracts::Optional)
    error_message_class.new(attribute, error_presenter.missing(attribute, class_attributes.fetch(attribute)))
  end

  # determine extraneous attributes that should not have been passed in
  unexpected_attributes = attributes.keys - class_attributes.keys
  unexpected_attribute_errors = unexpected_attributes.map do |attribute|
    error_message_class.new(attribute, error_presenter.unexpected(attribute))
  end

  # set attributes on the object and raise if they do not obey the contract
  setter_errors = defaults.merge(attributes).flat_map do |attribute, value|
    next([]) if unexpected_attributes.include?(attribute)

    begin
      contract = class_attributes.fetch(attribute)
      contract.within_opt_hash! if contract.is_a?(Contracts::Optional)
      # begin support customized setter
      send("#{attribute}=", value)
      set_value = instance_variable_get("@#{attribute}")
      # end support for customized setter
      if Contract.valid?(set_value, contract)
        []
      else
        raise ArgumentError, error_presenter.contract_failure(attribute, set_value, contract)
      end
    rescue self.class::DefinitionError => error
      error_message_class.new(attribute, error.message.gsub("\n", "\n\t"))
    rescue StandardError => error
      error_message_class.new(attribute, error.message)
    end
  end

  errors = missing_attribute_errors + unexpected_attribute_errors + setter_errors
  return if errors.empty?

  raise DefinitionError, errors
end

Public Instance Methods

==(other) click to toggle source
Calls superclass method
# File lib/contract_value_object.rb, line 111
def ==(other)
  return super(other) unless self.class == other.class

  self.class.attributes.all? do |attribute, _type|
    public_send(attribute) == other.public_send(attribute)
  end
end
Also aliased as: eql?
eql?(other)
Alias for: ==
hash() click to toggle source

treating contract value objects with the same values as the same object for hashes and sets

# File lib/contract_value_object.rb, line 107
def hash
  to_h.hash
end
inspect() click to toggle source
# File lib/contract_value_object.rb, line 121
def inspect
  "#{self.class} #{to_h.inspect}"
end
to_h() click to toggle source
# File lib/contract_value_object.rb, line 98
def to_h
  key_value_pairs = self.class.attributes.map do |attribute, _type|
    [attribute, public_send(attribute)]
  end

  key_value_pairs.to_h
end