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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**attributes) ⇒ Struct

Returns a new instance of Struct.

Parameters:

  • attributes ({Symbol => any})


101
102
103
104
105
106
107
108
109
110
111
# File 'lib/fear/struct.rb', line 101

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



64
65
66
67
68
69
# File 'lib/fear/struct.rb', line 64

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>)


74
75
76
# File 'lib/fear/struct.rb', line 74

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:



49
50
51
# File 'lib/fear/struct.rb', line 49

def inherited(base)
  base.instance_variable_set(:@attributes, attributes)
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:



89
90
91
92
93
94
95
96
97
# File 'lib/fear/struct.rb', line 89

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)


179
180
181
# File 'lib/fear/struct.rb', line 179

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})


123
124
125
# File 'lib/fear/struct.rb', line 123

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

#deconstruct_keys(keys) ⇒ Hash

Parameters:

  • keys (Hash, nil)

Returns:

  • (Hash)


229
230
231
232
233
234
235
# File 'lib/fear/struct.rb', line 229

def deconstruct_keys(keys)
  if keys
    to_h.slice(*(self.class.attributes & keys))
  else
    to_h
  end
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)


193
194
195
196
197
# File 'lib/fear/struct.rb', line 193

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>)


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

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)


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

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})


168
169
170
171
172
173
174
175
# File 'lib/fear/struct.rb', line 168

def to_h(&block)
  pairs = members.zip(@values)
  if block_given?
    Hash[pairs.map(&block)]
  else
    Hash[pairs]
  end
end