accessory 0.1.6 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9880e7e5ea88c0b373a1cc3d5d4861788f368905c761b2ec3a7236d7282ada39
4
- data.tar.gz: 7386d98dc728a663904564e0a6825de0d7690e806950e5e95d6e8b39d4d20cd6
3
+ metadata.gz: 1a19c2c9d6ea2aeed4a4b0140c027220e383faa178d89689ebc667256fd9e228
4
+ data.tar.gz: 0c4ead2a9dd32de8b51c9c9da52aefafa16b05c272da5393b0e3d365dc920708
5
5
  SHA512:
6
- metadata.gz: dbee544b8dc699adb962559b98278d48edeed2a4d5b9c4d3c6c92ad3c4a38ffb4df5ced18274b465dfe68fd34dd15a50c06c2febae4e54049a4049d377cb10cd
7
- data.tar.gz: 0d1be0f43650e87a1616b5c2ac968b7407c5d8e380abb7e30ad84823816d486341a2c1b791814c55695dc77f2bf0337df834816c2a703bacbb2a3f582f362907
6
+ metadata.gz: fddd92947828fe5fd287ac183a5e7a3d0215da01314dd766737d7b8bf26c7e078a9207c1b1504e69522964e6e9a72b9b45372b10f35111a41c67bec4798da90d
7
+ data.tar.gz: 4ad13454e5adcd7b6243075c53e0b52539cbbc1402ab19d118ff065dfd25d5ac2fc5d3b052a3cec3055837bedae117fb4bddb226a3bbf66915a996c1344d47ef
data/lib/accessory.rb CHANGED
@@ -1,6 +1,3 @@
1
- ##
2
- #
3
-
4
1
  module Accessory; end
5
2
 
6
3
  require 'accessory/version'
@@ -18,59 +18,59 @@ require 'accessory/accessors/last_accessor'
18
18
  # include Accessory
19
19
 
20
20
  module Accessory::Access
21
- # (see Accessory::SubscriptAccessor)
21
+ # (see Accessory::Accessors::SubscriptAccessor)
22
22
  def self.subscript(...)
23
- Accessory::SubscriptAccessor.new(...)
23
+ Accessory::Accessors::SubscriptAccessor.new(...)
24
24
  end
25
25
 
26
- # (see Accessory::AttributeAccessor)
26
+ # (see Accessory::Accessors::AttributeAccessor)
27
27
  def self.attr(...)
28
- Accessory::AttributeAccessor.new(...)
28
+ Accessory::Accessors::AttributeAccessor.new(...)
29
29
  end
30
30
 
31
- # (see Accessory::InstanceVariableAccessor)
31
+ # (see Accessory::Accessors::InstanceVariableAccessor)
32
32
  def self.ivar(...)
33
- Accessory::InstanceVariableAccessor.new(...)
33
+ Accessory::Accessors::InstanceVariableAccessor.new(...)
34
34
  end
35
35
 
36
- # (see Accessory::BetwixtAccessor)
36
+ # (see Accessory::Accessors::BetwixtAccessor)
37
37
  def self.betwixt(...)
38
- Accessory::BetwixtAccessor.new(...)
38
+ Accessory::Accessors::BetwixtAccessor.new(...)
39
39
  end
40
40
 
41
- # Alias for +Accessory::Access.betwixt(0)+. See {Access.betwixt}
41
+ # Alias for +Accessory::Accessors::Access.betwixt(0)+. See {Access.betwixt}
42
42
  def self.before_first
43
43
  self.betwixt(0)
44
44
  end
45
45
 
46
- # Alias for +Accessory::Access.betwixt(-1)+. See {Access.betwixt}
46
+ # Alias for +Accessory::Accessors::Access.betwixt(-1)+. See {Access.betwixt}
47
47
  def self.after_last
48
48
  self.betwixt(-1)
49
49
  end
50
50
 
51
- # (see Accessory::BetweenEachAccessor)
51
+ # (see Accessory::Accessors::BetweenEachAccessor)
52
52
  def self.between_each
53
- Accessory::BetweenEachAccessor.new
53
+ Accessory::Accessors::BetweenEachAccessor.new
54
54
  end
55
55
 
