Module: Decoding::Data

Defined in:
lib/decoding/data.rb

Overview

Wrapper around Ruby's Data class, providing a way to both define a new data class as usual but including a decoder to decode a value into such a data value.

end payload = { "postName" => "John", "yearsOld" => 30 } Decoding.decode(User.decoder, payload) => Decoding::Ok(user) user.greet # => "Hello, my name is John and I am 30 years old."

Examples:

User = Decoding::Data.define(
  name: field("postName", string),
  age: field("yearsOld", integer)
) do
 def greet
   "Hello, my name is #{name} and I am #{age} years old."
 end

Returns:

  • (Class)

Class Method Summary collapse

Class Method Details

.define(attributes) ⇒ Class

Returns A new data class with a decoder method.

Parameters:

  • attributes (Hash<Symbol, Decoding::Decoder>) —

    A hash of attribute names and decoders

Returns:

  • (Class) —

    A new data class with a decoder method.

See Also:

  • Data.define


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/decoding/data.rb', line 28

def self.define(attributes, &)
  # First ensure we have a hash of attribute names and decoders
  attributes = attributes.to_h do |key, value|
    [key.to_sym, value.to_decoder]
  end

  # Define the data class as usual
  data_class = ::Data.define(*attributes.keys, &)

  # Add a special `decode` method to use the provided decoders to decode a
  # value as hash and build a new instance with it
  data_class.define_singleton_method(:decoder) do
    Decoding::Decoders.map(
      Decoding::Decoders.decode_hash(attributes)
    ) { |attrs| new(**attrs) }
  end

  # Adapt to `to_decoder` protocol
  data_class.define_singleton_method(:to_decoder) { decoder }

  # Return the data class so regular assignment like
  # `User = ::Data.define(...)` works.
  data_class
end