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.
@@ -5,21 +5,25 @@ 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 {LensPath#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*
12
12
  # * {Access.between_each}
13
- # * {Access::FluentHelpers#between_each} (included in {LensPath} and {Lens})
13
+ # * {Access::FluentHelpers#between_each} (included in {Lens} and {BoundLens})
14
14
  #
15
15
  # <b>Default constructor</b> used by predecessor accessor
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
- def default_data_constructor
22
- lambda{ Array.new }
21
+ def ensure_valid(traversal_result)
22
+ if traversal_result.kind_of?(Enumerable)
23
+ traversal_result
24
+ else
25
+ []
26
+ end
23
27
  end
24
28
 
25
29
  # @!visibility private
@@ -46,7 +50,7 @@ class Accessory::BetweenEachAccessor < Accessory::Accessor
46
50
  # @param data [Enumerable] the +Enumerable+ to iterate through
47
51
  # @return [Array] the generated {TraversalPosition::EnumerableBeforeOffset}s
48
52
  def get(data)
49
- positions = value_or_default(data || [])
53
+ positions = traverse_or_default(data || [])
50
54
 
51
55
  if block_given?
52
56
  positions.map{ |rec| yield(rec) }
@@ -71,15 +75,20 @@ class Accessory::BetweenEachAccessor < Accessory::Accessor
71
75
  def get_and_update(data)
72
76
  results = []
73
77
  new_data = []
78
+ dirty = false
74
79
 
75
- positions = value_or_default(data || [])
80
+ positions = traverse_or_default(data || [])
76
81
 
77
82
  positions.each do |pos|
78
83
  case yield(pos)
79
- in [result, new_value]
84
+ in [:clean, result, _]
85
+ results.push(result)
86
+ in [:dirty, result, new_value]
80
87
  new_data.push(new_value)
81
88
  results.push(result)
89
+ dirty = true
82
90
  in :pop
91
+ # ok
83
92
  end
84
93
 
85
94
  unless pos.last?
@@ -87,6 +96,10 @@ class Accessory::BetweenEachAccessor < Accessory::Accessor
87
96
  end
88
97
  end
89
98
 
90
- [results, new_data]
99
+ if dirty
100
+ [:dirty, results, new_data]
101
+ else
102
+ [:clean, results, data]
103
+ end
91
104
  end
92
105
  end
@@ -13,20 +13,21 @@ 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 {LensPath#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}
23
- # * {Access::FluentHelpers#betwixt} (included in {LensPath} and {Lens})
24
+ # * {Access::FluentHelpers#betwixt} (included in {Lens} and {BoundLens})
24
25
  #
25
26
  # <b>Default constructor</b> used by predecessor accessor
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)
@@ -40,21 +41,30 @@ class Accessory::BetwixtAccessor < Accessory::Accessor
40
41
  end
41
42
 
42
43
  # @!visibility private
43
- def default_data_constructor
44
- lambda{ Array.new }
44
+ def ensure_valid(traversal_result)
45
+ if traversal_result.kind_of?(Enumerable)
46
+ traversal_result
47
+ else
48
+ []
49
+ end
45
50
  end
46
51
 
47
52
  # @!visibility private
48
53
  def traverse(data)
49
- data_len = data.length
54
+ nil
55
+ # return :error unless data.kind_of?(Enumerable)
56
+
57
+ # data_len = data.length
58
+
59
+ # ebo = Accessory::TraversalPosition::EnumerableBeforeOffset.new(
60
+ # @offset,
61
+ # (@offset > 0) ? data[@offset - 1] : nil,
62
+ # (@offset < (data_len - 1)) ? data[@offset + 1] : nil,
63
+ # is_first: @offset == 0,
64
+ # is_last: @offset == data_len
65
+ # )
50
66
 
51
- Accessory::TraversalPosition::EnumerableBeforeOffset.new(
52
- @offset,
53
- (@offset > 0) ? data[@offset - 1] : nil,
54
- (@offset < (data_len - 1)) ? data[@offset + 1] : nil,
55
- is_first: @offset == 0,
56
- is_last: @offset == data_len
57
- )
67
+ # [:ok, ebo]
58
68
  end
59
69
 
60
70
  # Feeds a {TraversalPosition::EnumerableBeforeOffset} representing the
@@ -64,7 +74,7 @@ class Accessory::BetwixtAccessor < Accessory::Accessor
64
74
  # @param data [Enumerable] the +Enumerable+ to traverse into
65
75
  # @return [Array] the generated {TraversalPosition::EnumerableBeforeOffset}
66
76
  def get(data)
67
- pos = value_or_default(data || [])
77
+ pos = traverse_or_default(data || [])
68
78
 
69
79
  if block_given?
70
80
  yield(pos)
@@ -87,15 +97,17 @@ class Accessory::BetwixtAccessor < Accessory::Accessor
87
97
  # 1. the generated {TraversalPosition::EnumerableBeforeOffset}
88
98
  # 2. the new {data}
89
99
  def get_and_update(data)
90
- pos = value_or_default(data || [])
100
+ pos = traverse_or_default(data || [])
91
101
 
92
102
  case yield(pos)
93
- in [result, new_value]
103
+ in [:dirty, result, new_value]
94
104
  data ||= []
95
105
  data.insert(@offset, new_value)
96
- [result, data]
106
+ [:dirty, result, data]
97
107
  in :pop
98
- [nil, data]
108
+ [:clean, nil, data]
109
+ in [:clean, result, _]
110
+ [:clean, result, data]
99
111
  end
100
112
  end
101
113
  end
@@ -6,7 +6,7 @@ require 'accessory/accessor'
6
6
  #
7
7
  # *Aliases*
8
8
  # * {Access.filter}
9
- # * {Access::FluentHelpers#filter} (included in {LensPath} and {Lens})
9
+ # * {Access::FluentHelpers#filter} (included in {Lens} and {BoundLens})
10
10
  #
11
11
  # *Equivalents* in Elixir's {https://hexdocs.pm/elixir/Access.html +Access+} module
12
12
  # * {https://hexdocs.pm/elixir/Access.html#filter/1 +Access.filter/1+}
@@ -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,
@@ -34,8 +34,12 @@ class Accessory::FilterAccessor < Accessory::Accessor
34
34
  end
35
35
 
36
36
  # @!visibility private
37
- def default_data_constructor
38
- lambda{ Array.new }
37
+ def ensure_valid(traversal_result)
38
+ if traversal_result.kind_of?(Enumerable)
39
+ traversal_result
40
+ else
41
+ []
42
+ end
39
43
  end
40
44
 
41
45
  # Feeds each element of +data+ matching the predicate down the accessor chain,
@@ -61,6 +65,7 @@ class Accessory::FilterAccessor < Accessory::Accessor
61
65
  def get_and_update(data)
62
66
  results = []
63
67
  new_data = []
68
+ dirty = false
64
69
 
65
70
  (data || []).each do |pos|
66
71
  unless @pred.call(pos)
@@ -69,14 +74,23 @@ class Accessory::FilterAccessor < Accessory::Accessor
69
74
  end
70
75
 
71
76
  case yield(pos)
72
- in [result, new_value]
77
+ in [:clean, result, _]
78
+ results.push(result)
79
+ new_data.push(pos)
80
+ in [:dirty, result, new_value]
73
81
  results.push(result)
74
82
  new_data.push(new_value)
83
+ dirty = true
75
84
  in :pop
76
85
  results.push(pos)
86
+ dirty = true
77
87
  end
78
88
  end
79
89
 
80
- [results, new_data]
90
+ if dirty
91
+ [:dirty, results, new_data]
92
+ else
93
+ [:clean, results, data]
94
+ end
81
95
  end
82
96
  end
@@ -4,21 +4,25 @@ 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}
12
- # * {Access::FluentHelpers#first} (included in {LensPath} and {Lens})
12
+ # * {Access::FluentHelpers#first} (included in {Lens} and {BoundLens})
13
13
  #
