Class: Dry::Struct

Inherits:
Object
  • Object
show all
Extended by:
Configurable, ClassInterface
Includes:
Core::Constants
Defined in:
lib/dry/struct.rb,
lib/dry/struct/value.rb,
lib/dry/struct/errors.rb,
lib/dry/struct/hashify.rb,
lib/dry/struct/version.rb,
lib/dry/struct/constructor.rb,
lib/dry/struct/class_interface.rb

Overview

Typed Struct with virtus-like DSL for defining schema.

Differences between dry-struct and virtus

Struct look somewhat similar to Virtus but there are few significant differences:

  • Structs don't provide attribute writers and are meant to be used as "data objects" exclusively.
  • Handling of attribute values is provided by standalone type objects from dry-types.
  • Handling of attribute hashes is provided by standalone hash schemas from dry-types, which means there are different types of constructors in Struct (see ClassInterface#constructor_type)
  • Struct classes quack like dry-types, which means you can use them in hash schemas, as array members or sum them

Struct class can specify a constructor type, which uses hash schemas to handle attributes in .new method. See ClassInterface#new for constructor types descriptions and examples.

Examples:

require 'dry-struct'

module Types
  include Dry::Types.module
end

class Book < Dry::Struct
  attribute :title, Types::Strict::String
  attribute :subtitle, Types::Strict::String.optional
end

rom_n_roda = Book.new(
  title: 'Web Development with ROM and Roda',
  subtitle: nil
)
rom_n_roda.title #=> 'Web Development with ROM and Roda'
rom_n_roda.subtitle #=> nil

refactoring = Book.new(
  title: 'Refactoring',
  subtitle: 'Improving the Design of Existing Code'
)
refactoring.title #=> 'Refactoring'
refactoring.subtitle #=> 'Improving the Design of Existing Code'

Direct Known Subclasses

Value

Defined Under Namespace

Modules: ClassInterface, Hashify Classes: Constructor, RepeatedAttributeError, Value

Constant Summary collapse

Error =

Raised when given input doesn't conform schema and constructor type

Class.new(TypeError)
VERSION =
'0.3.1'.freeze

Instance Method Summary collapse

Methods included from ClassInterface

argument_error_msg, attribute, attribute?, attribute_names, attributes, call, check_invalid_schema_keys, constrained?, constructor, default?, default_attributes, failure, inherited, optional?, primitive, result, success, try, valid?

Constructor Details

#initialize(attributes) ⇒ Struct

Returns a new instance of Struct.

Parameters:

  • attributes (Hash, #each)


187
188
189
# File 'lib/dry/struct.rb', line 187

def initialize(attributes)
  attributes.each { |key, value| instance_variable_set("@#{key}", value) }
end

Instance Method Details

#[](name) ⇒ Object

Retrieves value of previously defined attribute by its' name

Examples:

class Book < Dry::Struct
  attribute :title, Types::Strict::String
  attribute :subtitle, Types::Strict::String.optional
end

rom_n_roda = Book.new(
  title: 'Web Development with ROM and Roda',
  subtitle: nil
)
rom_n_roda[:title] #=> 'Web Development with ROM and Roda'
rom_n_roda[:subtitle] #=> nil

Parameters:

  • name (String)

Returns:

  • (Object)


208
209
210
# File 'lib/dry/struct.rb', line 208

def [](name)
  public_send(name)
end

#constructor_type(type) ⇒ Symbol #constructor_typeSymbol

Note:

All examples below assume that you have defined Dry::Struct with following attributes and explicitly call only #constructor_type:

class User < Dry::Struct
  attribute :name, Types::Strict::String.default('John Doe')
  attribute :age, Types::Strict::Int
end
Note:

Don’t use :weak and :symbolized as #constructor_type, and instead use dry-validation to process and validate attributes, otherwise your struct will behave as a data validator which raises exceptions on invalid input (assuming your attributes types are strict)

Sets or retrieves Dry::Struct::ClassInterface#constructor type as a symbol

Common constructor types include:

  • :permissive - the default constructor type, useful for defining Dry::Structs that are instantiated using data from the database (i.e. results of a database query), where you expect all defined attributes to be present and it's OK to ignore other keys (i.e. keys used for joining, that are not relevant from your domain Dry::Structs point of view). Default values are not used otherwise you wouldn't notice missing data.
  • :schema - missing keys will result in setting them using default values, unexpected keys will be ignored.
  • :strict - useful when you do not expect keys other than the ones you specified as attributes in the input hash
  • :strict_with_defaults - same as :strict but you are OK that some values may be nil and you want defaults to be set

To feel the difference between constructor types, look into examples. Each of them provide the same attributes' definitions, different constructor type, and 4 cases of given input:

  1. Input omits a key for a value that does not have a default
  2. Input omits a key for a value that has a default
  3. Input contains nil for a value that specifies a default
  4. Input includes a key that was not specified in the schema

Examples:

:permissive constructor

class User < Dry::Struct
  constructor_type :permissive
end

User.new(name: "Jane")
  #=> Dry::Struct::Error: [User.new] :age is missing in Hash input
User.new(age: 31)
  #=> Dry::Struct::Error: [User.new] :name is missing in Hash input
User.new(name: nil, age: 31)
  #=> #<User name="John Doe" age=31>
User.new(name: "Jane", age: 31, unexpected: "attribute")
  #=> #<User name="Jane" age=31>

:schema constructor

class User < Dry::Struct
  constructor_type :schema
end

User.new(name: "Jane")        #=> #<User name="Jane" age=nil>
User.new(age: 31)             #=> #<User name="John Doe" age=31>
User.new(name: nil, age: 31)  #=> #<User name="John Doe" age=31>
User.new(name: "Jane", age: 31, unexpected: "attribute")
  #=> #<User name="Jane" age=31>

:strict constructor

class User < Dry::Struct
  constructor_type :strict
end

User.new(name: "Jane")
  #=> Dry::Struct::Error: [User.new] :age is missing in Hash input
User.new(age: 31)
  #=> Dry::Struct::Error: [User.new] :name is missing in Hash input
User.new(name: nil, age: 31)
  #=> Dry::Struct::Error: [User.new] nil (NilClass) has invalid type for :name
User.new(name: "Jane", age: 31, unexpected: "attribute")
  #=> Dry::Struct::Error: [User.new] unexpected keys [:unexpected] in Hash input

:strict_with_defaults constructor

class User < Dry::Struct
  constructor_type :strict_with_defaults
end

User.new(name: "Jane")
  #=> Dry::Struct::Error: [User.new] :age is missing in Hash input
User.new(age: 31)
  #=> #<User name="John Doe" age=31>
User.new(name: nil, age: 31)
  #=> Dry::Struct::Error: [User.new] nil (NilClass) has invalid type for :name
User.new(name: "Jane", age: 31, unexpected: "attribute")
  #=> Dry::Struct::Error: [User.new] unexpected keys [:unexpected] in Hash input

Overloads:

  • #constructor_type(type) ⇒ Symbol

    Sets the constructor type for Dry::Struct

    Parameters:

    • type (Symbol)

      one of constructor types, see above

    Returns:

    • (Symbol)
  • #constructor_typeSymbol

    Returns the constructor type for Dry::Struct

    Returns:

    • (Symbol)

      (:strict)

See Also:



180
# File 'lib/dry/struct.rb', line 180

defines :constructor_type

#equalizerDry::Equalizer

Returns:

  • (Dry::Equalizer)


184
# File 'lib/dry/struct.rb', line 184

defines :equalizer

#inputDry::Types::Hash

Types::Hash subclass with specific behaviour defined for

Returns:

  • (Dry::Types::Hash)

See Also:



66
# File 'lib/dry/struct.rb', line 66

defines :input

#new(changeset) ⇒ Struct Also known as: __new__

Create a copy of Dry::Struct with overriden attributes

Examples:

class Book < Dry::Struct
  attribute :title, Types::Strict::String
  attribute :subtitle, Types::Strict::String.optional
end

rom_n_roda = Book.new(
  title: 'Web Development with ROM and Roda',
  subtitle: '2nd edition'
)
  #=> #<Book title="Web Development with ROM and Roda" subtitle="2nd edition">

rom_n_roda.new(subtitle: '3nd edition')
  #=> #<Book title="Web Development with ROM and Roda" subtitle="3nd edition">

Parameters:

  • changeset (Hash{Symbol => Object})

Returns:



256
257
258
# File 'lib/dry/struct.rb', line 256

def new(changeset)
  self.class[to_hash.merge(changeset)]
end

#schemaHash{Symbol => Dry::Types::Definition, Dry::Struct}

Returns:

  • (Hash{Symbol => Dry::Types::Definition, Dry::Struct})


70
# File 'lib/dry/struct.rb', line 70

defines :schema

#to_hashHash{Symbol => Object} Also known as: to_h

Converts the Dry::Struct to a hash with keys representing each attribute (as symbols) and their corresponding values

Examples:

class Book < Dry::Struct
  attribute :title, Types::Strict::String
  attribute :subtitle, Types::Strict::String.optional
end

rom_n_roda = Book.new(
  title: 'Web Development with ROM and Roda',
  subtitle: nil
)
rom_n_roda.to_hash
  #=> {title: 'Web Development with ROM and Roda', subtitle: nil}

Returns:

  • (Hash{Symbol => Object})


229
230
231
232
233
# File 'lib/dry/struct.rb', line 229

def to_hash
  self.class.schema.keys.each_with_object({}) do |key, result|
    result[key] = Hashify[self[key]]
  end
end