56
- # (see Accessory::AllAccessor)
56
+ # (see Accessory::Accessors::AllAccessor)
57
57
  def self.all
58
- Accessory::AllAccessor.new
58
+ Accessory::Accessors::AllAccessor.new
59
59
  end
60
60
 
61
- # (see Accessory::FirstAccessor)
61
+ # (see Accessory::Accessors::FirstAccessor)
62
62
  def self.first
63
- Accessory::FirstAccessor.new
63
+ Accessory::Accessors::FirstAccessor.new
64
64
  end
65
65
 
66
- # (see Accessory::LastAccessor)
66
+ # (see Accessory::Accessors::LastAccessor)
67
67
  def self.last
68
- Accessory::LastAccessor.new
68
+ Accessory::Accessors::LastAccessor.new
69
69
  end
70
70
 
71
- # (see Accessory::FilterAccessor)
71
+ # (see Accessory::Accessors::FilterAccessor)
72
72
  def self.filter(&pred)
73
- Accessory::FilterAccessor.new(pred)
73
+ Accessory::Accessors::FilterAccessor.new(pred)
74
74
  end
75
75
  end
76
76
 
@@ -83,29 +83,29 @@ end
83
83
  # {Lens} or {BoundLens} from the addition of the accessor.
84
84
 
85
85
  module Accessory::Access::FluentHelpers
86
- # (see Accessory::SubscriptAccessor)
86
+ # (see Accessory::Accessors::SubscriptAccessor)
87
87
  def subscript(...)
88
- self.then(Accessory::SubscriptAccessor.new(...))
88
+ self.then(Accessory::Accessors::SubscriptAccessor.new(...))
89
89
  end
90
90
 
91
91
  # Alias for {#subscript}
92
92
  def [](...)
93
- self.then(Accessory::SubscriptAccessor.new(...))
93
+ self.then(Accessory::Accessors::SubscriptAccessor.new(...))
94
94
  end
95
95
 
96
- # (see Accessory::AttributeAccessor)
96
+ # (see Accessory::Accessors::AttributeAccessor)
97
97
  def attr(...)
98
- self.then(Accessory::AttributeAccessor.new(...))
98
+ self.then(Accessory::Accessors::AttributeAccessor.new(...))
99
99
  end
100
100
 
101
- # (see Accessory::InstanceVariableAccessor)
101
+ # (see Accessory::Accessors::InstanceVariableAccessor)
102
102
  def ivar(...)
103
- self.then(Accessory::InstanceVariableAccessor.new(...))
103
+ self.then(Accessory::Accessors::InstanceVariableAccessor.new(...))
104
104
  end
105
105
 
106
- # (see Accessory::BetwixtAccessor)
106
+ # (see Accessory::Accessors::BetwixtAccessor)
107
107
  def betwixt(...)
108
- self.then(Accessory::BetwixtAccessor.new(...))
108
+ self.then(Accessory::Accessors::BetwixtAccessor.new(...))
109
109
  end
110
110
 
111
111
  # Alias for +#betwixt(0)+. See {#betwixt}
@@ -118,29 +118,29 @@ module Accessory::Access::FluentHelpers
118
118
  self.betwixt(-1)
119
119
  end
120
120
 
121
- # (see Accessory::BetweenEachAccessor)
121
+ # (see Accessory::Accessors::BetweenEachAccessor)
122
122
  def between_each
123
- self.then(Accessory::BetweenEachAccessor.new)
123
+ self.then(Accessory::Accessors::BetweenEachAccessor.new)
124
124
  end
125
125
 
126
- # (see Accessory::AllAccessor)
126
+ # (see Accessory::Accessors::AllAccessor)
127
127
  def all
128
- self.then(Accessory::AllAccessor.new)
128
+ self.then(Accessory::Accessors::AllAccessor.new)
129
129
  end
130
130
 
131
- # (see Accessory::FirstAccessor)
131
+ # (see Accessory::Accessors::FirstAccessor)
132
132
  def first
133
- self.then(Accessory::FirstAccessor.new)
133
+ self.then(Accessory::Accessors::FirstAccessor.new)
134
134
  end
135
135
 
136
- # (see Accessory::LastAccessor)
136
+ # (see Accessory::Accessors::LastAccessor)
137
137
  def last
