accessory 0.1.4 → 0.1.9

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.
@@ -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 {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}
16
- # * {Access::FluentHelpers#attr} (included in {LensPath} and {Lens})
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,15 +47,17 @@ class Accessory::AttributeAccessor < Accessory::Accessor
47
47
  end
48
48
 
49
49
  # @!visibility private
50
- def default_fn_for_previous_step
51
- lambda do
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
55
57
  end
56
58
 
57
59
  # @!visibility private
58
- def value_from(data)
60
+ def traverse(data)
59
61
  data.send(@getter_method_name)
60
62
  end
61
63
 
@@ -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 = value_or_default(data)
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 = value_or_default(data)
88
+ value = traverse_or_default(data)
87
89
 
88
90
  case yield(value)
89
- in [result, new_value]
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
@@ -1,32 +1,36 @@
1
1
  require 'accessory/accessor'
2
- require 'accessory/array_cursor_position'
2
+ require 'accessory/traversal_position/enumerable_before_offset'
3
3
 
4
4
  ##
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_fn_for_previous_step
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
26
30
  def inspect_args; nil; end
27
31
 
28
32
  # @!visibility private
29
- def value_from(data)
33
+ def traverse(data)
30
34
  data_len = data.length
31
35
 
32
36
  positions = [
@@ -36,17 +40,17 @@ class Accessory::BetweenEachAccessor < Accessory::Accessor
36
40
  ]
37
41
 
38
42
  positions.transpose.map do |(i, b, a)|
39
- Accessory::ArrayCursorPosition.new(i, b, a, is_first: i == 0, is_last: i == data_len)
43
+ Accessory::TraversalPosition::EnumerableBeforeOffset.new(i, b, a, is_first: i == 0, is_last: i == data_len)
40
44
  end
41
45
  end
42
46
 
43
- # Feeds {ArrayCursorPosition}s representing the positions between the elements
44
- # of +data+ down the accessor chain.
47
+ # Feeds {TraversalPosition::EnumerableBeforeOffset}s representing the
48
+ # positions between the elements of +data+ down the accessor chain.
45
49
  #
46
50
  # @param data [Enumerable] the +Enumerable+ to iterate through
47
- # @return [Array] the generated {ArrayCursorPosition}s
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) }
@@ -55,8 +59,9 @@ class Accessory::BetweenEachAccessor < Accessory::Accessor
55
59
  end
56
60
  end
57
61
 
58
- # Feeds {ArrayCursorPosition}s representing the positions between the elements
59
- # of +data+ down the accessor chain, manipulating +data+ using the results.
62
+ # Feeds {TraversalPosition::EnumerableBeforeOffset}s representing the
63
+ # positions between the elements of +data+ down the accessor chain,
64
+ # manipulating +data+ using the results.
60
65
  #
61
66
  # If a new element is returned up the accessor chain, the element is inserted
62
67
  # between the existing elements.
@@ -64,19 +69,26 @@ class Accessory::BetweenEachAccessor < Accessory::Accessor
64
69
  # If +:pop+ is returned up the accessor chain, no new element is added.
65
70
  #
66
71
  # @param data [Enumerable] the +Enumerable+ to iterate through
67
- # @return [Array] a two-element array containing 1. the {ArrayCursorPosition}s; and 2. the new {data}
72
+ # @return [Array] a two-element array containing
73
+ # 1. the {TraversalPosition::EnumerableBeforeOffset}s
74
+ # 2. the new {data}
68
75
  def get_and_update(data)
69
76
  results = []
70
77
  new_data = []
78
+ dirty = false
71
79
 
72
- positions = value_or_default(data || [])
80
+ positions = traverse_or_default(data || [])
73
81
 
74
82
  positions.each do |pos|
75
83
  case yield(pos)
76
- in [result, new_value]
84
+ in [:clean, result, _]
85
+ results.push(result)
86
+ in [:dirty, result, new_value]
77
87
  new_data.push(new_value)
78
88
  results.push(result)
89
+ dirty = true
79
90
  in :pop
91
+ # ok
80
92
  end
81
93
 
