accessory 0.1.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 +7 -0
- data/lib/accessory.rb +14 -0
- data/lib/accessory/access.rb +107 -0
- data/lib/accessory/accessor.rb +55 -0
- data/lib/accessory/accessors/all_accessor.rb +34 -0
- data/lib/accessory/accessors/between_each_accessor.rb +56 -0
- data/lib/accessory/accessors/betwixt_accessor.rb +52 -0
- data/lib/accessory/accessors/field_accessor.rb +47 -0
- data/lib/accessory/accessors/filter_accessor.rb +45 -0
- data/lib/accessory/accessors/first_accessor.rb +36 -0
- data/lib/accessory/accessors/instance_variable_accessor.rb +48 -0
- data/lib/accessory/accessors/last_accessor.rb +36 -0
- data/lib/accessory/accessors/subscript_accessor.rb +52 -0
- data/lib/accessory/array_cursor_position.rb +18 -0
- data/lib/accessory/lens.rb +66 -0
- data/lib/accessory/lens_path.rb +140 -0
- data/lib/accessory/version.rb +3 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 43e0f691627cd491ac44fecb56547a50a5688215a1a65a494407557b9e6abc0b
|
4
|
+
data.tar.gz: 6775e183ab5791f8fb7e484593bc9cda59fde745a7f7bbfd38fd7fd3180a5c77
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9a258b50f71e28a4f43c934aa53fdb53e348665ae2c269a0f427fbc6febdda97a00e6891d12c5d9cb53a94be513a0c4e59d1739d6d11656aabf1898364738bcc
|
7
|
+
data.tar.gz: 7bfb51fa829b919ac35a78e0ee17aa9f2edd439e155df61c509dada8a94c8ed0f5c2d3915b1766869547b558685175d8691a3c7ccaffe32b5becd9d00445d2d1
|
data/lib/accessory.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
module Accessory; end
|
2
|
+
|
3
|
+
require 'accessory/accessors/subscript_accessor'
|
4
|
+
require 'accessory/accessors/field_accessor'
|
5
|
+
require 'accessory/accessors/filter_accessor'
|
6
|
+
require 'accessory/accessors/instance_variable_accessor'
|
7
|
+
require 'accessory/accessors/betwixt_accessor'
|
8
|
+
require 'accessory/accessors/between_each_accessor'
|
9
|
+
require 'accessory/accessors/all_accessor'
|
10
|
+
require 'accessory/accessors/first_accessor'
|
11
|
+
require 'accessory/accessors/last_accessor'
|
12
|
+
|
13
|
+
module Accessory::Access
|
14
|
+
def self.field(...)
|
15
|
+
Accessory::FieldAccessor.new(...)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.ivar(...)
|
19
|
+
Accessory::InstanceVariableAccessor.new(...)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.betwixt(...)
|
23
|
+
Accessory::BetwixtAccessor.new(...)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.before_first
|
27
|
+
self.betwixt(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.after_last
|
31
|
+
self.betwixt(-1)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.between_each
|
35
|
+
Accessory::BetweenEachAccessor.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.all
|
39
|
+
Accessory::AllAccessor.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.first
|
43
|
+
Accessory::FirstAccessor.new
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.last
|
47
|
+
Accessory::LastAccessor.new
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.filter(&pred)
|
51
|
+
Accessory::FilterAccessor.new(pred)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module Accessory::Access::FluentHelpers
|
56
|
+
def [](...)
|
57
|
+
self.then(Accessory::SubscriptAccessor.new(...))
|
58
|
+
end
|
59
|
+
|
60
|
+
def field(...)
|
61
|
+
self.then(Accessory::FieldAccessor.new(...))
|
62
|
+
end
|
63
|
+
|
64
|
+
def ivar(...)
|
65
|
+
self.then(Accessory::InstanceVariableAccessor.new(...))
|
66
|
+
end
|
67
|
+
|
68
|
+
def betwixt(...)
|
69
|
+
self.then(Accessory::BetwixtAccessor.new(...))
|
70
|
+
end
|
71
|
+
|
72
|
+
def before_first
|
73
|
+
self.betwixt(0)
|
74
|
+
end
|
75
|
+
|
76
|
+
def after_last
|
77
|
+
self.betwixt(-1)
|
78
|
+
end
|
79
|
+
|
80
|
+
def between_each
|
81
|
+
self.then(Accessory::BetweenEachAccessor.new)
|
82
|
+
end
|
83
|
+
|
84
|
+
def all
|
85
|
+
self.then(Accessory::AllAccessor.new)
|
86
|
+
end
|
87
|
+
|
88
|
+
def first
|
89
|
+
self.then(Accessory::FirstAccessor.new)
|
90
|
+
end
|
91
|
+
|
92
|
+
def last
|
93
|
+
self.then(Accessory::LastAccessor.new)
|
94
|
+
end
|
95
|
+
|
96
|
+
def filter(&pred)
|
97
|
+
self.then(Accessory::FilterAccessor.new(pred))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Accessory::Lens
|
102
|
+
include Accessory::Access::FluentHelpers
|
103
|
+
end
|
104
|
+
|
105
|
+
class Accessory::LensPath
|
106
|
+
include Accessory::Access::FluentHelpers
|
107
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Accessory; end
|
2
|
+
|
3
|
+
class Accessory::Accessor
|
4
|
+
DEFAULT_NOT_SET_SENTINEL = :"98e47971-e708-42ca-bee7-0c62fe5e11c9"
|
5
|
+
TERMINAL_DEFAULT_FN = lambda{ nil }
|
6
|
+
|
7
|
+
def initialize(default: DEFAULT_NOT_SET_SENTINEL)
|
8
|
+
@default_value = default
|
9
|
+
@make_default_fn = TERMINAL_DEFAULT_FN
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
n = self.class.name.split('::').last.gsub(/Accessor$/, '')
|
14
|
+
n.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
15
|
+
n.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
16
|
+
n.tr!("-", "_")
|
17
|
+
n.downcase!
|
18
|
+
n
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect(format: :long)
|
22
|
+
case format
|
23
|
+
when :long
|
24
|
+
parts = ["Access.#{self.name}", inspect_args].compact.join(' ')
|
25
|
+
"#<#{parts}>"
|
26
|
+
when :short
|
27
|
+
fn_name = "Access.#{self.name}"
|
28
|
+
args = inspect_args
|
29
|
+
args ? "#{fn_name}(#{args})" : fn_name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
HIDDEN_IVARS = [:@default_value, :@make_default_fn]
|
34
|
+
def inspect_args
|
35
|
+
(instance_variables - HIDDEN_IVARS).map do |ivar_k|
|
36
|
+
ivar_v = instance_variable_get(ivar_k)
|
37
|
+
"#{ivar_k}=#{ivar_v.inspect}"
|
38
|
+
end.join(' ')
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_accessor :make_default_fn
|
42
|
+
|
43
|
+
def value_or_default(data)
|
44
|
+
return nil if data.nil?
|
45
|
+
|
46
|
+
maybe_value = value_from(data)
|
47
|
+
return maybe_value unless maybe_value.nil?
|
48
|
+
|
49
|
+
if DEFAULT_NOT_SET_SENTINEL.equal?(@default_value)
|
50
|
+
@make_default_fn.call
|
51
|
+
else
|
52
|
+
@default_value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'accessory/accessor'
|
2
|
+
|
3
|
+
class Accessory::AllAccessor < Accessory::Accessor
|
4
|
+
def default_fn_for_previous_step
|
5
|
+
lambda{ Array.new }
|
6
|
+
end
|
7
|
+
|
8
|
+
def inspect_args; nil; end
|
9
|
+
|
10
|
+
def get(data, &succ)
|
11
|
+
if succ
|
12
|
+
(data || []).map(&succ)
|
13
|
+
else
|
14
|
+
data
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_and_update(data)
|
19
|
+
results = []
|
20
|
+
new_data = []
|
21
|
+
|
22
|
+
(data || []).each do |pos|
|
23
|
+
case yield(pos)
|
24
|
+
in [result, new_value]
|
25
|
+
results.push(result)
|
26
|
+
new_data.push(new_value)
|
27
|
+
in :pop
|
28
|
+
results.push(pos)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
[results, new_data]
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'accessory/accessor'
|
2
|
+
require 'accessory/array_cursor_position'
|
3
|
+
|
4
|
+
class Accessory::BetweenEachAccessor < Accessory::Accessor
|
5
|
+
def default_fn_for_previous_step
|
6
|
+
lambda{ Array.new }
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect_args; nil; end
|
10
|
+
|
11
|
+
def value_from(data)
|
12
|
+
data_len = data.length
|
13
|
+
|
14
|
+
positions = [
|
15
|
+
(0..data_len).to_a,
|
16
|
+
data + [nil],
|
17
|
+
[nil] + data
|
18
|
+
]
|
19
|
+
|
20
|
+
positions.transpose.map do |(i, b, a)|
|
21
|
+
Accessory::ArrayCursorPosition.new(i, b, a, is_first: i == 0, is_last: i == data_len)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(data)
|
26
|
+
positions = value_or_default(data || [])
|
27
|
+
|
28
|
+
if block_given?
|
29
|
+
positions.map{ |rec| yield(rec) }
|
30
|
+
else
|
31
|
+
positions
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_and_update(data)
|
36
|
+
results = []
|
37
|
+
new_data = []
|
38
|
+
|
39
|
+
positions = value_or_default(data || [])
|
40
|
+
|
41
|
+
positions.each do |pos|
|
42
|
+
case yield(pos)
|
43
|
+
in [result, new_value]
|
44
|
+
new_data.push(new_value)
|
45
|
+
results.push(result)
|
46
|
+
in :pop
|
47
|
+
end
|
48
|
+
|
49
|
+
unless pos.last?
|
50
|
+
new_data.push(pos.elem_after)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
[results, new_data]
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'accessory/accessor'
|
2
|
+
require 'accessory/array_cursor_position'
|
3
|
+
|
4
|
+
class Accessory::BetwixtAccessor < Accessory::Accessor
|
5
|
+
def initialize(offset, **kwargs)
|
6
|
+
super(**kwargs)
|
7
|
+
@offset = offset
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect_args
|
11
|
+
@offset.inspect
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_fn_for_previous_step
|
15
|
+
lambda{ Array.new }
|
16
|
+
end
|
17
|
+
|
18
|
+
def value_from(data)
|
19
|
+
data_len = data.length
|
20
|
+
|
21
|
+
Accessory::ArrayCursorPosition.new(
|
22
|
+
@offset,
|
23
|
+
(@offset > 0) ? data[@offset - 1] : nil,
|
24
|
+
(@offset < (data_len - 1)) ? data[@offset + 1] : nil,
|
25
|
+
is_first: @offset == 0,
|
26
|
+
is_last: @offset == data_len
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def get(data)
|
31
|
+
pos = value_or_default(data || [])
|
32
|
+
|
33
|
+
if block_given?
|
34
|
+
yield(pos)
|
35
|
+
else
|
36
|
+
pos
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_and_update(data)
|
41
|
+
pos = value_or_default(data || [])
|
42
|
+
|
43
|
+
case yield(pos)
|
44
|
+
in [result, new_value]
|
45
|
+
data ||= []
|
46
|
+
data.insert(@offset, new_value)
|
47
|
+
[result, data]
|
48
|
+
in :pop
|
49
|
+
[nil, data]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'accessory/accessor'
|
2
|
+
|
3
|
+
class Accessory::FieldAccessor < Accessory::Accessor
|
4
|
+
def initialize(field_name, **kwargs)
|
5
|
+
super(**kwargs)
|
6
|
+
@getter_method_name = :"#{field_name}"
|
7
|
+
@setter_method_name = :"#{field_name}="
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect_args
|
11
|
+
@getter_method_name.inspect
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_fn_for_previous_step
|
15
|
+
lambda do
|
16
|
+
require 'ostruct'
|
17
|
+
OpenStruct.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def value_from(data)
|
22
|
+
data.send(@getter_method_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(data)
|
26
|
+
value = value_or_default(data)
|
27
|
+
|
28
|
+
if block_given?
|
29
|
+
yield(value)
|
30
|
+
else
|
31
|
+
value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_and_update(data)
|
36
|
+
value = value_or_default(data)
|
37
|
+
|
38
|
+
case yield(value)
|
39
|
+
in [result, new_value]
|
40
|
+
data.send(@setter_method_name, new_value)
|
41
|
+
[result, data]
|
42
|
+
in :pop
|
43
|
+
data.send(@setter_method_name, nil)
|
44
|
+
[value, data]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'accessory/accessor'
|
2
|
+
|
3
|
+
class Accessory::FilterAccessor < Accessory::Accessor
|
4
|
+
def initialize(pred)
|
5
|
+
@pred = pred
|
6
|
+
end
|
7
|
+
|
8
|
+
def inspect_args
|
9
|
+
@pred.inspect
|
10
|
+
end
|
11
|
+
|
12
|
+
def default_fn_for_previous_step
|
13
|
+
lambda{ Array.new }
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(data, &succ)
|
17
|
+
if succ
|
18
|
+
(data || []).filter(&@pred).map(&succ)
|
19
|
+
else
|
20
|
+
data
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_and_update(data)
|
25
|
+
results = []
|
26
|
+
new_data = []
|
27
|
+
|
28
|
+
(data || []).each do |pos|
|
29
|
+
unless @pred.call(pos)
|
30
|
+
new_data.push(pos)
|
31
|
+
next
|
32
|
+
end
|
33
|
+
|
34
|
+
case yield(pos)
|
35
|
+
in [result, new_value]
|
36
|
+
results.push(result)
|
37
|
+
new_data.push(new_value)
|
38
|
+
in :pop
|
39
|
+
results.push(pos)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
[results, new_data]
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'accessory/accessor'
|
2
|
+
|
3
|
+
class Accessory::FirstAccessor < Accessory::Accessor
|
4
|
+
def default_fn_for_previous_step
|
5
|
+
lambda{ Array.new }
|
6
|
+
end
|
7
|
+
|
8
|
+
def inspect_args; nil; end
|
9
|
+
|
10
|
+
def value_from(data)
|
11
|
+
data.first
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(data)
|
15
|
+
value = value_or_default(data)
|
16
|
+
|
17
|
+
if block_given?
|
18
|
+
yield(value)
|
19
|
+
else
|
20
|
+
value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_and_update(data)
|
25
|
+
old_value = value_or_default(data)
|
26
|
+
|
27
|
+
case yield(old_value)
|
28
|
+
in [result, new_value]
|
29
|
+
data[0] = new_value
|
30
|
+
[result, data]
|
31
|
+
in :pop
|
32
|
+
data.delete_at(0)
|
33
|
+
[old_value, data]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'accessory/accessor'
|
2
|
+
|
3
|
+
class Accessory::InstanceVariableAccessor < Accessory::Accessor
|
4
|
+
def initialize(ivar_name, **kwargs)
|
5
|
+
super(**kwargs)
|
6
|
+
|
7
|
+
ivar_name = ivar_name.to_s
|
8
|
+
ivar_name = "@#{ivar_name}" unless ivar_name.to_s.start_with?("@")
|
9
|
+
ivar_name = ivar_name.intern
|
10
|
+
|
11
|
+
@ivar_name = ivar_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect_args
|
15
|
+
@ivar_name.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_fn_for_previous_step
|
19
|
+
lambda{ Object.new }
|
20
|
+
end
|
21
|
+
|
22
|
+
def value_from(data)
|
23
|
+
data.instance_variable_get(@ivar_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(data)
|
27
|
+
value = value_or_default(data)
|
28
|
+
|
29
|
+
if block_given?
|
30
|
+
yield(value)
|
31
|
+
else
|
32
|
+
value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_and_update(data)
|
37
|
+
value = value_or_default(data)
|
38
|
+
|
39
|
+
case yield(value)
|
40
|
+
in [result, new_value]
|
41
|
+
data.instance_variable_set(@ivar_name, new_value)
|
42
|
+
[result, data]
|
43
|
+
in :pop
|
44
|
+
data.remove_instance_variable(@ivar_name)
|
45
|
+
[value, data]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'accessory/accessor'
|
2
|
+
|
3
|
+
class Accessory::LastAccessor < Accessory::Accessor
|
4
|
+
def default_fn_for_previous_step
|
5
|
+
lambda{ Array.new }
|
6
|
+
end
|
7
|
+
|
8
|
+
def inspect_args; nil; end
|
9
|
+
|
10
|
+
def value_from(data)
|
11
|
+
data.last
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(data)
|
15
|
+
value = value_or_default(data)
|
16
|
+
|
17
|
+
if block_given?
|
18
|
+
yield(value)
|
19
|
+
else
|
20
|
+
value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_and_update(data)
|
25
|
+
old_value = value_or_default(data)
|
26
|
+
|
27
|
+
case yield(old_value)
|
28
|
+
in [result, new_value]
|
29
|
+
data[-1] = new_value
|
30
|
+
[result, data]
|
31
|
+
in :pop
|
32
|
+
data.delete_at(-1)
|
33
|
+
[old_value, data]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'accessory/accessor'
|
2
|
+
|
3
|
+
class Accessory::SubscriptAccessor < Accessory::Accessor
|
4
|
+
def initialize(key, **kwargs)
|
5
|
+
super(**kwargs)
|
6
|
+
@key = key
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect(format: :long)
|
10
|
+
case format
|
11
|
+
when :long
|
12
|
+
super()
|
13
|
+
when :short
|
14
|
+
@key.inspect
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect_args
|
19
|
+
@key.inspect
|
20
|
+
end
|
21
|
+
|
22
|
+
def default_fn_for_previous_step
|
23
|
+
lambda{ Hash.new }
|
24
|
+
end
|
25
|
+
|
26
|
+
def value_from(data)
|
27
|
+
data[@key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def get(data)
|
31
|
+
value = value_or_default(data)
|
32
|
+
|
33
|
+
if block_given?
|
34
|
+
yield(value)
|
35
|
+
else
|
36
|
+
value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_and_update(data)
|
41
|
+
value = value_or_default(data)
|
42
|
+
|
43
|
+
case yield(value)
|
44
|
+
in [result, new_value]
|
45
|
+
data[@key] = new_value
|
46
|
+
[result, data]
|
47
|
+
in :pop
|
48
|
+
data.delete(@key)
|
49
|
+
[value, data]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Accessory; end
|
2
|
+
|
3
|
+
class Accessory::ArrayCursorPosition
|
4
|
+
def initialize(offset, elem_before, elem_after, is_first: false, is_last: false)
|
5
|
+
@offset = offset
|
6
|
+
@elem_before = elem_before
|
7
|
+
@elem_after = elem_after
|
8
|
+
@is_first = is_first
|
9
|
+
@is_last = is_last
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :offset
|
13
|
+
attr_reader :elem_before
|
14
|
+
attr_reader :elem_after
|
15
|
+
|
16
|
+
def first?; @is_first; end
|
17
|
+
def last?; @is_last; end
|
18
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Accessory; end
|
2
|
+
|
3
|
+
require 'accessory/lens_path'
|
4
|
+
|
5
|
+
class Accessory::Lens
|
6
|
+
def self.on(doc, path: nil)
|
7
|
+
self.new(doc, path || Accessory::LensPath.empty).freeze
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
private :new
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(doc, lens_path)
|
15
|
+
@doc = doc
|
16
|
+
@path = lens_path
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :path
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#<Lens on=#{@doc.inspect} #{@path.inspect(format: :short)}>"
|
23
|
+
end
|
24
|
+
|
25
|
+
def then(accessor)
|
26
|
+
d = self.dup
|
27
|
+
d.instance_eval do
|
28
|
+
@path = @path.then(accessor)
|
29
|
+
end
|
30
|
+
d.freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
def +(lens_path)
|
34
|
+
d = self.dup
|
35
|
+
d.instance_eval do
|
36
|
+
@path = @path + lens_path
|
37
|
+
end
|
38
|
+
d.freeze
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_in
|
42
|
+
@path.get_in(@doc)
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_and_update_in(&mutator_fn)
|
46
|
+
@path.get_and_update_in(@doc, &mutator_fn)
|
47
|
+
end
|
48
|
+
|
49
|
+
def update_in(&new_value_fn)
|
50
|
+
@path.update_in(@doc, &new_value_fn)
|
51
|
+
end
|
52
|
+
|
53
|
+
def put_in(new_value)
|
54
|
+
@path.put_in(@doc, new_value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def pop_in
|
58
|
+
@path.pop_in(@doc)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Accessory::LensPath
|
63
|
+
def on(doc)
|
64
|
+
Accessory::Lens.on(doc, path: self)
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Accessory; end
|
2
|
+
|
3
|
+
require 'accessory/accessor'
|
4
|
+
require 'accessory/accessors/subscript_accessor'
|
5
|
+
|
6
|
+
class Accessory::LensPath
|
7
|
+
def self.empty
|
8
|
+
@empty_lens_path ||= (new([]).freeze)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.[](*path)
|
12
|
+
new(path).freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
private :new
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(initial_parts)
|
20
|
+
@parts = []
|
21
|
+
|
22
|
+
for part in initial_parts
|
23
|
+
append_accessor!(part)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_a
|
28
|
+
@parts
|
29
|
+
end
|
30
|
+
|
31
|
+
def inspect(format: :long)
|
32
|
+
parts_desc = @parts.map{ |part| part.inspect(format: :short) }.join(', ')
|
33
|
+
parts_desc = "[#{parts_desc}]"
|
34
|
+
|
35
|
+
case format
|
36
|
+
when :long
|
37
|
+
"#LensPath#{parts_desc}"
|
38
|
+
when :short
|
39
|
+
parts_desc
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def then(accessor)
|
44
|
+
d = self.dup
|
45
|
+
d.append_accessor!(accessor)
|
46
|
+
d.freeze
|
47
|
+
end
|
48
|
+
|
49
|
+
def dup
|
50
|
+
d = super
|
51
|
+
d.instance_eval do
|
52
|
+
@parts = @parts.dup
|
53
|
+
end
|
54
|
+
d
|
55
|
+
end
|
56
|
+
|
57
|
+
def +(lp_b)
|
58
|
+
raise ArgumentError unless lp_b.kind_of?(Accessory::LensPath)
|
59
|
+
|
60
|
+
d = self.dup
|
61
|
+
for part in lp_b.to_a
|
62
|
+
d.append_accessor!(part)
|
63
|
+
end
|
64
|
+
d.freeze
|
65
|
+
end
|
66
|
+
|
67
|
+
def append_accessor!(part)
|
68
|
+
accessor =
|
69
|
+
case part
|
70
|
+
when Accessory::Accessor
|
71
|
+
part
|
72
|
+
when Array
|
73
|
+
Accessory::SubscriptAccessor.new(part[0], default: part[1])
|
74
|
+
else
|
75
|
+
Accessory::SubscriptAccessor.new(part)
|
76
|
+
end
|
77
|
+
|
78
|
+
unless @parts.empty?
|
79
|
+
@parts.last.make_default_fn = accessor.default_fn_for_previous_step
|
80
|
+
end
|
81
|
+
|
82
|
+
@parts.push(accessor)
|
83
|
+
end
|
84
|
+
|
85
|
+
protected :append_accessor!
|
86
|
+
|
87
|
+
def get_in(doc)
|
88
|
+
if @parts.empty?
|
89
|
+
doc
|
90
|
+
else
|
91
|
+
get_in_step(doc, @parts)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_and_update_in(doc, &mutator_fn)
|
96
|
+
if @parts.empty?
|
97
|
+
doc
|
98
|
+
else
|
99
|
+
get_and_update_in_step(doc, @parts, mutator_fn)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def update_in(data, &new_value_fn)
|
104
|
+
_, new_data = self.get_and_update_in(data){ |v| [nil, new_value_fn.call(v)] }
|
105
|
+
new_data
|
106
|
+
end
|
107
|
+
|
108
|
+
def put_in(data, new_value)
|
109
|
+
_, new_data = self.get_and_update_in(data){ [nil, new_value] }
|
110
|
+
new_data
|
111
|
+
end
|
112
|
+
|
113
|
+
def pop_in(data)
|
114
|
+
self.get_and_update_in(data){ :pop }
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def get_in_step(data, path)
|
119
|
+
step_accessor = path.first
|
120
|
+
rest_of_path = path[1..-1]
|
121
|
+
|
122
|
+
if rest_of_path.empty?
|
123
|
+
step_accessor.get(data)
|
124
|
+
else
|
125
|
+
step_accessor.get(data){ |v| get_in_step(v, rest_of_path) }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
def get_and_update_in_step(data, path, mutator_fn)
|
131
|
+
step_accessor = path.first
|
132
|
+
rest_of_path = path[1..-1]
|
133
|
+
|
134
|
+
if rest_of_path.empty?
|
135
|
+
step_accessor.get_and_update(data, &mutator_fn)
|
136
|
+
else
|
137
|
+
step_accessor.get_and_update(data){ |v| get_and_update_in_step(v, rest_of_path, mutator_fn) }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: accessory
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Levi Aul
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
- levi@leviaul.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/accessory.rb
|
21
|
+
- lib/accessory/access.rb
|
22
|
+
- lib/accessory/accessor.rb
|
23
|
+
- lib/accessory/accessors/all_accessor.rb
|
24
|
+
- lib/accessory/accessors/between_each_accessor.rb
|
25
|
+
- lib/accessory/accessors/betwixt_accessor.rb
|
26
|
+
- lib/accessory/accessors/field_accessor.rb
|
27
|
+
- lib/accessory/accessors/filter_accessor.rb
|
28
|
+
- lib/accessory/accessors/first_accessor.rb
|
29
|
+
- lib/accessory/accessors/instance_variable_accessor.rb
|
30
|
+
- lib/accessory/accessors/last_accessor.rb
|
31
|
+
- lib/accessory/accessors/subscript_accessor.rb
|
32
|
+
- lib/accessory/array_cursor_position.rb
|
33
|
+
- lib/accessory/lens.rb
|
34
|
+
- lib/accessory/lens_path.rb
|
35
|
+
- lib/accessory/version.rb
|
36
|
+
homepage: https://github.com/tsutsu/accessory
|
37
|
+
licenses:
|
38
|
+
- MIT
|
39
|
+
metadata:
|
40
|
+
homepage_uri: https://github.com/tsutsu/accessory
|
41
|
+
source_code_uri: https://github.com/tsutsu/accessory
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 2.7.0
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubygems_version: 3.2.3
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: Functional lenses for Ruby, borrowed from Elixir
|
61
|
+
test_files: []
|