138
- self.then(Accessory::LastAccessor.new)
138
+ self.then(Accessory::Accessors::LastAccessor.new)
139
139
  end
140
140
 
141
- # (see Accessory::FilterAccessor)
141
+ # (see Accessory::Accessors::FilterAccessor)
142
142
  def filter(&pred)
143
- self.then(Accessory::FilterAccessor.new(pred))
143
+ self.then(Accessory::Accessors::FilterAccessor.new(pred))
144
144
  end
145
145
  end
146
146
 
@@ -19,7 +19,7 @@ module Accessory; end
19
19
  # info):
20
20
  #
21
21
  # * {Accessor#traverse}
22
- # * {Accessor#default_data_constructor}
22
+ # * {Accessor#ensure_valid}
23
23
 
24
24
  class Accessory::Accessor
25
25
  TERMINAL_DEFAULT_FN = lambda{ nil }
@@ -78,16 +78,13 @@ class Accessory::Accessor
78
78
  # call <tt>traverse_or_default(data)</tt> within your implementation to safely
79
79
  # get a traversal-result to operate on.
80
80
  #
81
- # This method will return +nil+ if the input-data is +nil+, _without_ calling
82
- # your {traverse} callback. This means that accessors that use
83
- # {traverse_or_default} will _forward_ +nil+ traversal-results along the chain
84
- # without being confused by them.
85
- #
86
- # If your {traverse} callback returns <tt>:error</tt>, a default value will
87
- # be used. This is either the +default+ passed to {initialize} by your
88
- # implementation calling <tt>super(default)</tt>; or it's the result of
89
- # calling {default_data_constructor} on the successor-accessor in the accessor
90
- # 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.
91
88
  def traverse_or_default(data)
92
89
  traversal_result = traverse(data) || @default_value
93
90
 
@@ -137,7 +134,8 @@ class Accessory::Accessor
137
134
  # of the +get_result+s should replicate the data flow of {get}.
138
135
  # * the +new_value+ should be used to *replace* or *overwrite* the result of
139
136
  # this accessor's traversal within +data+. For example, in
140
- # {SubscriptAccessor}, <tt>data[key] = new_value</tt> is executed.
137
+ # {Accessors::SubscriptAccessor}, <tt>data[key] = new_value</tt> is
138
+ # executed.
141
139
  #
142
140
  # In the <tt>:pop</tt> command case:
143
141
  # * the result of the traversal (*before* the yield) should be returned. This
@@ -145,7 +143,7 @@ class Accessory::Accessor
145
143
  # traversal-results before feeding them into yield, in order to return them
146
144
  # here.
147
145
  # * the traversal-result should be *removed* from +data+. For example, in
148
- # {SubscriptAccessor}, <tt>data.delete(key)</tt> is executed.
146
+ # {Accessors::SubscriptAccessor}, <tt>data.delete(key)</tt> is executed.
149
147
  #
150
148
  # The successor in the accessor chain will receive the yielded
151
149
  # traversal-results as its own +data+.
@@ -198,9 +196,9 @@ class Accessory::Accessor
198
196
  #
199
197
  #
200
198
  # For example, if your accessor operates on +Enumerable+ values (like
201
- # {AllAccessor}), then this method should validate that the +traversal_result+
202
- # is +Enumerable+; and, if it isn't, return something that is — e.g. an empty
203
- # +Array+.
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+.
204
202
  #
205
203
  # This logic is used to replace invalid intermediate values (e.g. `nil`s and
206
204
  # scalars) with containers during {Lens#put_in} et al.
@@ -214,3 +212,5 @@ class Accessory::Accessor
214
212
 
215
213
  # @!endgroup
216
214
  end
215
+
216
+ module Accessory::Accessors; end
@@ -15,7 +15,7 @@ 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
20
  def ensure_valid(traversal_result)
21
21
  if traversal_result.kind_of?(Enumerable)
@@ -50,17 +50,28 @@ class Accessory::AllAccessor < Accessory::Accessor
50
50
  def get_and_update(data)
51
51
  results = []
52
52
  new_data = []
53
+ dirty = false
53
54
 
54
55
  (data || []).each do |pos|
55
56
  case yield(pos)
