Class: Fear::Struct

Inherits:
Object
  • Object
show all
Defined in:
lib/fear/struct.rb

Overview

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: '[email protected]', admin: false)

john.email #=> '[email protected]'

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 = ''[email protected]'' #=> raises NoMethodError

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

john1 = User.new(id: 2, email: '[email protected]', admin: false)
john2 = User.new(id: 2, admin: false, email: '[email protected]')
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: '[email protected]', 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: '[email protected]', 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.

Constant Summary collapse

INSPECT_TEMPLATE =
"<#Fear::Struct %{class_name} %{attributes}>"
MISSING_KEYWORDS_ERROR =
"missing keywords: %{keywords}"
UNKNOWN_KEYWORDS_ERROR =
"unknown keywords: %{keywords}"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**attributes) ⇒ Struct

Returns a new instance of Struct.

Parameters:

  • attributes ({Symbol => any})


112
113
114
115
116
117
118
119
120
121
122
# 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

Class Method Details

.attribute(name) ⇒ Symbol

Defines attribute

Examples:

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

Parameters:

  • name (Symbol)

Returns:

  • (Symbol)

    attribute name



75
76
77
78
79
80
# 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<Symbol>

Members of this struct

Returns:

  • (<Symbol>)


85
86
87
# File 'lib/fear/struct.rb', line 85

def attributes
  @attributes.dup
end

.inherited(base) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:



59
60
61
62
# 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

.with_attributes(*members, &block) ⇒ Fear::Struct

Creates new struct with given attributes

Examples:

User = Fear::Struct.with_attributes(:id, :email, :admin) do
  def admin?
    @admin
  end
end

Parameters:

  • members (<Symbol>)

Returns:



100
101
102
103
104
105
106
107
108
# 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

Instance Method Details

#==(other) ⇒ Boolean

Parameters:

  • other (any)

Returns:

  • (Boolean)


190
191
192
# File 'lib/fear/struct.rb', line 190

def ==(other)
  other.is_a?(other.class) && to_h == other.to_h
end

#copy(**attributes) ⇒ Object

Creates a shallow copy of this struct optionally changing the attributes arguments.

Examples:

User = Fear::Struct.new(:id, :email, :admin)
john = User.new(id: 2, email: '[email protected]', admin: false)
john.admin #=> false
admin_john = john.copy(admin: true)
admin_john.admin #=> true

Parameters:

  • attributes ({Symbol => any})


134
135
136
# File 'lib/fear/struct.rb', line 134

def copy(**attributes)
  self.class.new(to_h.merge(attributes))
end

#inspectString Also known as: to_s

Examples:

User = Fear::Struct.with_attributes(:id, :email)
user = User.new(id: 2, email: '[email protected]')
user.inspect #=> "<#Fear::Struct User id=2, email=>'[email protected]'>"

Returns:

  • (String)


203
204
205
206
207
# File 'lib/fear/struct.rb', line 203

def inspect
  attributes = to_h.map { |key, value| "#{key}=#{value.inspect}" }.join(", ")

  format(INSPECT_TEMPLATE, class_name: self.class.name, attributes: attributes)
end

#members<Symbol>

Returns the struct attributes as an array of symbols

Examples:

User = Fear::Struct.new(:id, :email, :admin)
john = User.new(email: '[email protected]', admin: false, id: 2)
john.attributes #=> [:id, :email, :admin]

Returns:

  • (<Symbol>)


146
147
148
# File 'lib/fear/struct.rb', line 146

def members
  self.class.attributes
end

#to_aArray

Returns the values for this struct as an Array.

Examples:

User = Fear::Struct.new(:id, :email, :admin)
john = User.new(email: '[email protected]', admin: false, id: 2)
john.to_a #=> [2, '[email protected]', false]

Returns:

  • (Array)


158
159
160
# File 'lib/fear/struct.rb', line 158

def to_a
  @values.dup
end

#to_h{Symbol => any} #to_h {|pair| ... } ⇒ {Symbol => any}

Examples:

User = Fear::Struct.new(:id, :email, :admin)
john = User.new(email: '[email protected]', admin: false, id: 2)
john.to_h #=> {id: 2, email: '[email protected]', admin: false}
john.to_h do |key, value|
  [key.to_s, value]
end #=> {'id' => 2, 'email' => '[email protected]', 'admin' => false}

Overloads:

  • #to_h{Symbol => any}

    Returns a Hash containing the names and values for the struct’s attributes

    Returns:

    • ({Symbol => any})
  • #to_h {|pair| ... } ⇒ {Symbol => any}

    Applies block to pairs of name name and value and use them to construct hash

    Yield Parameters:

    • pair (<Symbol, any>)

      yields pair of name name and value

    Returns:

    • ({Symbol => any})


179
180
181
182
183
184
185
186
# 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