class Evil::Struct
Nested structure with type constraints, based on the `dry-initializer` DSL
Public Class Methods
@!method attributes(options) Shares options between definitions made inside the block
@example
attributes optional: true do attribute :foo attribute :bar end
@option options (see attribute) @return [self] itself
# File lib/evil/struct.rb, line 48 def attributes(**options, &block) Attributes.call(self, options, &block) self end
Returns the list of defined attributes
@return [Array<Symbol>]
# File lib/evil/struct.rb, line 57 def list_of_attributes @list_of_attributes ||= [] end
Builds a struct from value that respond to `to_h` or `to_hash`
@param [#to_h, to_hash
] value (nil) @return [Evil::Struct]
@alias :call @alias :[] @alias :load
# File lib/evil/struct.rb, line 20 def new(value = {}) value if value.instance_of? self.class hash = value if value.is_a? Hash hash ||= value.to_h if value.respond_to? :to_h hash ||= value.to_hash if value.respond_to? :to_hash hash_with_symbolic_keys = hash.each_with_object({}) do |(key, val), obj| obj[key.to_sym] = val end super hash_with_symbolic_keys end
@!method attribute(name, type = nil, options) Declares the attribute
@param [#to_sym] name The name of the key @param [#call] type (nil) The type constraint @option options [#call] :type Type constraint (alternative syntax) @option options [#to_sym] :as The name of the attribute @option options [Proc] :default Block returning a default value @option options [Boolean] :optional (nil) Whether key is optional @return [self]
@alias :option @alias :param
# File lib/evil/struct.rb, line 75 def option(name, type = nil, as: nil, **opts) super.tap { list_of_attributes << (as || name).to_sym } self end
Private Class Methods
# File lib/evil/struct.rb, line 84 def inherited(klass) super klass.instance_variable_set :@list_of_attributes, list_of_attributes.dup end
Public Instance Methods
Checks an equality to other object that respond to `to_h` or `to_hash`
@param [Object] other @return [Boolean]
# File lib/evil/struct.rb, line 95 def ==(other) if other&.respond_to?(:to_h) to_h == other.to_h elsif other.respond_to?(:to_hash) to_h == other.to_hash else false end end
Shallowly merges other object to the current struct
@example
class User < Evil::Struct attribute :name attribute :age end joe_at_3 = User.new(name: "Joe", age: 3) joe_at_4 = joe_at_3.merge(age: 4) joe_at_4.name # => "Joe" joe_at_4.age # => 4
@param [Hash, to_h
, to_hash
] other @return [self.class] new instance of the current class
# File lib/evil/struct.rb, line 159 def merge(other) self.class[Utils.merge(to_h, other)] end
Deeply merges other object to the current struct
It iterates through hashes and objects responding to `to_h` and `to_hash`. The iteration stops when any non-hash value reached.
@example
class User < Evil::Struct attribute :info attribute :meta end user = User.new info: { names: [{ first: "Joe", last: "Doe" }], age: 33 }, meta: { type: :admin } user.merge info: { names: [{ first: "John" }] }, meta: { "role" => :cto } user.to_h # => { # info: { names: [{ first: "John" }], age: 33 }, # meta: { type: :admin, role: :cto } # }
@param [Hash, to_h
, to_hash
] other @return [self.class] new instance of the current class
@alias :deep_merge
# File lib/evil/struct.rb, line 187 def merge_deeply(other) self.class[Utils.merge_deeply(self, other)] end
Converts nested structure to hash
Makes conversion through nested hashes, arrays, enumerables, as well as trhough values that respond to `to_a`, `to_h`, and `to_hash`. Doesn't convert `nil`.
@return [Hash]
@alias :to_hash @alias :dump
# File lib/evil/struct.rb, line 116 def to_h self.class.list_of_attributes.each_with_object({}) do |key, hash| val = instance_variable_get :"@#{key}" hash[key] = Utils.hashify(val) unless val == Dry::Initializer::UNDEFINED end end