56
- in [result, new_value]
57
+ in [:clean, result, _]
58
+ results.push(result)
59
+ new_data.push(pos)
60
+ # ok
61
+ in [:dirty, result, new_value]
57
62
  results.push(result)
58
63
  new_data.push(new_value)
64
+ dirty = true
59
65
  in :pop
60
66
  results.push(pos)
67
+ dirty = true
61
68
  end
62
69
  end
63
70
 
64
- [results, new_data]
71
+ if dirty
72
+ [:dirty, results, new_data]
73
+ else
74
+ [:clean, results, data]
75
+ end
65
76
  end
66
77
  end
@@ -8,8 +8,8 @@ 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 {AttributeAccessor} will work as long as the relevant
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}
@@ -19,7 +19,7 @@ require 'accessory/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)
@@ -88,12 +88,14 @@ class Accessory::AttributeAccessor < Accessory::Accessor
88
88
  value = traverse_or_default(data)
89
89
 
90
90
  case yield(value)
91
- in [result, new_value]
91
+ in [:clean, result, _]
92
+ [:clean, result, data]
93
+ in [:dirty, result, new_value]
92
94
  data.send(@setter_method_name, new_value)
93
- [result, data]
95
+ [:dirty, result, data]
94
96
  in :pop
95
97
  data.send(@setter_method_name, nil)
96
- [value, data]
98
+ [:dirty, value, data]
97
99
  end
98
100
  end
99
101
  end
@@ -5,7 +5,7 @@ require 'accessory/traversal_position/enumerable_before_offset'
5
5
  # Traverses the positions "between" the elements of an +Enumerable+, including
6
6
  # the positions at the "edges" (i.e. before the first, and after the last.)
7
7
  #
8
- # {BetweenEachAccessor} can be used with {Lens#put_in} to insert new
8
+ # BetweenEachAccessor can be used with {Lens#put_in} to insert new
9
9
  # elements into an Enumerable between the existing ones.
10
10
  #
11
11
  # *Aliases*
@@ -16,7 +16,7 @@ require 'accessory/traversal_position/enumerable_before_offset'
16
16
  #
17
17
  # * +Array.new+
18
18
 
19
- class Accessory::BetweenEachAccessor < Accessory::Accessor
19
+ class Accessory::Accessors::BetweenEachAccessor < Accessory::Accessor
20
20
  # @!visibility private
21
21
  def ensure_valid(traversal_result)
22
22
  if traversal_result.kind_of?(Enumerable)
@@ -75,15 +75,20 @@ class Accessory::BetweenEachAccessor < Accessory::Accessor
75
75
  def get_and_update(data)
76
76
  results = []
77
77
  new_data = []
78
+ dirty = false
78
79
 
79
80
  positions = traverse_or_default(data || [])
80
81
 
81
82
  positions.each do |pos|
82
83
  case yield(pos)
83
- in [result, new_value]
84
+ in [:clean, result, _]
85
+ results.push(result)
86
+ in [:dirty, result, new_value]
84
87
  new_data.push(new_value)
85
88
  results.push(result)
89
+ dirty = true
86
90
  in :pop
91
+ # ok
87
92
  end
88
93
 
89
94
  unless pos.last?
@@ -91,6 +96,10 @@ class Accessory::BetweenEachAccessor < Accessory::Accessor
91
96
  end
92
97
  end
93
98
 
94
- [results, new_data]
99
+ if dirty
100
+ [:dirty, results, new_data]
101
+ else
102
+ [:clean, results, data]
103
+ end
95
104
  end
96
105
  end
@@ -13,10 +13,11 @@ require 'accessory/traversal_position/enumerable_before_offset'
13
13
  # The +offset+ in this accessor has equivalent semantics to the offset in
14
14
  # <tt>Array#insert(offset, obj)</tt>.
15
15
  #
16
- # {BetwixtAccessor} can be used with {Lens#put_in} to insert new
16
+ # {Accessors::BetwixtAccessor} can be used with {Lens#put_in} to insert new
17
17
  # elements into an Enumerable between the existing ones. If you want to extend
18
18
  # an +Enumerable+ as you would with <tt>#push</tt> or <tt>#unshift</tt>, this