82
94
  unless pos.last?
@@ -84,6 +96,10 @@ class Accessory::BetweenEachAccessor < Accessory::Accessor
84
96
  end
85
97
  end
86
98
 
87
- [results, new_data]
99
+ if dirty
100
+ [:dirty, results, new_data]
101
+ else
102
+ [:clean, results, data]
103
+ end
88
104
  end
89
105
  end
@@ -1,5 +1,5 @@
1
1
  require 'accessory/accessor'
2
- require 'accessory/array_cursor_position'
2
+ require 'accessory/traversal_position/enumerable_before_offset'
3
3
 
4
4
  ##
5
5
  # Traverses into a specified cursor-position "between" two elements of an
@@ -13,20 +13,21 @@ require 'accessory/array_cursor_position'
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,30 +41,40 @@ class Accessory::BetwixtAccessor < Accessory::Accessor
40
41
  end
41
42
 
42
43
  # @!visibility private
43
- def default_fn_for_previous_step
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
- def value_from(data)
49
- data_len = data.length
53
+ def traverse(data)
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::ArrayCursorPosition.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
- # Feeds an {ArrayCursorPosition} representing the position between the
61
- # elements of +data+ at +@offset+ down the accessor chain.
70
+ # Feeds a {TraversalPosition::EnumerableBeforeOffset} representing the
71
+ # position between the elements of +data+ at +@offset+ down the accessor
72
+ # chain.
62
73
  #
63
74
  # @param data [Enumerable] the +Enumerable+ to traverse into
64
- # @return [Array] the generated {ArrayCursorPosition}
75
+ # @return [Array] the generated {TraversalPosition::EnumerableBeforeOffset}
65
76
  def get(data)
66
- pos = value_or_default(data || [])
77
+ pos = traverse_or_default(data || [])
67
78
 
68
79
  if block_given?
69
80
  yield(pos)
@@ -72,9 +83,9 @@ class Accessory::BetwixtAccessor < Accessory::Accessor
72
83
  end
73
84
  end
74
85
 
75
- # Feeds an {ArrayCursorPosition} representing the position between the
76
- # elements of +data+ at +@offset+ down the accessor chain, manipulating
77
- # +data+ using the result.
86
+ # Feeds a {TraversalPosition::EnumerableBeforeOffset} representing the
87
+ # position between the elements of +data+ at +@offset+ down the accessor
88
+ # chain, manipulating +data+ using the result.
78
89
  #
79
90
  # If a new element is returned up the accessor chain, the element is inserted
80
91
  # at the specified position, using <tt>data.insert(@offset, e)</tt>.
@@ -82,17 +93,21 @@ class Accessory::BetwixtAccessor < Accessory::Accessor
82
93
  # If +:pop+ is returned up the accessor chain, no new element is added.
83
94
  #
84
95
  # @param data [Enumerable] the +Enumerable+ to traverse into
85
- # @return [Array] a two-element array containing 1. the generated {ArrayCursorPosition}; and 2. the new {data}
96
+ # @return [Array] a two-element array containing
97
+ # 1. the generated {TraversalPosition::EnumerableBeforeOffset}
98
+ # 2. the new {data}
86
99
  def get_and_update(data)
87
- pos = value_or_default(data || [])
100
+ pos = traverse_or_default(data || [])
88
101
 
89
102
  case yield(pos)
90
- in [result, new_value]
103
+ in [:dirty, result, new_value]
91
104
  data ||= []
92
105
  data.insert(@offset, new_value)
93
- [result, data]
106
+ [:dirty, result, data]
94
107
  in :pop
95
- [nil, data]
108
+ [:clean, nil, data]
109
+ in [:clean, result, _]
110
+ [:clean, result, data]
96
111
  end
97
112
  end
98
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_fn_for_previous_step
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,28 +4,32 @@ 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_fn_for_previous_step
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
25
29
  def inspect_args; nil; end
26
30
 
27
31
  # @!visibility private
28
- def value_from(data)
32
+ def traverse(data)
29
33
  data.first
30
34
  end
31
35
 
@@ -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