- Support for wrapping constructors and fallbacks, see release notes for dry-types 1.5.0 (@flash-gordon)
- Improvements of the attribute DSL, now it's possible to use optional structs as a base class (@flash-gordon)
class User < Dry::Struct attribute :name, Types::String attribute :address, Dry::Struct.optional do attribute :city, Types::String end end User.new(name: "John", address: nil) # => #<User name="John" address=nil>
-
Nested structures will reuse type and key transformations from the enclosing struct (@flash-gordon)
class User < Dry::Struct transform_keys(&:to_sym) attribute :name, Types::String attribute :address do # this struct will inherit transform_keys(&:to_sym) attribute :city, Types::String end # nested struct will _not_ transform keys because a parent # struct is given attribute :contacts, Dry::Struct do attribute :email, Types::String end end
-
Dry::Struct::Constructor
finally acts like a fully-featured type (@flash-gordon) -
Dry::Struct.abstract
declares a struct class as abstract. An abstract class is used as a default superclass for nested structs (@flash-gordon) -
Dry::Struct.to_ast
and struct compiler (@flash-gordon) -
Struct composition with
Dry::Struct.attributes_from
. It's more flexible than inheritance (@waiting-for-dev + @flash-gordon)class Address < Dry::Struct attribute :city, Types::String attribute :zipcode, Types::String end class Buyer < Dry::Struct attribute :name, Types::String attributes_from Address end class Seller < Dry::Struct attribute :name, Types::String attribute :email, Types::String attributes_from Address end
- [internal] metadata is now stored inside schema (@flash-gordon)
Dry::Struct::Value
is deprecated.Dry::Struct
instances were never meant to be mutable, we have no support for this. The only difference betweenDry::Struct
andDry::Struct::Value
is that the latter is deeply frozen. Freezing objects slows the code down and gives you very little benefit in return. If you have a use case forValue
, it won't be hard to roll your own solution using ice_nine (flash-gordon)- In the thread of the previous change, structs now use immutable equalizer. This means
Struct#hash
memoizes its value after the first invocation. Depending on the case, this may speed up your code significantly (flash-gordon)
-
Pattern matching syntax is simplified with
deconstruct_keys
(k-tsj)User = Dry.Struct(name: 'string', email: 'string') user = User.new(name: 'John Doe', email: '[email protected]') case user in User(name: 'John Doe', email:) puts email else puts 'Not John' end
See more examples in the specs.
- Experimental support for pattern matching 🎉 (flash-gordon)
Struct.call
now accepts an optional block that will be called on failed coercion. This behavior is consistent with dry-types 1.0. Note that.new
doesn't take a block (flash-gordon)User = Dry::Struct(name: 'string') User.(1) { :oh_no } # => :oh_no
valid?
and===
behave differently,===
works the same wayClass#===
does andvalid?
checks if the value can be coerced to the struct (flash-gordon)
- [BREAKING]
Struct.input
was renamedStruct.schema
, henceStruct.schema
returns an instance ofDry::Types::Hash::Schema
rather than aHash
. Schemas are also implementingEnumerable
but they iterate over key types. New API:To get a type by its name useUser.schema.each do |key| puts "Key name: #{ key.name }" puts "Key type: #{ key.type }" end
.key
:User.schema.key(:id) # => #<Dry::Types::Hash::Key ...>
- [BREAKING]
transform_types
now passes one argument to the block, an instance of theKey
type. Combined with the new API from dry-types it simplifies declaring omittable keys:class StructWithOptionalKeys < Dry::Struct transform_types { |key| key.required(false) } # or simply transform_types(&:omittable) end
Dry::Stuct#new
is now more efficient for partial updates (flash-gordon)- Ruby 2.3 is EOL and not officially supported. It may work but we don't test it.
-
Struct.attribute?
is an easy way to define omittable attributes (flash-gordon):class User < Dry::Struct attribute :name, Types::Strict::String attribute? :email, Types::Strict::String end # User.new(name: 'John') # => #<User name="John">
Struct#to_h
recursively converts hash values to hashes, this was done to be consistent with current behavior for arrays (oeoeaio + ZimbiX)
- [BREAKING]
Struct.attribute?
in the old sense is deprecated, usehas_attribute?
as a replacement
- Pretty print extension (ojab)
Dry::Struct.load_extensions(:pretty_print) PP.pp(user) #<Test::User name="Jane", age=21, address=#<Test::Address city="NYC", zipcode="123">>
- Constant resolution is now restricted to the current module when structs are automatically defined using the block syntax. This shouldn't break any existing code (piktur)
-
Dry::Struct.transform_types
accepts a block which is yielded on every type to add. Since types aredry-types
' objects that come with a robust DSL it's rather simple to restore the behavior ofconstructor_type
. See dry-rb#64 for details (flash-gordon)Example: evaluate defaults on
nil
valuesclass User < Dry::Struct transform_types do |type| type.constructor { |value| value.nil? ? Undefined : value } end end
-
Data::Struct.transform_keys
accepts a block/proc that transforms keys of input hashes. The most obvious usage is simbolization but arbitrary transformations are allowed (flash-gordon) -
Dry.Struct
builds a struct by a hash of attribute names and types (citizen428)User = Dry::Struct(name: 'strict.string') do attribute :email, 'strict.string' end
-
Support for
Struct.meta
, note that.meta
returns a new class (flash-gordon)class User < Dry::Struct attribute :name, Dry::Types['strict.string'] end UserWithMeta = User.meta(foo: :bar) User.new(name: 'Jade').class == UserWithMeta.new(name: 'Jade').class # => false
-
Struct.attribute
yields a block with definition for nested structs. It defines a nested constant for the new struct and supports arrays (AMHOL + flash-gordon)class User < Dry::Struct attribute :name, Types::Strict::String attribute :address do attribute :country, Types::Strict::String attribute :city, Types::Strict::String end attribute :accounts, Types::Strict::Array do attribute :currency, Types::Strict::String attribute :balance, Types::Strict::Decimal end end # ^This automatically defines User::Address and User::Account
- Adding a new attribute invalidates
attribute_names
(flash-gordon) - Struct classes track subclasses and define attributes in them, now it doesn't matter whether you define attributes first and then subclass or vice versa. Note this can lead to memory leaks in Rails environment when struct classes are reloaded (flash-gordon)
Struct#new
doesn't call.to_hash
recursively (flash-gordon)
- Attribute readers don't override existing instance methods (solnic)
Struct#new
uses raw attributes instead of method calls, this makes the behavior consistent with the change above (flash-gordon)constructor_type
now actively rejects:weak
and:symbolized
values (GustavoCaso)
Struct.constructor
that makes dry-struct more aligned with dry-types; now you can have a struct with a custom constructor that will be called before calling thenew
method (v-kolesnikov)Struct.attribute?
andStruct.attribute_names
for introspecting struct attributes (flash-gordon)Struct#__new__
is a safe-to-use-in-gems alias forStruct#new
(flash-gordon)
Dry::Struct#new
method to return new instance with applied changeset (Kukunin)
.[]
and.call
does not coerce subclass to superclass anymore (Kukunin)- Raise ArgumentError when attribute type is a string and no value provided is for
new
(GustavoCaso)
.new
without arguments doesn't use nil as an input for non-default types anymore (flash-gordon)
- Fixed
Dry::Struct::Value
which appeared to be broken in the last release (flash-gordon)
- Struct attributes can be overridden in a subclass (flash-gordon)
- Make
Dry::Struct
act as a constrained type. This fixes the behavior of sum types containing structs (flash-gordon)
:strict_with_defaults
constructor type (backus)
- [BREAKING]
:strict
was renamed to:permissive
as it ignores missing keys (backus) - [BREAKING]
:strict
now raises on unexpected keys (backus) - Structs no longer auto-register themselves in the types container as they implement
Type
interface and we don't have to wrap them inType::Definition
(flash-gordon)
Initial release of code imported from dry-types