19
- # accessor will have better behavior than using {SubscriptAccessor} would.
19
+ # accessor will have better behavior than using {Accessors::SubscriptAccessor}
20
+ # would.
20
21
  #
21
22
  # *Aliases*
22
23
  # * {Access.betwixt}
@@ -26,7 +27,7 @@ require 'accessory/traversal_position/enumerable_before_offset'
26
27
  #
27
28
  # * +Array.new+
28
29
 
29
- class Accessory::BetwixtAccessor < Accessory::Accessor
30
+ class Accessory::Accessors::BetwixtAccessor < Accessory::Accessor
30
31
  # @param offset [Integer] the cursor position (i.e. the index of the element after the cursor)
31
32
  # @param default [Object] the default to use if the predecessor accessor passes +nil+ data
32
33
  def initialize(offset, default: nil)
@@ -99,12 +100,14 @@ class Accessory::BetwixtAccessor < Accessory::Accessor
99
100
  pos = traverse_or_default(data || [])
100
101
 
101
102
  case yield(pos)
102
- in [result, new_value]
103
+ in [:dirty, result, new_value]
103
104
  data ||= []
104
105
  data.insert(@offset, new_value)
105
- [result, data]
106
+ [:dirty, result, data]
106
107
  in :pop
107
- [nil, data]
108
+ [:clean, nil, data]
109
+ in [:clean, result, _]
110
+ [:clean, result, data]
108
111
  end
109
112
  end
110
113
  end
@@ -15,7 +15,7 @@ require 'accessory/accessor'
15
15
  #
16
16
  # * +Array.new+
17
17
 
18
- class Accessory::FilterAccessor < Accessory::Accessor
18
+ class Accessory::Accessors::FilterAccessor < Accessory::Accessor
19
19
  # Returns a new instance of {FilterAccessor}.
20
20
  #
21
21
  # The predicate function may be passed in as either a positional argument,
@@ -65,6 +65,7 @@ class Accessory::FilterAccessor < Accessory::Accessor
65
65
  def get_and_update(data)
66
66
  results = []
67
67
  new_data = []
68
+ dirty = false
68
69
 
69
70
  (data || []).each do |pos|
70
71
  unless @pred.call(pos)
@@ -73,14 +74,23 @@ class Accessory::FilterAccessor < Accessory::Accessor
73
74
  end
74
75
 
75
76
  case yield(pos)
76
- in [result, new_value]
77
+ in [:clean, result, _]
78
+ results.push(result)
79
+ new_data.push(pos)
80
+ in [:dirty, result, new_value]
77
81
  results.push(result)
78
82
  new_data.push(new_value)
83
+ dirty = true
79
84
  in :pop
80
85
  results.push(pos)
86
+ dirty = true
81
87
  end
82
88
  end
83
89
 
84
- [results, new_data]
90
+ if dirty
91
+ [:dirty, results, new_data]
92
+ else
93
+ [:clean, results, data]
94
+ end
85
95
  end
86
96
  end
@@ -4,8 +4,8 @@ require 'accessory/accessor'
4
4
  # Traverses into the "first" element within an +Enumerable+, using
5
5
  # <tt>#first</tt>.
6
6
  #
7
- # This accessor can be preferable to {SubscriptAccessor} for objects that
8
- # are not subscriptable, e.g. {Range}.
7
+ # This accessor can be preferable to {Accessors::SubscriptAccessor} for objects
8
+ # that are not subscriptable, e.g. +Range+.
9
9
  #
10
10
  # *Aliases*
11
11
  # * {Access.first}
@@ -15,7 +15,7 @@ require 'accessory/accessor'
15
15
  #
16
16
  # * +Array.new+
17
17
 
18
- class Accessory::FirstAccessor < Accessory::Accessor
18
+ class Accessory::Accessors::FirstAccessor < Accessory::Accessor
19
19
  # @!visibility private
20
20
  def ensure_valid(traversal_result)
21
21
  if traversal_result.kind_of?(Enumerable)
@@ -58,16 +58,18 @@ class Accessory::FirstAccessor < Accessory::Accessor
58
58
  old_value = traverse_or_default(data)
59
59
 
60
60
  case yield(old_value)