14
14
  # <b>Default constructor</b> used by predecessor 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
- def default_data_constructor
21
- lambda{ Array.new }
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
@@ -33,7 +37,7 @@ class Accessory::FirstAccessor < Accessory::Accessor
33
37
  # @param data [Object] the object to traverse
34
38
  # @return [Object] the value derived from the rest of the accessor chain
35
39
  def get(data)
36
- value = value_or_default(data)
40
+ value = traverse_or_default(data)
37
41
 
38
42
  if block_given?
39
43
  yield(value)
@@ -51,19 +55,21 @@ class Accessory::FirstAccessor < Accessory::Accessor
51
55
  # @param data [Object] the object to traverse
52
56
  # @return [Array] a two-element array containing 1. the original value found; and 2. the result value from the accessor chain
53
57
  def get_and_update(data)
54
- old_value = value_or_default(data)
58
+ old_value = traverse_or_default(data)
55
59
 
56
60
  case yield(old_value)
57
- in [result, new_value]
61
+ in [:clean, result, _]
62
+ [:clean, result, data]
63
+ in [:dirty, result, new_value]
58
64
  if data.respond_to?(:"first=")
59
65
  data.first = new_value
60
66
  else
61
67
  data[0] = new_value
62
68
  end
63
- [result, data]
69
+ [:dirty, result, data]
64
70
  in :pop
