functional-ruby 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +14 -12
- data/doc/memo.md +192 -0
- data/doc/pattern_matching.md +481 -0
- data/doc/protocol.md +219 -0
- data/doc/record.md +247 -0
- data/lib/functional/abstract_struct.rb +8 -8
- data/lib/functional/delay.rb +31 -38
- data/lib/functional/either.rb +48 -45
- data/lib/functional/final_struct.rb +23 -34
- data/lib/functional/final_var.rb +20 -21
- data/lib/functional/memo.rb +33 -24
- data/lib/functional/method_signature.rb +1 -2
- data/lib/functional/option.rb +7 -7
- data/lib/functional/pattern_matching.rb +12 -10
- data/lib/functional/protocol.rb +2 -4
- data/lib/functional/protocol_info.rb +5 -3
- data/lib/functional/record.rb +82 -16
- data/lib/functional/synchronization.rb +88 -0
- data/lib/functional/tuple.rb +14 -4
- data/lib/functional/type_check.rb +0 -2
- data/lib/functional/union.rb +5 -4
- data/lib/functional/value_struct.rb +5 -3
- data/lib/functional/version.rb +1 -1
- data/spec/functional/complex_pattern_matching_spec.rb +1 -2
- data/spec/functional/configuration_spec.rb +0 -2
- data/spec/functional/delay_spec.rb +0 -2
- data/spec/functional/either_spec.rb +0 -1
- data/spec/functional/final_struct_spec.rb +0 -1
- data/spec/functional/final_var_spec.rb +0 -2
- data/spec/functional/memo_spec.rb +7 -10
- data/spec/functional/option_spec.rb +0 -1
- data/spec/functional/pattern_matching_spec.rb +0 -1
- data/spec/functional/protocol_info_spec.rb +0 -2
- data/spec/functional/protocol_spec.rb +1 -3
- data/spec/functional/record_spec.rb +170 -87
- data/spec/functional/tuple_spec.rb +0 -1
- data/spec/functional/type_check_spec.rb +0 -2
- data/spec/functional/union_spec.rb +0 -1
- data/spec/functional/value_struct_spec.rb +0 -1
- metadata +14 -29
- data/doc/memo.txt +0 -192
- data/doc/pattern_matching.txt +0 -485
- data/doc/protocol.txt +0 -221
- data/doc/record.txt +0 -207
- data/doc/thread_safety.txt +0 -17
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
require 'functional/protocol'
|
2
|
+
require 'functional/synchronization'
|
2
3
|
|
3
4
|
Functional::SpecifyProtocol(:Struct) do
|
4
5
|
instance_method :fields
|
@@ -11,8 +12,7 @@ end
|
|
11
12
|
module Functional
|
12
13
|
|
13
14
|
# An abstract base class for immutable struct classes.
|
14
|
-
#
|
15
|
-
# @since 1.0.0
|
15
|
+
# @!visibility private
|
16
16
|
module AbstractStruct
|
17
17
|
|
18
18
|
# @return [Array] the values of all record fields in order, frozen
|
@@ -94,7 +94,7 @@ module Functional
|
|
94
94
|
# Set the internal data hash to a copy of the given hash and freeze it.
|
95
95
|
# @param [Hash] data the data hash
|
96
96
|
#
|
97
|
-
# @!visibility private
|
97
|
+
# @!visibility private
|
98
98
|
def set_data_hash(data)
|
99
99
|
@data = data.dup.freeze
|
100
100
|
end
|
@@ -102,7 +102,7 @@ module Functional
|
|
102
102
|
# Set the internal values array to a copy of the given array and freeze it.
|
103
103
|
# @param [Array] values the values array
|
104
104
|
#
|
105
|
-
# @!visibility private
|
105
|
+
# @!visibility private
|
106
106
|
def set_values_array(values)
|
107
107
|
@values = values.dup.freeze
|
108
108
|
end
|
@@ -117,9 +117,9 @@ module Functional
|
|
117
117
|
# @return [Functional::AbstractStruct, Array] the new class and the
|
118
118
|
# (possibly) updated fields array
|
119
119
|
#
|
120
|
-
# @!visibility private
|
120
|
+
# @!visibility private
|
121
121
|
def self.define_class(parent, datatype, fields)
|
122
|
-
struct = Class.new{ include AbstractStruct }
|
122
|
+
struct = Class.new(Functional::Synchronization::Object){ include AbstractStruct }
|
123
123
|
if fields.first.is_a? String
|
124
124
|
parent.const_set(fields.first, struct)
|
125
125
|
fields = fields[1, fields.length-1]
|
@@ -157,7 +157,7 @@ module Functional
|
|
157
157
|
attr_writer :datatype
|
158
158
|
|
159
159
|
fields = [].freeze
|
160
|
-
datatype = :struct
|
160
|
+
datatype = :struct
|
161
161
|
end
|
162
162
|
end
|
163
163
|
end
|
data/lib/functional/delay.rb
CHANGED
@@ -1,26 +1,25 @@
|
|
1
|
-
require '
|
1
|
+
require 'functional/synchronization'
|
2
2
|
|
3
3
|
module Functional
|
4
4
|
|
5
|
-
# Lazy evaluation of a block yielding an immutable result. Useful for
|
6
|
-
# operations that may never be needed.
|
7
|
-
#
|
5
|
+
# Lazy evaluation of a block yielding an immutable result. Useful for
|
6
|
+
# expensive operations that may never be needed.
|
7
|
+
#
|
8
8
|
# When a `Delay` is created its state is set to `pending`. The value and
|
9
9
|
# reason are both `nil`. The first time the `#value` method is called the
|
10
10
|
# enclosed opration will be run and the calling thread will block. Other
|
11
11
|
# threads attempting to call `#value` will block as well. Once the operation
|
12
12
|
# is complete the *value* will be set to the result of the operation or the
|
13
13
|
# *reason* will be set to the raised exception, as appropriate. All threads
|
14
|
-
# blocked on `#value` will return. Subsequent calls to `#value` will
|
15
|
-
# return the cached value. The operation will only be run once.
|
16
|
-
# any side effects created by the operation will only happen
|
17
|
-
#
|
18
|
-
# @see http://clojuredocs.org/clojure_core/clojure.core/delay Clojure delay
|
19
|
-
#
|
20
|
-
# @since 1.0.0
|
14
|
+
# blocked on `#value` will return. Subsequent calls to `#value` will
|
15
|
+
# immediately return the cached value. The operation will only be run once.
|
16
|
+
# This means that any side effects created by the operation will only happen
|
17
|
+
# once as well.
|
21
18
|
#
|
22
19
|
# @!macro thread_safe_immutable_object
|
23
|
-
|
20
|
+
#
|
21
|
+
# @see http://clojuredocs.org/clojure_core/clojure.core/delay Clojure delay
|
22
|
+
class Delay < Synchronization::Object
|
24
23
|
|
25
24
|
# Create a new `Delay` in the `:pending` state.
|
26
25
|
#
|
@@ -29,79 +28,72 @@ module Functional
|
|
29
28
|
# @raise [ArgumentError] if no block is given
|
30
29
|
def initialize(&block)
|
31
30
|
raise ArgumentError.new('no block given') unless block_given?
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
super
|
32
|
+
synchronize do
|
33
|
+
@state = :pending
|
34
|
+
@task = block
|
35
|
+
end
|
35
36
|
end
|
36
37
|
|
37
38
|
# Current state of block processing.
|
38
39
|
#
|
39
40
|
# @return [Symbol] the current state of block processing
|
40
41
|
def state
|
41
|
-
@
|
42
|
-
@state
|
43
|
-
ensure
|
44
|
-
@mutex.unlock
|
42
|
+
synchronize{ @state }
|
45
43
|
end
|
46
44
|
|
47
45
|
# The exception raised when processing the block. Returns `nil` if the
|
48
46
|
# operation is still `:pending` or has been `:fulfilled`.
|
49
47
|
#
|
50
|
-
# @return [StandardError] the exception raised when processing the block
|
48
|
+
# @return [StandardError] the exception raised when processing the block
|
49
|
+
# else nil.
|
51
50
|
def reason
|
52
|
-
@
|
53
|
-
@reason
|
54
|
-
ensure
|
55
|
-
@mutex.unlock
|
51
|
+
synchronize{ @reason }
|
56
52
|
end
|
57
53
|
|
58
54
|
# Return the (possibly memoized) value of the delayed operation.
|
59
|
-
#
|
55
|
+
#
|
60
56
|
# If the state is `:pending` then the calling thread will block while the
|
61
57
|
# operation is performed. All other threads simultaneously calling `#value`
|
62
58
|
# will block as well. Once the operation is complete (either `:fulfilled` or
|
63
59
|
# `:rejected`) all waiting threads will unblock and the new value will be
|
64
60
|
# returned.
|
65
61
|
#
|
66
|
-
# If the state is not `:pending` when `#value` is called the (possibly
|
67
|
-
# value will be returned without blocking and without performing
|
68
|
-
# again.
|
62
|
+
# If the state is not `:pending` when `#value` is called the (possibly
|
63
|
+
# memoized) value will be returned without blocking and without performing
|
64
|
+
# the operation again.
|
69
65
|
#
|
70
66
|
# @return [Object] the (possibly memoized) result of the block operation
|
71
67
|
def value
|
72
|
-
|
73
|
-
execute_task_once
|
74
|
-
@value
|
75
|
-
ensure
|
76
|
-
@mutex.unlock
|
68
|
+
synchronize{ execute_task_once }
|
77
69
|
end
|
78
70
|
|
79
71
|
# Has the delay been fulfilled?
|
80
72
|
# @return [Boolean]
|
81
73
|
def fulfilled?
|
82
|
-
state == :fulfilled
|
74
|
+
synchronize{ @state == :fulfilled }
|
83
75
|
end
|
84
76
|
alias_method :value?, :fulfilled?
|
85
77
|
|
86
78
|
# Has the delay been rejected?
|
87
79
|
# @return [Boolean]
|
88
80
|
def rejected?
|
89
|
-
state == :rejected
|
81
|
+
synchronize{ @state == :rejected }
|
90
82
|
end
|
91
83
|
alias_method :reason?, :rejected?
|
92
84
|
|
93
85
|
# Is delay completion still pending?
|
94
86
|
# @return [Boolean]
|
95
87
|
def pending?
|
96
|
-
state == :pending
|
88
|
+
synchronize{ @state == :pending }
|
97
89
|
end
|
98
90
|
|
99
91
|
protected
|
100
92
|
|
101
93
|
# @!visibility private
|
102
94
|
#
|
103
|
-
# Execute the enclosed task then cache and return the result if
|
104
|
-
#
|
95
|
+
# Execute the enclosed task then cache and return the result if the current
|
96
|
+
# state is pending. Otherwise, return the cached result.
|
105
97
|
#
|
106
98
|
# @return [Object] the result of the block operation
|
107
99
|
def execute_task_once
|
@@ -114,6 +106,7 @@ module Functional
|
|
114
106
|
@state = :rejected
|
115
107
|
end
|
116
108
|
end
|
109
|
+
@value
|
117
110
|
end
|
118
111
|
end
|
119
112
|
end
|
data/lib/functional/either.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'functional/abstract_struct'
|
2
|
+
require 'functional/protocol'
|
3
|
+
require 'functional/synchronization'
|
3
4
|
|
4
5
|
Functional::SpecifyProtocol(:Either) do
|
5
6
|
instance_method :left, 0
|
@@ -10,45 +11,47 @@ end
|
|
10
11
|
|
11
12
|
module Functional
|
12
13
|
|
13
|
-
# The `Either` type represents a value of one of two possible types (a
|
14
|
-
# It is an immutable structure that contains one and only one
|
15
|
-
# be stored in one of two virtual position, `left` or
|
16
|
-
# context for the encapsulated data.
|
14
|
+
# The `Either` type represents a value of one of two possible types (a
|
15
|
+
# disjoint union). It is an immutable structure that contains one and only one
|
16
|
+
# value. That value can be stored in one of two virtual position, `left` or
|
17
|
+
# `right`. The position provides context for the encapsulated data.
|
17
18
|
#
|
18
|
-
# One of the main uses of `Either` is as a return value that can indicate
|
19
|
-
# success or failure. Object oriented programs generally report errors
|
20
|
-
# either state or exception handling, neither of which work well in
|
21
|
-
# programming. In the former case, a method is called on an object
|
22
|
-
# error occurs the state of the object is updated to reflect the
|
23
|
-
# not translate well to functional programming because they
|
24
|
-
# mutable objects. In the latter, an exception handling block
|
25
|
-
# logic when an exception is thrown. This does not
|
26
|
-
# programming because it eschews side effects
|
27
|
-
#
|
28
|
-
# a powerful and easy-to-use
|
19
|
+
# One of the main uses of `Either` is as a return value that can indicate
|
20
|
+
# either success or failure. Object oriented programs generally report errors
|
21
|
+
# through either state or exception handling, neither of which work well in
|
22
|
+
# functional programming. In the former case, a method is called on an object
|
23
|
+
# and when an error occurs the state of the object is updated to reflect the
|
24
|
+
# error. This does not translate well to functional programming because they
|
25
|
+
# eschew state and mutable objects. In the latter, an exception handling block
|
26
|
+
# provides branching logic when an exception is thrown. This does not
|
27
|
+
# translate well to functional programming because it eschews side effects
|
28
|
+
# like structured exception handling (and structured exception handling tends
|
29
|
+
# to be very expensive). `Either` provides a powerful and easy-to-use
|
30
|
+
# alternative.
|
29
31
|
#
|
30
|
-
# A function that may generate an error can choose to return an immutable
|
31
|
-
# object in which the position of the value (left or right) indicates
|
32
|
-
# of the data. By convention, a `left` value indicates an error and
|
33
|
-
# indicates success. This leaves the caller with no ambiguity
|
34
|
-
# failure, requires no persistent state, and does not
|
35
|
-
# handling facilities.
|
32
|
+
# A function that may generate an error can choose to return an immutable
|
33
|
+
# `Either` object in which the position of the value (left or right) indicates
|
34
|
+
# the nature of the data. By convention, a `left` value indicates an error and
|
35
|
+
# a `right` value indicates success. This leaves the caller with no ambiguity
|
36
|
+
# regarding success or failure, requires no persistent state, and does not
|
37
|
+
# require expensive exception handling facilities.
|
36
38
|
#
|
37
|
-
# `Either` provides several aliases and convenience functions to facilitate
|
38
|
-
# failure/success conventions. The `left` and `right` functions,
|
39
|
-
# derivatives, are mirrored by `reason` and `value`. Failure
|
40
|
-
# presence of a `reason` and success is indicated by the
|
41
|
-
# When an operation has failed the either is in a
|
42
|
-
# operation has successed the either is in a
|
43
|
-
# is to use a Ruby `Exception` as the
|
44
|
-
#
|
45
|
-
#
|
39
|
+
# `Either` provides several aliases and convenience functions to facilitate
|
40
|
+
# these failure/success conventions. The `left` and `right` functions,
|
41
|
+
# including their derivatives, are mirrored by `reason` and `value`. Failure
|
42
|
+
# is indicated by the presence of a `reason` and success is indicated by the
|
43
|
+
# presence of a `value`. When an operation has failed the either is in a
|
44
|
+
# `rejected` state, and when an operation has successed the either is in a
|
45
|
+
# `fulfilled` state. A common convention is to use a Ruby `Exception` as the
|
46
|
+
# `reason`. The factory method `error` facilitates this. The semantics and
|
47
|
+
# conventions of `reason`, `value`, and their derivatives follow the
|
48
|
+
# conventions of the Concurrent Ruby gem.
|
46
49
|
#
|
47
|
-
# The `left`/`right` and `reason`/`value` methods are not mutually exclusive.
|
48
|
-
# can be commingled and still result in functionally correct code. This
|
49
|
-
# should be avoided, however. Consistent use of either `left`/`right`
|
50
|
-
# `reason`/`value` against each `Either` instance will result in more
|
51
|
-
# intent-revealing code.
|
50
|
+
# The `left`/`right` and `reason`/`value` methods are not mutually exclusive.
|
51
|
+
# They can be commingled and still result in functionally correct code. This
|
52
|
+
# practice should be avoided, however. Consistent use of either `left`/`right`
|
53
|
+
# or `reason`/`value` against each `Either` instance will result in more
|
54
|
+
# expressive, intent-revealing code.
|
52
55
|
#
|
53
56
|
# @example
|
54
57
|
#
|
@@ -77,16 +80,14 @@ module Functional
|
|
77
80
|
# @see https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Either.html Haskell Data.Either
|
78
81
|
# @see http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Obligation.html Concurrent Ruby
|
79
82
|
#
|
80
|
-
# @since 1.0.0
|
81
|
-
#
|
82
83
|
# @!macro thread_safe_immutable_object
|
83
|
-
class Either
|
84
|
+
class Either < Synchronization::Object
|
84
85
|
include AbstractStruct
|
85
86
|
|
86
87
|
self.datatype = :either
|
87
88
|
self.fields = [:left, :right].freeze
|
88
89
|
|
89
|
-
# @!visibility private
|
90
|
+
# @!visibility private
|
90
91
|
NO_VALUE = Object.new.freeze
|
91
92
|
|
92
93
|
private_class_method :new
|
@@ -119,7 +120,7 @@ module Functional
|
|
119
120
|
# given error class will be used.
|
120
121
|
#
|
121
122
|
# @example
|
122
|
-
#
|
123
|
+
#
|
123
124
|
# either = Functional::Either.error("You're a bad monkey, Mojo Jojo")
|
124
125
|
# either.fulfilled? #=> false
|
125
126
|
# either.rejected? #=> true
|
@@ -137,7 +138,7 @@ module Functional
|
|
137
138
|
end
|
138
139
|
|
139
140
|
# Projects this either as a left.
|
140
|
-
#
|
141
|
+
#
|
141
142
|
# @return [Object] The left value or `nil` when `right`.
|
142
143
|
def left
|
143
144
|
left? ? to_h[:left] : nil
|
@@ -145,7 +146,7 @@ module Functional
|
|
145
146
|
alias_method :reason, :left
|
146
147
|
|
147
148
|
# Projects this either as a right.
|
148
|
-
#
|
149
|
+
#
|
149
150
|
# @return [Object] The right value or `nil` when `left`.
|
150
151
|
def right
|
151
152
|
right? ? to_h[:right] : nil
|
@@ -213,12 +214,14 @@ module Functional
|
|
213
214
|
# @param [Object] value the value of this either
|
214
215
|
# @param [Boolean] is_left is this a left either or right?
|
215
216
|
#
|
216
|
-
# @!visibility private
|
217
|
+
# @!visibility private
|
217
218
|
def initialize(value, is_left)
|
219
|
+
super
|
218
220
|
@is_left = is_left
|
219
221
|
hsh = is_left ? {left: value, right: nil} : {left: nil, right: value}
|
220
222
|
set_data_hash(hsh)
|
221
223
|
set_values_array(hsh.values)
|
224
|
+
ensure_ivar_visibility!
|
222
225
|
end
|
223
226
|
end
|
224
227
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
require '
|
1
|
+
require 'functional/final_var'
|
2
|
+
require 'functional/synchronization'
|
3
3
|
|
4
4
|
module Functional
|
5
5
|
|
@@ -41,14 +41,11 @@ module Functional
|
|
41
41
|
#
|
42
42
|
# name.first = 'Sam' #=> Functional::FinalityError: final accessor 'first' has already been set
|
43
43
|
#
|
44
|
-
# @since 1.1.0
|
45
|
-
#
|
46
|
-
# @see Functional::FinalVar
|
47
44
|
# @see http://www.ruby-doc.org/stdlib-2.1.2/libdoc/ostruct/rdoc/OpenStruct.html
|
48
45
|
# @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword
|
49
46
|
#
|
50
47
|
# @!macro thread_safe_final_object
|
51
|
-
class FinalStruct
|
48
|
+
class FinalStruct < Synchronization::Object
|
52
49
|
|
53
50
|
# Creates a new `FinalStruct` object. By default, the resulting `FinalStruct`
|
54
51
|
# object will have no attributes. The optional hash, if given, will generate
|
@@ -57,10 +54,12 @@ module Functional
|
|
57
54
|
# @param [Hash] attributes the field/value pairs to set on creation
|
58
55
|
def initialize(attributes = {})
|
59
56
|
raise ArgumentError.new('attributes must be given as a hash or not at all') unless attributes.respond_to?(:to_h)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
57
|
+
super
|
58
|
+
synchronize do
|
59
|
+
@attribute_hash = {}
|
60
|
+
attributes.to_h.each_pair do |field, value|
|
61
|
+
ns_set_attribute(field, value)
|
62
|
+
end
|
64
63
|
end
|
65
64
|
end
|
66
65
|
|
@@ -71,9 +70,7 @@ module Functional
|
|
71
70
|
# @param [Symbol] field the field to retrieve the value for
|
72
71
|
# @return [Object] the value of the field is set else nil
|
73
72
|
def get(field)
|
74
|
-
|
75
|
-
get_attribute(field)
|
76
|
-
}
|
73
|
+
synchronize { ns_get_attribute(field) }
|
77
74
|
end
|
78
75
|
alias_method :[], :get
|
79
76
|
|
@@ -91,13 +88,13 @@ module Functional
|
|
91
88
|
#
|
92
89
|
# @raise [Functional::FinalityError] if the given field has already been set
|
93
90
|
def set(field, value)
|
94
|
-
|
95
|
-
if
|
91
|
+
synchronize do
|
92
|
+
if ns_attribute_has_been_set?(field)
|
96
93
|
raise FinalityError.new("final accessor '#{field}' has already been set")
|
97
94
|
else
|
98
|
-
|
95
|
+
ns_set_attribute(field, value)
|
99
96
|
end
|
100
|
-
|
97
|
+
end
|
101
98
|
end
|
102
99
|
alias_method :[]=, :set
|
103
100
|
|
@@ -109,9 +106,7 @@ module Functional
|
|
109
106
|
# @param [Symbol] field the field to get the value for
|
110
107
|
# @return [Boolean] true if the field has been set else false
|
111
108
|
def set?(field)
|
112
|
-
|
113
|
-
attribute_has_been_set?(field)
|
114
|
-
}
|
109
|
+
synchronize { ns_attribute_has_been_set?(field) }
|
115
110
|
end
|
116
111
|
|
117
112
|
# Get the current value of the given field if already set else set the value of
|
@@ -121,9 +116,7 @@ module Functional
|
|
121
116
|
# @param [Object] value the value to set the field to when not previously set
|
122
117
|
# @return [Object] the final value of the given field
|
123
118
|
def get_or_set(field, value)
|
124
|
-
|
125
|
-
attribute_has_been_set?(field) ? get_attribute(field) : set_attribute(field, value)
|
126
|
-
}
|
119
|
+
synchronize { ns_attribute_has_been_set?(field) ? ns_get_attribute(field) : ns_set_attribute(field, value) }
|
127
120
|
end
|
128
121
|
|
129
122
|
# Get the current value of the given field if already set else return the given
|
@@ -133,9 +126,7 @@ module Functional
|
|
133
126
|
# @param [Object] default the value to return if the field has not been set
|
134
127
|
# @return [Object] the value of the given field else the given default value
|
135
128
|
def fetch(field, default)
|
136
|
-
|
137
|
-
attribute_has_been_set?(field) ? get_attribute(field) : default
|
138
|
-
}
|
129
|
+
synchronize { ns_attribute_has_been_set?(field) ? ns_get_attribute(field) : default }
|
139
130
|
end
|
140
131
|
|
141
132
|
# Calls the block once for each attribute, passing the key/value pair as parameters.
|
@@ -147,11 +138,11 @@ module Functional
|
|
147
138
|
# @return [Enumerable] when no block is given
|
148
139
|
def each_pair
|
149
140
|
return enum_for(:each_pair) unless block_given?
|
150
|
-
|
141
|
+
synchronize do
|
151
142
|
@attribute_hash.each do |field, value|
|
152
143
|
yield(field, value)
|
153
144
|
end
|
154
|
-
|
145
|
+
end
|
155
146
|
end
|
156
147
|
|
157
148
|
# Converts the `FinalStruct` to a `Hash` with keys representing each attribute
|
@@ -159,9 +150,7 @@ module Functional
|
|
159
150
|
#
|
160
151
|
# @return [Hash] a `Hash` representing this struct
|
161
152
|
def to_h
|
162
|
-
@
|
163
|
-
@attribute_hash.dup
|
164
|
-
}
|
153
|
+
synchronize { @attribute_hash.dup }
|
165
154
|
end
|
166
155
|
|
167
156
|
# Compares this object and other for equality. A `FinalStruct` is `eql?` to
|
@@ -190,19 +179,19 @@ module Functional
|
|
190
179
|
|
191
180
|
# @!macro final_struct_get_method
|
192
181
|
# @!visibility private
|
193
|
-
def
|
182
|
+
def ns_get_attribute(field)
|
194
183
|
@attribute_hash[field.to_sym]
|
195
184
|
end
|
196
185
|
|
197
186
|
# @!macro final_struct_set_method
|
198
187
|
# @!visibility private
|
199
|
-
def
|
188
|
+
def ns_set_attribute(field, value)
|
200
189
|
@attribute_hash[field.to_sym] = value
|
201
190
|
end
|
202
191
|
|
203
192
|
# @!macro final_struct_set_predicate
|
204
193
|
# @!visibility private
|
205
|
-
def
|
194
|
+
def ns_attribute_has_been_set?(field)
|
206
195
|
@attribute_hash.has_key?(field.to_sym)
|
207
196
|
end
|
208
197
|
|