61
- in [result, new_value]
61
+ in [:clean, result, _]
62
+ [:clean, result, data]
63
+ in [:dirty, result, new_value]
62
64
  if data.respond_to?(:"first=")
63
65
  data.first = new_value
64
66
  else
65
67
  data[0] = new_value
66
68
  end
67
- [result, data]
69
+ [:dirty, result, data]
68
70
  in :pop
69
71
  data.delete_at(0)
70
- [old_value, data]
72
+ [:dirty, old_value, data]
71
73
  end
72
74
  end
73
75
  end
@@ -14,7 +14,7 @@ require 'accessory/accessor'
14
14
  #
15
15
  # * +Object.new+
16
16
 
17
- class Accessory::InstanceVariableAccessor < Accessory::Accessor
17
+ class Accessory::Accessors::InstanceVariableAccessor < Accessory::Accessor
18
18
  # @param ivar_name [Symbol] the instance-variable name
19
19
  # @param default [Object] the default to use if the predecessor accessor passes +nil+ data
20
20
  def initialize(ivar_name, default: nil)
@@ -70,12 +70,14 @@ class Accessory::InstanceVariableAccessor < Accessory::Accessor
70
70
  value = traverse_or_default(data)
71
71
 
72
72
  case yield(value)
73
- in [result, new_value]
73
+ in [:clean, result, _]
74
+ [:clean, result, data]
75
+ in [:dirty, result, new_value]
74
76
  data.instance_variable_set(@ivar_name, new_value)
75
- [result, data]
77
+ [:dirty, result, data]
76
78
  in :pop
77
79
  data.remove_instance_variable(@ivar_name)
78
- [value, data]
80
+ [:dirty, value, data]
79
81
  end
80
82
  end
81
83
  end
@@ -4,8 +4,8 @@ require 'accessory/accessor'
4
4
  # Traverses into the "last" element within an +Enumerable+, using
5
5
  # <tt>#last</tt>.
6
6
  #
7
- # This accessor can be preferable to {SubscriptAccessor} for objects that
8
- # are not subscriptable, e.g. {Range}.
7
+ # This accessor can be preferable to {Accessors::SubscriptAccessor} for objects
8
+ # that are not subscriptable, e.g. +Range+.
9
9
  #
10
10
  # *Aliases*
11
11
  # * {Access.last}
@@ -15,7 +15,7 @@ require 'accessory/accessor'
15
15
  #
16
16
  # * +Array.new+
17
17
 
18
- class Accessory::LastAccessor < Accessory::Accessor
18
+ class Accessory::Accessors::LastAccessor < Accessory::Accessor
19
19
  # @!visibility private
20
20
  def ensure_valid(traversal_result)
21
21
  if traversal_result.kind_of?(Enumerable)
@@ -58,16 +58,18 @@ class Accessory::LastAccessor < Accessory::Accessor
58
58
  old_value = traverse_or_default(data)
59
59
 
60
60
  case yield(old_value)
61
- in [result, new_value]
61
+ in [:clean, result, _]
62
+ [:clean, result, data]
63
+ in [:dirty, result, new_value]
62
64
  if data.respond_to?(:"last=")
63
65
  data.last = new_value
64
66
  else
65
67
  data[-1] = new_value
66
68
  end
67
- [result, data]
69
+ [:dirty, result, data]
68
70
  in :pop
69
71
  data.delete_at(-1)
70
- [old_value, data]
72
+ [:dirty, old_value, data]
71
73
  end
72
74
  end
73
75
  end
@@ -29,9 +29,9 @@ require 'accessory/accessor'
29
29
  # # default-constructs a Hash, not an Array
30
30
  # [].lens[0][0].put_in(1) # => [{0=>1}]
31
31
  #
32
- # Other accessors ({FirstAccessor}, {BetwixtAccessor}, etc.) may fit your expectations more closely for +Array+ traversal.
32
+ # Other accessors ({Accessors::FirstAccessor}, {Accessors::BetwixtAccessor}, etc.) may fit your expectations more closely for +Array+ traversal.
33
33
 
34
- class Accessory::SubscriptAccessor < Accessory::Accessor
34
+ class Accessory::Accessors::SubscriptAccessor < Accessory::Accessor
35
35
  # @!visibility private
