accessory 0.1.5 → 0.1.10
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/lib/accessory.rb +2 -2
- data/lib/accessory/access.rb +55 -40
- data/lib/accessory/accessor.rb +43 -50
- data/lib/accessory/accessors/all_accessor.rb +21 -6
- data/lib/accessory/accessors/attribute_accessor.rb +15 -11
- data/lib/accessory/accessors/between_each_accessor.rb +22 -9
- data/lib/accessory/accessors/betwixt_accessor.rb +31 -19
- data/lib/accessory/accessors/filter_accessor.rb +20 -6
- data/lib/accessory/accessors/first_accessor.rb +17 -11
- data/lib/accessory/accessors/instance_variable_accessor.rb +12 -10
- data/lib/accessory/accessors/last_accessor.rb +17 -11
- data/lib/accessory/accessors/subscript_accessor.rb +19 -12
- data/lib/accessory/bound_lens.rb +139 -0
- data/lib/accessory/lens.rb +203 -75
- data/lib/accessory/traversal_position/enumerable_before_offset.rb +5 -5
- data/lib/accessory/version.rb +1 -1
- metadata +3 -3
- data/lib/accessory/lens_path.rb +0 -219
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d946b468b04343c14a9a11fc36428673c56d6851365a6b7119db3dec69111dc8
|
4
|
+
data.tar.gz: 5f2ea0b5fbd0edf89d1529428030370c2e7407f1a62ca4048bc9a078350db379
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a20295b442cb3e2be9c9c7fba21438aa48e759192b24169e475354f72516c1536db19a99fbdc5d41c5b95459183a280d34f5bb2289f149b469e16f8c41027911
|
7
|
+
data.tar.gz: 82d72455e0a4f5e0b078966d644da4d0d26a44d015c74347280da273fa3adfba0b194d736a183c816eb6109477cb0a45dbff2d294a2c8b983411823aacc87b1a
|
data/lib/accessory.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
module Accessory; end
|
2
2
|
|
3
3
|
require 'accessory/version'
|
4
|
-
require 'accessory/lens_path'
|
5
4
|
require 'accessory/lens'
|
5
|
+
require 'accessory/bound_lens'
|
6
6
|
require 'accessory/access'
|
7
7
|
|
8
8
|
module Accessory
|
9
9
|
refine ::Object do
|
10
10
|
def lens(...)
|
11
|
-
::Accessory::
|
11
|
+
::Accessory::BoundLens.on(self, ...)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/accessory/access.rb
CHANGED
@@ -10,87 +10,102 @@ require 'accessory/accessors/all_accessor'
|
|
10
10
|
require 'accessory/accessors/first_accessor'
|
11
11
|
require 'accessory/accessors/last_accessor'
|
12
12
|
|
13
|
+
##
|
14
|
+
# A set of convenient module-function helpers to use with <tt>Lens[...]</tt>.
|
15
|
+
#
|
16
|
+
# These functions aren't very convenient unless you
|
17
|
+
#
|
18
|
+
# include Accessory
|
19
|
+
|
13
20
|
module Accessory::Access
|
14
|
-
# (see Accessory::SubscriptAccessor)
|
21
|
+
# (see Accessory::Accessors::SubscriptAccessor)
|
15
22
|
def self.subscript(...)
|
16
|
-
Accessory::SubscriptAccessor.new(...)
|
23
|
+
Accessory::Accessors::SubscriptAccessor.new(...)
|
17
24
|
end
|
18
25
|
|
19
|
-
# (see Accessory::AttributeAccessor)
|
26
|
+
# (see Accessory::Accessors::AttributeAccessor)
|
20
27
|
def self.attr(...)
|
21
|
-
Accessory::AttributeAccessor.new(...)
|
28
|
+
Accessory::Accessors::AttributeAccessor.new(...)
|
22
29
|
end
|
23
30
|
|
24
|
-
# (see Accessory::InstanceVariableAccessor)
|
31
|
+
# (see Accessory::Accessors::InstanceVariableAccessor)
|
25
32
|
def self.ivar(...)
|
26
|
-
Accessory::InstanceVariableAccessor.new(...)
|
33
|
+
Accessory::Accessors::InstanceVariableAccessor.new(...)
|
27
34
|
end
|
28
35
|
|
29
|
-
# (see Accessory::BetwixtAccessor)
|
36
|
+
# (see Accessory::Accessors::BetwixtAccessor)
|
30
37
|
def self.betwixt(...)
|
31
|
-
Accessory::BetwixtAccessor.new(...)
|
38
|
+
Accessory::Accessors::BetwixtAccessor.new(...)
|
32
39
|
end
|
33
40
|
|
34
|
-
# Alias for +Accessory::Access.betwixt(0)+. See {Access.betwixt}
|
41
|
+
# Alias for +Accessory::Accessors::Access.betwixt(0)+. See {Access.betwixt}
|
35
42
|
def self.before_first
|
36
43
|
self.betwixt(0)
|
37
44
|
end
|
38
45
|
|
39
|
-
# Alias for +Accessory::Access.betwixt(-1)+. See {Access.betwixt}
|
46
|
+
# Alias for +Accessory::Accessors::Access.betwixt(-1)+. See {Access.betwixt}
|
40
47
|
def self.after_last
|
41
48
|
self.betwixt(-1)
|
42
49
|
end
|
43
50
|
|
44
|
-
# (see Accessory::BetweenEachAccessor)
|
51
|
+
# (see Accessory::Accessors::BetweenEachAccessor)
|
45
52
|
def self.between_each
|
46
|
-
Accessory::BetweenEachAccessor.new
|
53
|
+
Accessory::Accessors::BetweenEachAccessor.new
|
47
54
|
end
|
48
55
|
|
49
|
-
# (see Accessory::AllAccessor)
|
56
|
+
# (see Accessory::Accessors::AllAccessor)
|
50
57
|
def self.all
|
51
|
-
Accessory::AllAccessor.new
|
58
|
+
Accessory::Accessors::AllAccessor.new
|
52
59
|
end
|
53
60
|
|
54
|
-
# (see Accessory::FirstAccessor)
|
61
|
+
# (see Accessory::Accessors::FirstAccessor)
|
55
62
|
def self.first
|
56
|
-
Accessory::FirstAccessor.new
|
63
|
+
Accessory::Accessors::FirstAccessor.new
|
57
64
|
end
|
58
65
|
|
59
|
-
# (see Accessory::LastAccessor)
|
66
|
+
# (see Accessory::Accessors::LastAccessor)
|
60
67
|
def self.last
|
61
|
-
Accessory::LastAccessor.new
|
68
|
+
Accessory::Accessors::LastAccessor.new
|
62
69
|
end
|
63
70
|
|
64
|
-
# (see Accessory::FilterAccessor)
|
71
|
+
# (see Accessory::Accessors::FilterAccessor)
|
65
72
|
def self.filter(&pred)
|
66
|
-
Accessory::FilterAccessor.new(pred)
|
73
|
+
Accessory::Accessors::FilterAccessor.new(pred)
|
67
74
|
end
|
68
75
|
end
|
69
76
|
|
77
|
+
##
|
78
|
+
# A set of convenient "fluent API" builder methods
|
79
|
+
# that get mixed into {Lens} and {BoundLens}.
|
80
|
+
#
|
81
|
+
# These do the same thing as the {Access} helper of the same name, but
|
82
|
+
# wrap the resulting accessor in a call to <tt>#then</tt>, deriving a new
|
83
|
+
# {Lens} or {BoundLens} from the addition of the accessor.
|
84
|
+
|
70
85
|
module Accessory::Access::FluentHelpers
|
71
|
-
# (see Accessory::SubscriptAccessor)
|
86
|
+
# (see Accessory::Accessors::SubscriptAccessor)
|
72
87
|
def subscript(...)
|
73
|
-
self.then(Accessory::SubscriptAccessor.new(...))
|
88
|
+
self.then(Accessory::Accessors::SubscriptAccessor.new(...))
|
74
89
|
end
|
75
90
|
|
76
91
|
# Alias for {#subscript}
|
77
92
|
def [](...)
|
78
|
-
self.then(Accessory::SubscriptAccessor.new(...))
|
93
|
+
self.then(Accessory::Accessors::SubscriptAccessor.new(...))
|
79
94
|
end
|
80
95
|
|
81
|
-
# (see Accessory::AttributeAccessor)
|
96
|
+
# (see Accessory::Accessors::AttributeAccessor)
|
82
97
|
def attr(...)
|
83
|
-
self.then(Accessory::AttributeAccessor.new(...))
|
98
|
+
self.then(Accessory::Accessors::AttributeAccessor.new(...))
|
84
99
|
end
|
85
100
|
|
86
|
-
# (see Accessory::InstanceVariableAccessor)
|
101
|
+
# (see Accessory::Accessors::InstanceVariableAccessor)
|
87
102
|
def ivar(...)
|
88
|
-
self.then(Accessory::InstanceVariableAccessor.new(...))
|
103
|
+
self.then(Accessory::Accessors::InstanceVariableAccessor.new(...))
|
89
104
|
end
|
90
105
|
|
91
|
-
# (see Accessory::BetwixtAccessor)
|
106
|
+
# (see Accessory::Accessors::BetwixtAccessor)
|
92
107
|
def betwixt(...)
|
93
|
-
self.then(Accessory::BetwixtAccessor.new(...))
|
108
|
+
self.then(Accessory::Accessors::BetwixtAccessor.new(...))
|
94
109
|
end
|
95
110
|
|
96
111
|
# Alias for +#betwixt(0)+. See {#betwixt}
|
@@ -103,29 +118,29 @@ module Accessory::Access::FluentHelpers
|
|
103
118
|
self.betwixt(-1)
|
104
119
|
end
|
105
120
|
|
106
|
-
# (see Accessory::BetweenEachAccessor)
|
121
|
+
# (see Accessory::Accessors::BetweenEachAccessor)
|
107
122
|
def between_each
|
108
|
-
self.then(Accessory::BetweenEachAccessor.new)
|
123
|
+
self.then(Accessory::Accessors::BetweenEachAccessor.new)
|
109
124
|
end
|
110
125
|
|
111
|
-
# (see Accessory::AllAccessor)
|
126
|
+
# (see Accessory::Accessors::AllAccessor)
|
112
127
|
def all
|
113
|
-
self.then(Accessory::AllAccessor.new)
|
128
|
+
self.then(Accessory::Accessors::AllAccessor.new)
|
114
129
|
end
|
115
130
|
|
116
|
-
# (see Accessory::FirstAccessor)
|
131
|
+
# (see Accessory::Accessors::FirstAccessor)
|
117
132
|
def first
|
118
|
-
self.then(Accessory::FirstAccessor.new)
|
133
|
+
self.then(Accessory::Accessors::FirstAccessor.new)
|
119
134
|
end
|
120
135
|
|
121
|
-
# (see Accessory::LastAccessor)
|
136
|
+
# (see Accessory::Accessors::LastAccessor)
|
122
137
|
def last
|
123
|
-
self.then(Accessory::LastAccessor.new)
|
138
|
+
self.then(Accessory::Accessors::LastAccessor.new)
|
124
139
|
end
|
125
140
|
|
126
|
-
# (see Accessory::FilterAccessor)
|
141
|
+
# (see Accessory::Accessors::FilterAccessor)
|
127
142
|
def filter(&pred)
|
128
|
-
self.then(Accessory::FilterAccessor.new(pred))
|
143
|
+
self.then(Accessory::Accessors::FilterAccessor.new(pred))
|
129
144
|
end
|
130
145
|
end
|
131
146
|
|
@@ -133,6 +148,6 @@ class Accessory::Lens
|
|
133
148
|
include Accessory::Access::FluentHelpers
|
134
149
|
end
|
135
150
|
|
136
|
-
class Accessory::
|
151
|
+
class Accessory::BoundLens
|
137
152
|
include Accessory::Access::FluentHelpers
|
138
153
|
end
|
data/lib/accessory/accessor.rb
CHANGED
@@ -19,19 +19,15 @@ module Accessory; end
|
|
19
19
|
# info):
|
20
20
|
#
|
21
21
|
# * {Accessor#traverse}
|
22
|
-
# * {Accessor#
|
22
|
+
# * {Accessor#ensure_valid}
|
23
23
|
|
24
24
|
class Accessory::Accessor
|
25
|
-
# @!visibility private
|
26
|
-
DEFAULT_NOT_SET_SENTINEL = :"98e47971-e708-42ca-bee7-0c62fe5e11c9"
|
27
|
-
|
28
|
-
# @!visibility private
|
29
25
|
TERMINAL_DEFAULT_FN = lambda{ nil }
|
26
|
+
private_constant :TERMINAL_DEFAULT_FN
|
30
27
|
|
31
28
|
# @!visibility private
|
32
|
-
def initialize(
|
33
|
-
@default_value =
|
34
|
-
@succ_default_data_constructor = TERMINAL_DEFAULT_FN
|
29
|
+
def initialize(default_value = nil)
|
30
|
+
@default_value = default_value
|
35
31
|
end
|
36
32
|
|
37
33
|
# @!visibility private
|
@@ -58,7 +54,8 @@ class Accessory::Accessor
|
|
58
54
|
end
|
59
55
|
|
60
56
|
# @!visibility private
|
61
|
-
HIDDEN_IVARS = [:@default_value, :@
|
57
|
+
HIDDEN_IVARS = [:@default_value, :@successor]
|
58
|
+
private_constant :HIDDEN_IVARS
|
62
59
|
|
63
60
|
# @!visibility private
|
64
61
|
def inspect_args
|
@@ -69,7 +66,7 @@ class Accessory::Accessor
|
|
69
66
|
end
|
70
67
|
|
71
68
|
# @!visibility private
|
72
|
-
attr_accessor :
|
69
|
+
attr_accessor :successor
|
73
70
|
|
74
71
|
# @!group Helpers
|
75
72
|
|
@@ -81,28 +78,20 @@ class Accessory::Accessor
|
|
81
78
|
# call <tt>traverse_or_default(data)</tt> within your implementation to safely
|
82
79
|
# get a traversal-result to operate on.
|
83
80
|
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
# {
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
# implementation calling <tt>super(default)</tt>; or it's the result of
|
92
|
-
# calling {default_data_constructor} on the successor-accessor in the accessor
|
93
|
-
# chain.
|
81
|
+
# The value returned by your {traverse} callback will be validated by your
|
82
|
+
# calling the {ensure_valid} callback of the _successor accessor_ in the Lens
|
83
|
+
# path. {ensure_valid} will replace any value it considers invalid with a
|
84
|
+
# reasonable default. This means you don't need to worry about what will
|
85
|
+
# happen if your traversal doesn't find a value and so returns +nil+. The
|
86
|
+
# successor's {ensure_valid} will replace that +nil+ with a value that works
|
87
|
+
# for it.
|
94
88
|
def traverse_or_default(data)
|
95
|
-
|
89
|
+
traversal_result = traverse(data) || @default_value
|
96
90
|
|
97
|
-
|
98
|
-
|
91
|
+
if @successor
|
92
|
+
@successor.ensure_valid(traversal_result)
|
93
|
+
else
|
99
94
|
traversal_result
|
100
|
-
in :error
|
101
|
-
if DEFAULT_NOT_SET_SENTINEL.equal?(@default_value)
|
102
|
-
@succ_default_data_constructor.call
|
103
|
-
else
|
104
|
-
@default_value
|
105
|
-
end
|
106
95
|
end
|
107
96
|
end
|
108
97
|
|
@@ -124,7 +113,7 @@ class Accessory::Accessor
|
|
124
113
|
#
|
125
114
|
# @param data [Enumerable] the data yielded by the predecessor accessor.
|
126
115
|
# @param succ [Proc] a thunk to the successor accessor. When {get} is called
|
127
|
-
# by a {
|
116
|
+
# by a {Lens}, this is passed implicitly.
|
128
117
|
# @return [Object] the data to pass back to the predecessor accessor as a
|
129
118
|
# yield result.
|
130
119
|
def get(data, &succ)
|
@@ -145,7 +134,8 @@ class Accessory::Accessor
|
|
145
134
|
# of the +get_result+s should replicate the data flow of {get}.
|
146
135
|
# * the +new_value+ should be used to *replace* or *overwrite* the result of
|
147
136
|
# this accessor's traversal within +data+. For example, in
|
148
|
-
# {SubscriptAccessor}, <tt>data[key] = new_value</tt> is
|
137
|
+
# {Accessors::SubscriptAccessor}, <tt>data[key] = new_value</tt> is
|
138
|
+
# executed.
|
149
139
|
#
|
150
140
|
# In the <tt>:pop</tt> command case:
|
151
141
|
# * the result of the traversal (*before* the yield) should be returned. This
|
@@ -153,7 +143,7 @@ class Accessory::Accessor
|
|
153
143
|
# traversal-results before feeding them into yield, in order to return them
|
154
144
|
# here.
|
155
145
|
# * the traversal-result should be *removed* from +data+. For example, in
|
156
|
-
# {SubscriptAccessor}, <tt>data.delete(key)</tt> is executed.
|
146
|
+
# {Accessors::SubscriptAccessor}, <tt>data.delete(key)</tt> is executed.
|
157
147
|
#
|
158
148
|
# The successor in the accessor chain will receive the yielded
|
159
149
|
# traversal-results as its own +data+.
|
@@ -171,7 +161,7 @@ class Accessory::Accessor
|
|
171
161
|
#
|
172
162
|
# @param data [Enumerable] the data yielded by the predecessor accessor.
|
173
163
|
# @param succ [Proc] a thunk to the successor accessor. When {get} is called
|
174
|
-
# by a {
|
164
|
+
# by a {Lens}, this is passed implicitly.
|
175
165
|
# @return [Object] the modification-command to pass back to the predecessor
|
176
166
|
# accessor as a yield result.
|
177
167
|
def get_and_update(data, &succ)
|
@@ -187,37 +177,40 @@ class Accessory::Accessor
|
|
187
177
|
# traversal-results.
|
188
178
|
#
|
189
179
|
# This method can assume that +data+ is a valid receiver for the traversal
|
190
|
-
# it performs. {traverse_or_default} takes care of feeding in a default +data+
|
191
|
-
# the case where the predecessor passed invalid data.
|
180
|
+
# it performs. {traverse_or_default} takes care of feeding in a default +data+
|
181
|
+
# in the case where the predecessor passed invalid data.
|
192
182
|
#
|
193
183
|
# @param data [Object] the object to be traversed
|
194
|
-
# @return [Object]
|
195
|
-
# * <tt>[:ok, traversal_results]</tt> if traversal succeeds
|
196
|
-
# * +:error+ if traversal fails
|
184
|
+
# @return [Object] the result of traversal
|
197
185
|
def traverse(data)
|
198
186
|
raise NotImplementedError, "Accessor subclass #{self.class} must implement #traverse to use #traverse_or_default"
|
199
187
|
end
|
200
188
|
|
201
|
-
#
|
189
|
+
# Ensures that the predecessor accessor's traversal result is one this
|
190
|
+
# accessor can operate on; called by {traverse_or_default}.
|
191
|
+
#
|
192
|
+
# This callback should validate that the +traversal_result+ is one that can
|
193
|
+
# be traversed by the traversal-method of this accessor. If it can, the
|
194
|
+
# +traversal_result+ should be returned unchanged. If it cannot, an object
|
195
|
+
# that _can_ be traversed should be returned instead.
|
202
196
|
#
|
203
|
-
# Returns a default constructor Proc for the {traverse_or_default} call in
|
204
|
-
# the *predecessor* accessor to use.
|
205
197
|
#
|
206
198
|
# For example, if your accessor operates on +Enumerable+ values (like
|
207
|
-
# {AllAccessor}), then
|
208
|
-
#
|
199
|
+
# {Accessors::AllAccessor}), then this method should validate that the
|
200
|
+
# +traversal_result+ is +Enumerable+; and, if it isn't, return something that
|
201
|
+
# is — e.g. an empty +Array+.
|
209
202
|
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
# will then use it in {traverse_or_default} if it was not configured with
|
213
|
-
# an explicit default.
|
203
|
+
# This logic is used to replace invalid intermediate values (e.g. `nil`s and
|
204
|
+
# scalars) with containers during {Lens#put_in} et al.
|
214
205
|
#
|
215
|
-
# @return [
|
216
|
-
def
|
206
|
+
# @return [Object] a now-valid traversal result
|
207
|
+
def ensure_valid(traversal_result)
|
217
208
|
lambda do
|
218
|
-
raise NotImplementedError, "Accessor subclass #{self.class} must implement #
|
209
|
+
raise NotImplementedError, "Accessor subclass #{self.class} must implement #ensure_valid to allow chain-predecessor to use #traverse_or_default"
|
219
210
|
end
|
220
211
|
end
|
221
212
|
|
222
213
|
# @!endgroup
|
223
214
|
end
|
215
|
+
|
216
|
+
module Accessory::Accessors; end
|
@@ -5,7 +5,7 @@ require 'accessory/accessor'
|
|
5
5
|
#
|
6
6
|
# *Aliases*
|
7
7
|
# * {Access.all}
|
8
|
-
# * {Access::FluentHelpers#all} (included in {
|
8
|
+
# * {Access::FluentHelpers#all} (included in {Lens} and {BoundLens})
|
9
9
|
#
|
10
10
|
# *Equivalents* in Elixir's {https://hexdocs.pm/elixir/Access.html +Access+} module
|
11
11
|
# * {https://hexdocs.pm/elixir/Access.html#all/0 +Access.all/0+}
|
@@ -15,10 +15,14 @@ require 'accessory/accessor'
|
|
15
15
|
#
|
16
16
|
# * +Array.new+
|
17
17
|
|
18
|
-
class Accessory::AllAccessor < Accessory::Accessor
|
18
|
+
class Accessory::Accessors::AllAccessor < Accessory::Accessor
|
19
19
|
# @!visibility private
|
20
|
-
def
|
21
|
-
|
20
|
+
def ensure_valid(traversal_result)
|
21
|
+
if traversal_result.kind_of?(Enumerable)
|
22
|
+
traversal_result
|
23
|
+
else
|
24
|
+
[]
|
25
|
+
end
|
22
26
|
end
|
23
27
|
|
24
28
|
# @!visibility private
|
@@ -46,17 +50,28 @@ class Accessory::AllAccessor < Accessory::Accessor
|
|
46
50
|
def get_and_update(data)
|
47
51
|
results = []
|
48
52
|
new_data = []
|
53
|
+
dirty = false
|
49
54
|
|
50
55
|
(data || []).each do |pos|
|
51
56
|
case yield(pos)
|
52
|
-
in [result,
|
57
|
+
in [:clean, result, _]
|
58
|
+
results.push(result)
|
59
|
+
new_data.push(pos)
|
60
|
+
# ok
|
61
|
+
in [:dirty, result, new_value]
|
53
62
|
results.push(result)
|
54
63
|
new_data.push(new_value)
|
64
|
+
dirty = true
|
55
65
|
in :pop
|
56
66
|
results.push(pos)
|
67
|
+
dirty = true
|
57
68
|
end
|
58
69
|
end
|
59
70
|
|
60
|
-
|
71
|
+
if dirty
|
72
|
+
[:dirty, results, new_data]
|
73
|
+
else
|
74
|
+
[:clean, results, data]
|
75
|
+
end
|
61
76
|
end
|
62
77
|
end
|
@@ -8,18 +8,18 @@ require 'accessory/accessor'
|
|
8
8
|
# the getter/setter pair <tt>.foo</tt> and <tt>.foo=</tt>.
|
9
9
|
#
|
10
10
|
# The abstract "attribute" does not have to correspond to an actual
|
11
|
-
# +attr_accessor+; the
|
12
|
-
# named getter/setter methods exist on the receiver.
|
11
|
+
# +attr_accessor+; the AttributeAccessor will work as long as the
|
12
|
+
# relevant named getter/setter methods exist on the receiver.
|
13
13
|
#
|
14
14
|
# *Aliases*
|
15
15
|
# * {Access.attr}
|
16
|
-
# * {Access::FluentHelpers#attr} (included in {
|
16
|
+
# * {Access::FluentHelpers#attr} (included in {Lens} and {BoundLens})
|
17
17
|
#
|
18
18
|
# <b>Default constructor</b> used by predecessor accessor
|
19
19
|
#
|
20
20
|
# * +OpenStruct.new+
|
21
21
|
|
22
|
-
class Accessory::AttributeAccessor < Accessory::Accessor
|
22
|
+
class Accessory::Accessors::AttributeAccessor < Accessory::Accessor
|
23
23
|
# @param attr_name [Symbol] the attribute name (i.e. name of the getter method)
|
24
24
|
# @param default [Object] the default to use if the predecessor accessor passes +nil+ data
|
25
25
|
def initialize(attr_name, default: nil)
|
@@ -47,8 +47,10 @@ class Accessory::AttributeAccessor < Accessory::Accessor
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# @!visibility private
|
50
|
-
def
|
51
|
-
|
50
|
+
def ensure_valid(traversal_result)
|
51
|
+
if traversal_result
|
52
|
+
traversal_result
|
53
|
+
else
|
52
54
|
require 'ostruct'
|
53
55
|
OpenStruct.new
|
54
56
|
end
|
@@ -64,7 +66,7 @@ class Accessory::AttributeAccessor < Accessory::Accessor
|
|
64
66
|
# @param data [Object] the object to traverse
|
65
67
|
# @return [Object] the value derived from the rest of the accessor chain
|
66
68
|
def get(data)
|
67
|
-
value =
|
69
|
+
value = traverse_or_default(data)
|
68
70
|
|
69
71
|
if block_given?
|
70
72
|
yield(value)
|
@@ -83,15 +85,17 @@ class Accessory::AttributeAccessor < Accessory::Accessor
|
|
83
85
|
# @param data [Object] the object to traverse
|
84
86
|
# @return [Array] a two-element array containing 1. the original value found; and 2. the result value from the accessor chain
|
85
87
|
def get_and_update(data)
|
86
|
-
value =
|
88
|
+
value = traverse_or_default(data)
|
87
89
|
|
88
90
|
case yield(value)
|
89
|
-
in [result,
|
91
|
+
in [:clean, result, _]
|
92
|
+
[:clean, result, data]
|
93
|
+
in [:dirty, result, new_value]
|
90
94
|
data.send(@setter_method_name, new_value)
|
91
|
-
[result, data]
|
95
|
+
[:dirty, result, data]
|
92
96
|
in :pop
|
93
97
|
data.send(@setter_method_name, nil)
|
94
|
-
[value, data]
|
98
|
+
[:dirty, value, data]
|
95
99
|
end
|
96
100
|
end
|
97
101
|
end
|