class Fear::Struct

Structs are like regular classes and good for modeling immutable data.

A minimal struct requires just a list of attributes:

User = Fear::Struct.with_attributes(:id, :email, :admin)
john = User.new(id: 2, email: 'john@example.com', admin: false)

john.email #=> 'john@example.com'

Instead of `.with_attributes` factory method you can use classic inheritance:

class User < Fear::Struct
  attribute :id
  attribute :email
  attribute :admin
end

Since structs are immutable, you are not allowed to reassign their attributes

john.email = ''john.doe@example.com'' #=> raises NoMethodError

Two structs of the same type with the same attributes are equal

john1 = User.new(id: 2, email: 'john@example.com', admin: false)
john2 = User.new(id: 2, admin: false, email: 'john@example.com')
john1 == john2 #=> true

You can create a shallow copy of a Struct by using copy method optionally changing its attributes.

john = User.new(id: 2, email: 'john@example.com', admin: false)
admin_john = john.copy(admin: true)

john.admin #=> false
admin_john.admin #=> true

It's possible to match against struct attributes. The following example extracts email from user only if user is admin

john = User.new(id: 2, email: 'john@example.com', admin: false)
john.match |m|
  m.xcase('Fear::Struct(_, email, true)') do |email|
    email
  end
end

Note, parameters got extracted in order they was defined.

Constants

INSPECT_TEMPLATE
MISSING_KEYWORDS_ERROR
UNKNOWN_KEYWORDS_ERROR

Public Class Methods

attribute(name) click to toggle source

Defines attribute

@param name [Symbol] @return [Symbol] attribute name

@example

class User < Fear::Struct
  attribute :id
  attribute :email
end
# File lib/fear/struct.rb, line 75
def attribute(name)
  name.to_sym.tap do |symbolized_name|
    @attributes << symbolized_name
    attr_reader symbolized_name
  end
end
attributes() click to toggle source

Members of this struct

@return [<Symbol>]

# File lib/fear/struct.rb, line 85
def attributes
  @attributes.dup
end
inherited(base) click to toggle source

@param base [Fear::Struct] @api private

# File lib/fear/struct.rb, line 59
def inherited(base)
  base.instance_variable_set(:@attributes, attributes)
  Fear.register_extractor(base, Fear.case(base, &:to_a).lift)
end
new(**attributes) click to toggle source

@param attributes [{Symbol => any}]

# File lib/fear/struct.rb, line 112
def initialize(**attributes)
  _check_missing_attributes!(attributes)
  _check_unknown_attributes!(attributes)

  @values = members.each_with_object([]) do |name, values|
    attributes.fetch(name).tap do |value|
      _set_attribute(name, value)
      values << value
    end
  end
end
with_attributes(*members, &block) click to toggle source

Creates new struct with given attributes @param members [<Symbol>] @return [Fear::Struct]

@example

User = Fear::Struct.with_attributes(:id, :email, :admin) do
  def admin?
    @admin
  end
end
# File lib/fear/struct.rb, line 100
def with_attributes(*members, &block)
  members = members
  block = block

  Class.new(self) do
    members.each { |member| attribute(member) }
    class_eval(&block) if block
  end
end

Public Instance Methods

==(other) click to toggle source

@param other [any] @return [Boolean]

# File lib/fear/struct.rb, line 190
def ==(other)
  other.is_a?(other.class) && to_h == other.to_h
end
copy(**attributes) click to toggle source

Creates a shallow copy of this struct optionally changing the attributes arguments. @param attributes [{Symbol => any}]

@example

User = Fear::Struct.new(:id, :email, :admin)
john = User.new(id: 2, email: 'john@example.com', admin: false)
john.admin #=> false
admin_john = john.copy(admin: true)
admin_john.admin #=> true
# File lib/fear/struct.rb, line 134
def copy(**attributes)
  self.class.new(to_h.merge(attributes))
end
deconstruct_keys(keys) click to toggle source

@param keys [Hash, nil] @return [Hash]

# File lib/fear/struct.rb, line 240
def deconstruct_keys(keys)
  if keys
    to_h.slice(*(self.class.attributes & keys))
  else
    to_h
  end
end
inspect() click to toggle source

@return [String]

@example

User = Fear::Struct.with_attributes(:id, :email)
user = User.new(id: 2, email: 'john@exmaple.com')
user.inspect #=> "<#Fear::Struct User id=2, email=>'john@exmaple.com'>"
# File lib/fear/struct.rb, line 204
def inspect
  attributes = to_h.map { |key, value| "#{key}=#{value.inspect}" }.join(", ")

  format(INSPECT_TEMPLATE, class_name: self.class.name, attributes: attributes)
end
Also aliased as: to_s
members() click to toggle source

Returns the struct attributes as an array of symbols @return [<Symbol>]

@example

User = Fear::Struct.new(:id, :email, :admin)
john = User.new(email: 'john@example.com', admin: false, id: 2)
john.attributes #=> [:id, :email, :admin]
# File lib/fear/struct.rb, line 146
def members
  self.class.attributes
end
to_a() click to toggle source

Returns the values for this struct as an Array. @return [Array]

@example

User = Fear::Struct.new(:id, :email, :admin)
john = User.new(email: 'john@example.com', admin: false, id: 2)
john.to_a #=> [2, 'john@example.com', false]
# File lib/fear/struct.rb, line 158
def to_a
  @values.dup
end
to_h(&block) click to toggle source

@overload to_h()

Returns a Hash containing the names and values for the struct's attributes
@return [{Symbol => any}]

@overload to_h(&block)

Applies block to pairs of name name and value and use them to construct hash
@yieldparam pair [<Symbol, any>] yields pair of name name and value
@return [{Symbol => any}]

@example

User = Fear::Struct.new(:id, :email, :admin)
john = User.new(email: 'john@example.com', admin: false, id: 2)
john.to_h #=> {id: 2, email: 'john@example.com', admin: false}
john.to_h do |key, value|
  [key.to_s, value]
end #=> {'id' => 2, 'email' => 'john@example.com', 'admin' => false}
# File lib/fear/struct.rb, line 179
def to_h(&block)
  pairs = members.zip(@values)
  if block_given?
    Hash[pairs.map(&block)]
  else
    Hash[pairs]
  end
end
to_s()
Alias for: inspect

Private Instance Methods

_check_missing_attributes!(provided_attributes) click to toggle source
# File lib/fear/struct.rb, line 214
        def _check_missing_attributes!(provided_attributes)
  missing_attributes = members - provided_attributes.keys

  unless missing_attributes.empty?
    raise ArgumentError, format(MISSING_KEYWORDS_ERROR, keywords: missing_attributes.join(", "))
  end
end
_check_unknown_attributes!(provided_attributes) click to toggle source
# File lib/fear/struct.rb, line 225
        def _check_unknown_attributes!(provided_attributes)
  unknown_attributes = provided_attributes.keys - members

  unless unknown_attributes.empty?
    raise ArgumentError, format(UNKNOWN_KEYWORDS_ERROR, keywords: unknown_attributes.join(", "))
  end
end
_set_attribute(name, value) click to toggle source

@return [void]

# File lib/fear/struct.rb, line 234
        def _set_attribute(name, value)
  instance_variable_set(:"@#{name}", value)
end