Class: ValueSemantics::Attribute

Inherits:
Object
  • Object
show all
Defined in:
lib/value_semantics/attribute.rb

Overview

Represents a single attribute of a value class

Constant Summary collapse

NOT_SPECIFIED =
Object.new.freeze
NO_DEFAULT_GENERATOR =
lambda do
  raise NoDefaultValue, "Attribute does not have a default value"
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, default_generator: NO_DEFAULT_GENERATOR, validator: Anything, coercer: nil) ⇒ Attribute

Returns a new instance of Attribute.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 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

Instance Attribute Details

#coercerObject (readonly)

Returns the value of attribute coercer.



11
12
13
# File 'lib/value_semantics/attribute.rb', line 11

def coercer
  @coercer
end

#default_generatorObject (readonly)

Returns the value of attribute default_generator.



11
12
13
# File 'lib/value_semantics/attribute.rb', line 11

def default_generator
  @default_generator
end

#instance_variableObject (readonly)

Returns the value of attribute instance_variable.



11
12
13
# File 'lib/value_semantics/attribute.rb', line 11

def instance_variable
  @instance_variable
end

#nameObject (readonly)

Returns the value of attribute name.



11
12
13
# File 'lib/value_semantics/attribute.rb', line 11

def name
  @name
end

#optionalObject (readonly) Also known as: optional?

Returns the value of attribute optional.



11
12
13
# File 'lib/value_semantics/attribute.rb', line 11

def optional
  @optional
end

#validatorObject (readonly)

Returns the value of attribute validator.



11
12
13
# File 'lib/value_semantics/attribute.rb', line 11

def validator
  @validator
end

Class Method Details

.define(name, validator = Anything, default: NOT_SPECIFIED, default_generator: nil, coerce: nil) ⇒ Object



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
# 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

Instance Method Details

#coerce(attr_value, value_class) ⇒ Object



82
83
84
85
86
87
88
89
90
# 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_methodObject



96
97
98
# File 'lib/value_semantics/attribute.rb', line 96

def coercion_method
  "coerce_#{name}"
end

#determine_from!(attr_hash, value_class) ⇒ Object

Deprecated.

Use a combination of the other instance methods instead



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 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) ⇒ Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/value_semantics/attribute.rb', line 92

def validate?(value)
  validator === value
end