36
36
  def initialize(key, **kwargs)
37
37
  super(**kwargs)
@@ -92,12 +92,14 @@ class Accessory::SubscriptAccessor < Accessory::Accessor
92
92
  value = traverse_or_default(data)
93
93
 
94
94
  case yield(value)
95
- in [result, new_value]
95
+ in [:clean, result, _]
96
+ [:clean, result, data]
97
+ in [:dirty, result, new_value]
96
98
  data[@key] = new_value
97
- [result, data]
99
+ [:dirty, result, data]
98
100
  in :pop
99
101
  data.delete(@key)
100
- [value, data]
102
+ [:dirty, value, data]
101
103
  end
102
104
  end
103
105
  end
@@ -164,7 +164,17 @@ class Accessory::Lens
164
164
  # 1. the _old_ value(s) found after all traversals, and
165
165
  # 2. the updated +subject+
166
166
  def update_in(subject, &new_value_fn)
167
- _, new_data = self.get_and_update_in(data){ |v| [nil, new_value_fn.call(v)] }
167
+ _, _, new_data = self.get_and_update_in(subject) do |v|
168
+ case new_value_fn.call(v)
169
+ in [:set, new_value]
170
+ [:dirty, nil, new_value]
171
+ in :keep
172
+ [:clean, nil, v]
173
+ in :pop
174
+ :pop
175
+ end
176
+ end
177
+
168
178
  new_data
169
179
  end
170
180
 
@@ -178,7 +188,7 @@ class Accessory::Lens
178
188
  # @param new_value [Object] a replacement value at the traversal position.
179
189
  # @return [Object] the updated +subject+
180
190
  def put_in(subject, new_value)
181
- _, new_data = self.get_and_update_in(subject){ [nil, new_value] }
191
+ _, _, new_data = self.get_and_update_in(subject){ [:dirty, nil, new_value] }
182
192
  new_data
183
193
  end
184
194
 
@@ -191,7 +201,8 @@ class Accessory::Lens
191
201
  # @param subject [Object] the data-structure to traverse
192
202
  # @return [Object] the updated +subject+
193
203
  def pop_in(subject)
194
- self.get_and_update_in(subject){ :pop }
204
+ _, popped_item, new_data = self.get_and_update_in(subject){ :pop }
205
+ [popped_item, new_data]
195
206
  end
196
207
 
197
208
  def append_accessor!(part)
@@ -200,9 +211,9 @@ class Accessory::Lens
200
211
  when Accessory::Accessor
201
212
  part
202
213
  when Array
203
- Accessory::SubscriptAccessor.new(part[0], default: part[1])
214
+ Accessory::Accessors::SubscriptAccessor.new(part[0], default: part[1])
204
215
  else
205
- Accessory::SubscriptAccessor.new(part)
216
+ Accessory::Accessors::SubscriptAccessor.new(part)
206
217
  end
207
218
 
208
219
  unless @parts.empty?
@@ -1,3 +1,3 @@
1
1
  module Accessory
2
- VERSION = "0.1.6"
2
+ VERSION = "0.1.11"
3
3
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: accessory
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Levi Aul
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-13 00:00:00.000000000 Z
11
+ date: 2021-02-03 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description:
13
+ description:
14
14
  email:
15
15
  - levi@leviaul.com
16
16
  executables: []
@@ -40,7 +40,7 @@ licenses:
40
40
  metadata:
41
41
  homepage_uri: https://github.com/tsutsu/accessory
42
42
  source_code_uri: https://github.com/tsutsu/accessory
43
- post_install_message:
43
+ post_install_message:
44
44
  rdoc_options: []
45
45
  require_paths:
46
46
  - lib
@@ -48,15 +48,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
48
48
  requirements:
49
49
  - - ">="
50
50
  - !ruby/object:Gem::Version
51
- version: 2.7.0
51
+ version: 2.6.0
52
52
  required_rubygems_version: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
56
  version: '0'
57
57
  requirements: []
58
- rubygems_version: 3.2.3
59
- signing_key:
58
+ rubygems_version: 3.0.3
59
+ signing_key:
60
60
  specification_version: 4
61
61
  summary: Functional lenses for Ruby, borrowed from Elixir
62
62
  test_files: []