Evil::Struct
¶ ↑
Nested structure with type constraints, based on the dry-initializer DSL.
Installation¶ ↑
Add this line to your application's Gemfile:
gem 'evil-struct'
And then execute:
$ bundle
Or install it yourself as:
$ gem install evil-struct
Synopsis¶ ↑
The structure is like dry-struct except for it controls optional attributes and default values aside of type constraints.
Its DSL is taken from dry-initializer. Its method attribute
is just an alias for option
.
require "evil-struct" require "dry-types" class Product < Evil::Struct attribute :title attribute :price, Dry::Types["coercible.float"] attribute :quantity, Dry::Types["coercible.int"], default: proc { 0 } # shared options attributes optional: true do attribute :subtitle attribute :description end end # Accepts both symbolic and string keys. # Class methods `[]`, `call`, and `load` are just aliases for `new` product = Product[title: "apple", "price" => "10.9", description: "a fruit"] # Attributes are available via methods or `[]` product.title # => "apple" product[:price] # => 10.9 product["quantity"] # => 0 product.description # => "a fruit" # unassigned value differs from `nil` product.subtitle # => Dry::Initializer::UNDEFINED # Raises in case a mandatory value not assigned Product.new # BOOM! because neither title, nor price are assigned # Hashifies all attributes except for undefined subtitle # You can use `dump` as an alias for `to_h` product.to_h # => { title: "apple", price: 10.9, description: "a fruit", quantity: 0 } # The structure is comparable to any object that responds to `#to_h` product == { title: "apple", price: 10.9, description: "a fruit", quantity: 0 } # => true
The structure is designed for immutability. That's why it doesn't contain writers (but you can define them by yourself via attr_writer
).
Instead of mutating current instance, you can merge another hash to the object via merge
or deep_merge
.
new_product = product.merge(title: "orange") new_product.class # => Product new_product.to_h # => { title: "orange", price: 10.9, description: "a fruit", quantity: 0 } # you can merge any object that responds to `to_h` or `to_hash` other = Product[title: "orange", price: 12] new_product = product.merge(other) new_product.to_h # => { title: "orange", price: 12, description: "a fruit", quantity: 0 } # merge_deeply (deep_merge) gracefully merge nested hashes or hashified objects grape = Product.new title: "grape", price: 30, description: { country: "FR", year: 2016, sort: "Merlot" } new_grape = grape.merge_deeply description: { year: 2017 } new_grape.to_h # => { # title: "grape", # price: 30, # description: { country: "FR", year: 2017, sort: "Merlot" } # }
Compatibility¶ ↑
Tested under rubies compatible to MRI 2.2+.
Contributing¶ ↑
-
Create your feature branch (
git checkout -b my-new-feature
) -
Add tests for it
-
Commit your changes (
git commit -am '[UPDATE] Add some feature'
) -
Push to the branch (
git push origin my-new-feature
) -
Create a new Pull Request
License¶ ↑
The gem is available as open source under the terms of the MIT License.