Class: Functional::FinalStruct
- Inherits:
-
Synchronization::Object
- Object
- Synchronization::Object
- Functional::FinalStruct
- Defined in:
- lib/functional/final_struct.rb
Overview
This is a write-once, read-many, thread safe object that can be used in concurrent systems. Thread safety guarantees cannot be made about objects contained within this object, however. Ruby variables are mutable references to mutable objects. This cannot be changed. The best practice it to only encapsulate immutable, frozen, or thread safe objects. Ultimately, thread safety is the responsibility of the programmer.
A variation on Ruby’s ‘OpenStruct` in which all fields are “final” (meaning that new fields can be arbitrarily added to a `FinalStruct` object but once set each field becomes immutable). Additionally, predicate methods exist for all fields and these predicates indicate if the field has been set.
There are two ways to initialize a ‘FinalStruct`: with zero arguments or with a `Hash` (or any other object that implements a `to_h` method). The only difference in behavior is that a `FinalStruct` initialized with a hash will pre-define and pre-populate attributes named for the hash keys and with values corresponding to the hash values.
Instance Method Summary collapse
-
#each_pair {|field, value| ... } ⇒ Enumerable
Calls the block once for each attribute, passing the key/value pair as parameters.
-
#eql?(other) ⇒ Boolean
(also: #==)
Compares this object and other for equality.
-
#fetch(field, default) ⇒ Object
Get the current value of the given field if already set else return the given default value.
-
#get(field) ⇒ Object
(also: #[])
Get the value of the given field.
-
#get_or_set(field, value) ⇒ Object
Get the current value of the given field if already set else set the value of the given field to the given value.
-
#initialize(attributes = {}) ⇒ FinalStruct
constructor
Creates a new ‘FinalStruct` object.
-
#set(field, value) ⇒ Object
(also: #[]=)
Set the value of the give field to the given value.
-
#set?(field) ⇒ Boolean
Check the internal hash to unambiguously verify that the given attribute has been set.
-
#to_h ⇒ Hash
Converts the ‘FinalStruct` to a `Hash` with keys representing each attribute (as symbols) and their corresponding values.
Constructor Details
#initialize(attributes = {}) ⇒ FinalStruct
Creates a new ‘FinalStruct` object. By default, the resulting `FinalStruct` object will have no attributes. The optional hash, if given, will generate attributes and values (can be a `Hash` or any object with a `to_h` method).
55 56 57 58 59 60 61 62 63 64 |
# File 'lib/functional/final_struct.rb', line 55 def initialize(attributes = {}) raise ArgumentError.new('attributes must be given as a hash or not at all') unless attributes.respond_to?(:to_h) super synchronize do @attribute_hash = {} attributes.to_h.each_pair do |field, value| ns_set_attribute(field, value) end end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(symbol, *args) ⇒ Object (private)
Check the method name and args for signatures matching potential final attribute reader, writer, and predicate methods. If the signature matches a reader or predicate, treat the attribute as unset. If the signature matches a writer, attempt to set the new attribute.
208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/functional/final_struct.rb', line 208 def method_missing(symbol, *args) if args.length == 1 && (match = /([^=]+)=$/.match(symbol)) set(match[1], args.first) elsif args.length == 0 && (match = /([^\?]+)\?$/.match(symbol)) set?(match[1]) elsif args.length == 0 get(symbol) else super end end |
Instance Method Details
#each_pair {|field, value| ... } ⇒ Enumerable
Calls the block once for each attribute, passing the key/value pair as parameters. If no block is given, an enumerator is returned instead.
139 140 141 142 143 144 145 146 |
# File 'lib/functional/final_struct.rb', line 139 def each_pair return enum_for(:each_pair) unless block_given? synchronize do @attribute_hash.each do |field, value| yield(field, value) end end end |
#eql?(other) ⇒ Boolean Also known as: ==
Compares this object and other for equality. A ‘FinalStruct` is `eql?` to other when other is a `FinalStruct` and the two objects have identical fields and values.
162 163 164 |
# File 'lib/functional/final_struct.rb', line 162 def eql?(other) other.is_a?(self.class) && to_h == other.to_h end |
#fetch(field, default) ⇒ Object
Get the current value of the given field if already set else return the given default value.
128 129 130 |
# File 'lib/functional/final_struct.rb', line 128 def fetch(field, default) synchronize { ns_attribute_has_been_set?(field) ? ns_get_attribute(field) : default } end |
#get(field) ⇒ Object Also known as: []
Get the value of the given field.
72 73 74 |
# File 'lib/functional/final_struct.rb', line 72 def get(field) synchronize { ns_get_attribute(field) } end |
#get_or_set(field, value) ⇒ Object
Get the current value of the given field if already set else set the value of the given field to the given value.
118 119 120 |
# File 'lib/functional/final_struct.rb', line 118 def get_or_set(field, value) synchronize { ns_attribute_has_been_set?(field) ? ns_get_attribute(field) : ns_set_attribute(field, value) } end |
#set(field, value) ⇒ Object Also known as: []=
Set the value of the give field to the given value.
It is a logical error to attempt to set a ‘final` field more than once, as this violates the concept of finality. Calling the method a second or subsequent time for a given field will result in an exception being raised.
90 91 92 93 94 95 96 97 98 |
# File 'lib/functional/final_struct.rb', line 90 def set(field, value) synchronize do if ns_attribute_has_been_set?(field) raise FinalityError.new("final accessor '#{field}' has already been set") else ns_set_attribute(field, value) end end end |
#set?(field) ⇒ Boolean
Check the internal hash to unambiguously verify that the given attribute has been set.
108 109 110 |
# File 'lib/functional/final_struct.rb', line 108 def set?(field) synchronize { ns_attribute_has_been_set?(field) } end |
#to_h ⇒ Hash
Converts the ‘FinalStruct` to a `Hash` with keys representing each attribute (as symbols) and their corresponding values.
152 153 154 |
# File 'lib/functional/final_struct.rb', line 152 def to_h synchronize { @attribute_hash.dup } end |