65
71
  data.delete_at(0)
66
- [old_value, data]
72
+ [:dirty, old_value, data]
67
73
  end
68
74
  end
69
75
  end
@@ -8,14 +8,14 @@ require 'accessory/accessor'
8
8
  #
9
9
  # *Aliases*
10
10
  # * {Access.ivar}
11
- # * {Access::FluentHelpers#ivar} (included in {LensPath} and {Lens})
11
+ # * {Access::FluentHelpers#ivar} (included in {Lens} and {BoundLens})
12
12
  #
13
13
  # <b>Default constructor</b> used by predecessor accessor
14
14
  #
15
15
  # * +Object.new+
16
16
 
17
- class Accessory::InstanceVariableAccessor < Accessory::Accessor
18
- # @param attr_name [Symbol] the instance-variable name
17
+ class Accessory::Accessors::InstanceVariableAccessor < Accessory::Accessor
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)
21
21
  super(default)
@@ -33,8 +33,8 @@ class Accessory::InstanceVariableAccessor < Accessory::Accessor
33
33
  end
34
34
 
35
35
  # @!visibility private
36
- def default_data_constructor
37
- lambda{ Object.new }
36
+ def ensure_valid(traversal_result)
37
+ traversal_result || Object.new
38
38
  end
39
39
 
40
40
  # @!visibility private
@@ -47,7 +47,7 @@ class Accessory::InstanceVariableAccessor < Accessory::Accessor
47
47
  # @param data [Object] the object to traverse
48
48
  # @return [Object] the value derived from the rest of the accessor chain
49
49
  def get(data)
50
- value = value_or_default(data)
50
+ value = traverse_or_default(data)
51
51
 
52
52
  if block_given?
53
53
  yield(value)
@@ -67,15 +67,17 @@ class Accessory::InstanceVariableAccessor < Accessory::Accessor
67
67
  # @param data [Object] the object to traverse
68
68
  # @return [Array] a two-element array containing 1. the original value found; and 2. the result value from the accessor chain
69
69
  def get_and_update(data)
70
- value = value_or_default(data)
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,21 +4,25 @@ 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}
12
- # * {Access::FluentHelpers#last} (included in {LensPath} and {Lens})
12
+ # * {Access::FluentHelpers#last} (included in {Lens} and {BoundLens})
13
13
  #
14
14
  # <b>Default constructor</b> used by predecessor 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
- def default_data_constructor
21
- lambda{ Array.new }
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
@@ -33,7 +37,7 @@ class Accessory::LastAccessor < Accessory::Accessor
33
37
  # @param data [Object] the object to traverse
34
38
  # @return [Object] the value derived from the rest of the accessor chain
35
39
  def get(data)
36
- value = value_or_default(data)
40
+ value = traverse_or_default(data)
37
41
 
38
42
  if block_given?
39
43
  yield(value)
@@ -51,19 +55,21 @@ class Accessory::LastAccessor < Accessory::Accessor
51
55
  # @param data [Object] the object to traverse
52
56
  # @return [Array] a two-element array containing 1. the original value found; and 2. the result value from the accessor chain
53
57
  def get_and_update(data)
54
- old_value = value_or_default(data)
58
+ old_value = traverse_or_default(data)
55
59
 
56
60
  case yield(old_value)
57
- in [result, new_value]
61
+ in [:clean, result, _]
62
+ [:clean, result, data]
63
+ in [:dirty, result, new_value]
58
64
  if data.respond_to?(:"last=")
59
65
  data.last = new_value
60
66
  else
61
67
  data[-1] = new_value
62
68
  end
63
- [result, data]
69
+ [:dirty, result, data]
64
70
  in :pop
65
71
  data.delete_at(-1)
66
- [old_value, data]
72
+ [:dirty, old_value, data]
67
73
  end
68
74
  end
69
75
  end
@@ -7,9 +7,10 @@ require 'accessory/accessor'
7
7
  #
8
8
  # *Aliases*
9
9
  # * {Access.subscript}
10
- # * {Access::FluentHelpers#subscript} (included in {LensPath} and {Lens})
11
- # * {Access::FluentHelpers#[]} (included in {LensPath} and {Lens})
12
- # * just passing a +key+ will also work, when +not(key.kind_of?(Accessor))+ (this is a special case in {LensPath#initialize})
10
+ # * {Access::FluentHelpers#subscript} (included in {Lens} and {BoundLens})
11
+ # * {Access::FluentHelpers#[]} (included in {Lens} and {BoundLens})
12
+ # * just passing a +key+ will also work, when +not(key.kind_of?(Accessor))+
13
+ # (this is a special case in {Lens#initialize})
13
14
  #
14
15
  # *Equivalents* in Elixir's {https://hexdocs.pm/elixir/Access.html +Access+} module
15
16
  # * {https://hexdocs.pm/elixir/Access.html#at/1 +Access.at/1+}
@@ -28,9 +29,9 @@ require 'accessory/accessor'
28
29
  # # default-constructs a Hash, not an Array
29
30
  # [].lens[0][0].put_in(1) # => [{0=>1}]
30
31
  #
31
- # 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.
32
33
 
33
- class Accessory::SubscriptAccessor < Accessory::Accessor
34
+ class Accessory::Accessors::SubscriptAccessor < Accessory::Accessor
34
35
  # @!visibility private
35
36
  def initialize(key, **kwargs)
36
37
  super(**kwargs)
@@ -53,8 +54,12 @@ class Accessory::SubscriptAccessor < Accessory::Accessor
53
54
  end
54
55
 
55
56
  # @!visibility private
56
- def default_data_constructor
57
- lambda{ Hash.new }
57
+ def ensure_valid(traversal_result)
58
+ if traversal_result.respond_to?(:[])
59
+ traversal_result
60
+ else
61
+ {}
62
+ end
58
63
  end
59
64
 
60
65
  # @!visibility private
@@ -67,7 +72,7 @@ class Accessory::SubscriptAccessor < Accessory::Accessor
67
72
  # @param data [Enumerable] the +Enumerable+ to index into
68
73
  # @return [Object] the value derived from the rest of the accessor chain
69
74
  def get(data)
70
- value = value_or_default(data)
75
+ value = traverse_or_default(data)
71
76
 
72
77
  if block_given?
73
78
  yield(value)
@@ -84,15 +89,17 @@ class Accessory::SubscriptAccessor < Accessory::Accessor
84
89
  # @param data [Enumerable] the +Enumerable+ to index into
85
90
  # @return [Array] a two-element array containing 1. the original value found; and 2. the result value from the accessor chain
86
91
  def get_and_update(data)
87
- value = value_or_default(data)
92
+ value = traverse_or_default(data)
88
93
 
89
94
  case yield(value)
90
- in [result, new_value]
95
+ in [:clean, result, _]
96
+ [:clean, result, data]
97
+ in [:dirty, result, new_value]
91
98
  data[@key] = new_value
92
- [result, data]
99
+ [:dirty, result, data]
93
100
  in :pop
94
101
  data.delete(@key)
95
- [value, data]
102
+ [:dirty, value, data]
96
103
  end
97
104
  end
98
105
  end