monolens 0.2.0 → 0.5.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 +4 -4
- data/README.md +6 -0
- data/bin/monolens +11 -0
- data/lib/monolens/array/compact.rb +2 -2
- data/lib/monolens/array/join.rb +2 -2
- data/lib/monolens/array/map.rb +45 -6
- data/lib/monolens/array.rb +2 -2
- data/lib/monolens/coerce/date.rb +22 -6
- data/lib/monolens/coerce/date_time.rb +30 -6
- data/lib/monolens/coerce/integer.rb +15 -0
- data/lib/monolens/coerce/string.rb +13 -0
- data/lib/monolens/coerce.rb +12 -3
- data/lib/monolens/command.rb +96 -0
- data/lib/monolens/core/chain.rb +2 -2
- data/lib/monolens/core/dig.rb +52 -0
- data/lib/monolens/core/mapping.rb +23 -3
- data/lib/monolens/core.rb +6 -0
- data/lib/monolens/error.rb +9 -2
- data/lib/monolens/error_handler.rb +21 -0
- data/lib/monolens/file.rb +2 -7
- data/lib/monolens/lens/fetch_support.rb +19 -0
- data/lib/monolens/lens/location.rb +17 -0
- data/lib/monolens/lens/options.rb +41 -0
- data/lib/monolens/lens.rb +39 -23
- data/lib/monolens/object/extend.rb +53 -0
- data/lib/monolens/object/keys.rb +8 -10
- data/lib/monolens/object/rename.rb +3 -3
- data/lib/monolens/object/select.rb +71 -15
- data/lib/monolens/object/transform.rb +34 -12
- data/lib/monolens/object/values.rb +34 -10
- data/lib/monolens/object.rb +6 -0
- data/lib/monolens/skip/null.rb +1 -1
- data/lib/monolens/str/downcase.rb +2 -2
- data/lib/monolens/str/split.rb +2 -2
- data/lib/monolens/str/strip.rb +3 -1
- data/lib/monolens/str/upcase.rb +2 -2
- data/lib/monolens/version.rb +1 -1
- data/lib/monolens.rb +6 -0
- data/spec/fixtures/coerce.yml +3 -2
- data/spec/fixtures/transform.yml +5 -4
- data/spec/monolens/array/test_map.rb +89 -6
- data/spec/monolens/coerce/test_date.rb +34 -4
- data/spec/monolens/coerce/test_datetime.rb +70 -7
- data/spec/monolens/coerce/test_integer.rb +46 -0
- data/spec/monolens/coerce/test_string.rb +15 -0
- data/spec/monolens/command/map-upcase.lens.yml +5 -0
- data/spec/monolens/command/names-with-null.json +5 -0
- data/spec/monolens/command/names.json +4 -0
- data/spec/monolens/command/robust-map-upcase.lens.yml +7 -0
- data/spec/monolens/core/test_dig.rb +78 -0
- data/spec/monolens/core/test_mapping.rb +53 -11
- data/spec/monolens/lens/test_options.rb +73 -0
- data/spec/monolens/object/test_extend.rb +94 -0
- data/spec/monolens/object/test_keys.rb +54 -22
- data/spec/monolens/object/test_rename.rb +1 -1
- data/spec/monolens/object/test_select.rb +217 -4
- data/spec/monolens/object/test_transform.rb +93 -6
- data/spec/monolens/object/test_values.rb +110 -12
- data/spec/monolens/test_command.rb +128 -0
- data/spec/monolens/test_error_traceability.rb +60 -0
- data/spec/monolens/test_lens.rb +1 -1
- data/spec/test_readme.rb +7 -5
- metadata +37 -2
data/lib/monolens/lens.rb
CHANGED
@@ -1,51 +1,67 @@
|
|
1
|
+
require_relative 'lens/fetch_support'
|
2
|
+
require_relative 'lens/options'
|
3
|
+
require_relative 'lens/location'
|
4
|
+
|
1
5
|
module Monolens
|
2
6
|
module Lens
|
7
|
+
include FetchSupport
|
8
|
+
|
3
9
|
def initialize(options = {})
|
4
|
-
@options =
|
5
|
-
memo[k.to_sym] = v
|
6
|
-
}
|
10
|
+
@options = Options.new(options)
|
7
11
|
end
|
8
12
|
attr_reader :options
|
9
13
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
elsif arg.key?(attr_s = attr.to_s)
|
14
|
-
[ attr_s, arg[attr_s] ]
|
15
|
-
elsif arg.key?(attr_sym = attr.to_sym)
|
16
|
-
[ attr_sym, arg[attr_sym] ]
|
17
|
-
else
|
18
|
-
[ attr, nil ]
|
19
|
-
end
|
14
|
+
def fail!(msg, world)
|
15
|
+
location = world[:location]&.to_a || []
|
16
|
+
raise Monolens::LensError.new(msg, location)
|
20
17
|
end
|
21
18
|
|
19
|
+
protected
|
20
|
+
|
22
21
|
def option(name, default = nil)
|
23
|
-
|
24
|
-
|
22
|
+
@options.fetch(name, default)
|
23
|
+
end
|
24
|
+
|
25
|
+
def deeper(world, part)
|
26
|
+
world[:location] ||= Location.new
|
27
|
+
world[:location].deeper(part) do |loc|
|
28
|
+
yield(world.merge(location: loc))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
MISSING_ERROR_HANDLER = <<~TXT
|
33
|
+
An :error_handler world entry is required to use error handling
|
34
|
+
TXT
|
35
|
+
|
36
|
+
def error_handler!(world)
|
37
|
+
_, handler = fetch_on(:error_handler, world)
|
38
|
+
return handler if handler
|
39
|
+
|
40
|
+
raise Monolens::Error, MISSING_ERROR_HANDLER
|
25
41
|
end
|
26
42
|
|
27
|
-
def is_string!(arg)
|
43
|
+
def is_string!(arg, world)
|
28
44
|
return if arg.is_a?(::String)
|
29
45
|
|
30
|
-
|
46
|
+
fail!("String expected, got #{arg.class}", world)
|
31
47
|
end
|
32
48
|
|
33
|
-
def is_hash!(arg)
|
49
|
+
def is_hash!(arg, world)
|
34
50
|
return if arg.is_a?(::Hash)
|
35
51
|
|
36
|
-
|
52
|
+
fail!("Hash expected, got #{arg.class}", world)
|
37
53
|
end
|
38
54
|
|
39
|
-
def is_enumerable!(arg)
|
55
|
+
def is_enumerable!(arg, world)
|
40
56
|
return if arg.is_a?(::Enumerable)
|
41
57
|
|
42
|
-
|
58
|
+
fail!("Enumerable expected, got #{arg.class}", world)
|
43
59
|
end
|
44
60
|
|
45
|
-
def is_array!(arg)
|
61
|
+
def is_array!(arg, world)
|
46
62
|
return if arg.is_a?(::Array)
|
47
63
|
|
48
|
-
|
64
|
+
fail!("Array expected, got #{arg.class}", world)
|
49
65
|
end
|
50
66
|
end
|
51
67
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Monolens
|
2
|
+
module Object
|
3
|
+
class Extend
|
4
|
+
include Lens
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
super(options)
|
8
|
+
ts = option(:defn, {})
|
9
|
+
ts.each_pair do |k,v|
|
10
|
+
ts[k] = Monolens.lens(v)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(arg, world = {})
|
15
|
+
is_hash!(arg, world)
|
16
|
+
|
17
|
+
result = arg.dup
|
18
|
+
is_symbol = arg.keys.any?{|k| k.is_a?(Symbol) }
|
19
|
+
option(:defn, {}).each_pair do |attr, lens|
|
20
|
+
deeper(world, attr) do |w|
|
21
|
+
actual = is_symbol ? attr.to_sym : attr.to_s
|
22
|
+
begin
|
23
|
+
result[actual] = lens.call(arg, w)
|
24
|
+
rescue Monolens::LensError => ex
|
25
|
+
strategy = option(:on_error, :fail)
|
26
|
+
handle_error(strategy, ex, result, actual, world)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_error(strategy, ex, result, attr, world)
|
34
|
+
strategy = strategy.to_sym unless strategy.is_a?(::Array)
|
35
|
+
case strategy
|
36
|
+
when ::Array
|
37
|
+
strategy.each{|s| handle_error(s, ex, result, attr, world) }
|
38
|
+
when :handler
|
39
|
+
error_handler!(world).call(ex)
|
40
|
+
when :fail
|
41
|
+
raise
|
42
|
+
when :null
|
43
|
+
result[attr] = nil
|
44
|
+
when :skip
|
45
|
+
nil
|
46
|
+
else
|
47
|
+
raise Monolens::Error, "Unexpected error strategy `#{strategy}`"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
private :handle_error
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/monolens/object/keys.rb
CHANGED
@@ -3,19 +3,17 @@ module Monolens
|
|
3
3
|
class Keys
|
4
4
|
include Lens
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
@lens = Monolens.lens(lens)
|
9
|
-
end
|
10
|
-
|
11
|
-
def call(arg, *rest)
|
12
|
-
is_hash!(arg)
|
6
|
+
def call(arg, world = {})
|
7
|
+
is_hash!(arg, world)
|
13
8
|
|
9
|
+
lenses = option(:lenses)
|
14
10
|
dup = {}
|
15
11
|
arg.each_pair do |attr, value|
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
deeper(world, attr) do |w|
|
13
|
+
lensed = lenses.call(attr, w)
|
14
|
+
lensed = lensed.to_sym if lensed && attr.is_a?(Symbol)
|
15
|
+
dup[lensed] = value
|
16
|
+
end
|
19
17
|
end
|
20
18
|
dup
|
21
19
|
end
|
@@ -3,11 +3,11 @@ module Monolens
|
|
3
3
|
class Rename
|
4
4
|
include Lens
|
5
5
|
|
6
|
-
def call(arg,
|
7
|
-
is_hash!(arg)
|
6
|
+
def call(arg, world = {})
|
7
|
+
is_hash!(arg, world)
|
8
8
|
|
9
9
|
dup = arg.dup
|
10
|
-
|
10
|
+
option(:defn).each_pair do |oldname, newname|
|
11
11
|
actual_name, value = fetch_on(oldname, arg)
|
12
12
|
newname = actual_name.is_a?(Symbol) ? newname.to_sym : newname.to_s
|
13
13
|
dup.delete(actual_name)
|
@@ -3,28 +3,84 @@ module Monolens
|
|
3
3
|
class Select
|
4
4
|
include Lens
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
|
6
|
+
def call(arg, world = {})
|
7
|
+
is_hash!(arg, world)
|
8
|
+
|
9
|
+
result = {}
|
10
|
+
is_symbol = arg.keys.any?{|k| k.is_a?(Symbol) }
|
11
|
+
defn.each_pair do |new_attr, selector|
|
12
|
+
new_attr = is_symbol ? new_attr.to_sym : new_attr.to_s
|
13
|
+
|
14
|
+
deeper(world, new_attr) do |w|
|
15
|
+
catch (:skip) do
|
16
|
+
value = do_select(arg, selector, w)
|
17
|
+
result[new_attr] = value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
result
|
9
22
|
end
|
10
23
|
|
11
|
-
|
12
|
-
is_hash!(arg)
|
24
|
+
private
|
13
25
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
26
|
+
def do_select(arg, selector, world)
|
27
|
+
if selector.is_a?(::Array)
|
28
|
+
do_array_select(arg, selector, world)
|
29
|
+
else
|
30
|
+
do_single_select(arg, selector, world)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def do_array_select(arg, selector, world)
|
35
|
+
case option(:strategy, :all).to_sym
|
36
|
+
when :all
|
37
|
+
selector.each_with_object([]) do |old_attr, values|
|
38
|
+
catch (:skip) do
|
39
|
+
values << do_single_select(arg, old_attr, world)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
when :first
|
43
|
+
selector.each do |old_attr|
|
19
44
|
actual, fetched = fetch_on(old_attr, arg)
|
20
|
-
|
21
|
-
fetched
|
45
|
+
return fetched if actual
|
22
46
|
end
|
23
|
-
|
24
|
-
|
47
|
+
on_missing(selector.first, [], world).first
|
48
|
+
else
|
49
|
+
raise Monolens::Error, "Unexpected strategy `#{strategy}`"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def do_single_select(arg, selector, world)
|
54
|
+
actual, fetched = fetch_on(selector, arg)
|
55
|
+
if actual.nil?
|
56
|
+
on_missing(selector, [], world).first
|
57
|
+
else
|
58
|
+
fetched
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def defn
|
63
|
+
defn = option(:defn, {})
|
64
|
+
defn = defn.each_with_object({}) do |attr, memo|
|
65
|
+
memo[attr] = attr
|
66
|
+
end if defn.is_a?(::Array)
|
67
|
+
defn
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_missing(attr, values, world)
|
71
|
+
strategy = option(:on_missing, :fail)
|
72
|
+
case strategy.to_sym
|
73
|
+
when :fail
|
74
|
+
fail!("Expected `#{attr}` to be defined", world)
|
75
|
+
when :null
|
76
|
+
values << nil
|
77
|
+
when :skip
|
78
|
+
throw :skip
|
79
|
+
else
|
80
|
+
raise Monolens::Error, "Unexpected on_missing strategy `#{strategy}`"
|
25
81
|
end
|
26
|
-
result
|
27
82
|
end
|
83
|
+
private :on_missing
|
28
84
|
end
|
29
85
|
end
|
30
86
|
end
|
@@ -3,23 +3,45 @@ module Monolens
|
|
3
3
|
class Transform
|
4
4
|
include Lens
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
super(
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
def initialize(options)
|
7
|
+
super(options)
|
8
|
+
ts = option(:defn, {})
|
9
|
+
ts.each_pair do |k,v|
|
10
|
+
ts[k] = Monolens.lens(v)
|
11
|
+
end
|
11
12
|
end
|
12
13
|
|
13
|
-
def call(arg,
|
14
|
-
is_hash!(arg)
|
14
|
+
def call(arg, world = {})
|
15
|
+
is_hash!(arg, world)
|
16
|
+
|
17
|
+
result = arg.dup
|
18
|
+
option(:defn, {}).each_pair do |attr, sub_lens|
|
19
|
+
deeper(world, attr) do |w|
|
20
|
+
actual_attr, fetched = fetch_on(attr, arg)
|
21
|
+
if actual_attr
|
22
|
+
result[actual_attr] = sub_lens.call(fetched, w)
|
23
|
+
else
|
24
|
+
on_missing(result, attr, world)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
result
|
29
|
+
end
|
15
30
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
31
|
+
def on_missing(result, attr, world)
|
32
|
+
strategy = option(:on_missing, :fail)
|
33
|
+
case strategy&.to_sym
|
34
|
+
when :fail
|
35
|
+
fail!("Expected `#{attr}` to be defined", world)
|
36
|
+
when :null
|
37
|
+
result[attr] = nil
|
38
|
+
when :skip
|
39
|
+
nil
|
40
|
+
else
|
41
|
+
raise Monolens::Error, "Unexpected missing strategy `#{strategy}`"
|
20
42
|
end
|
21
|
-
dup
|
22
43
|
end
|
44
|
+
private :on_missing
|
23
45
|
end
|
24
46
|
end
|
25
47
|
end
|
@@ -3,20 +3,44 @@ module Monolens
|
|
3
3
|
class Values
|
4
4
|
include Lens
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
@lens = Monolens.lens(lens)
|
9
|
-
end
|
10
|
-
|
11
|
-
def call(arg, *rest)
|
12
|
-
is_hash!(arg)
|
6
|
+
def call(arg, world = {})
|
7
|
+
is_hash!(arg, world)
|
13
8
|
|
14
|
-
|
9
|
+
lenses = option(:lenses)
|
10
|
+
result = arg.dup
|
15
11
|
arg.each_pair do |attr, value|
|
16
|
-
|
12
|
+
deeper(world, attr) do |w|
|
13
|
+
begin
|
14
|
+
result[attr] = lenses.call(value, w)
|
15
|
+
rescue Monolens::LensError => ex
|
16
|
+
strategy = option(:on_error, :fail)
|
17
|
+
handle_error(strategy, ex, result, attr, value, world)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle_error(strategy, ex, result, attr, value, world)
|
25
|
+
strategy = strategy.to_sym unless strategy.is_a?(::Array)
|
26
|
+
case strategy
|
27
|
+
when ::Array
|
28
|
+
strategy.each{|s| handle_error(s, ex, result, attr, value, world) }
|
29
|
+
when :handler
|
30
|
+
error_handler!(world).call(ex)
|
31
|
+
when :fail
|
32
|
+
raise
|
33
|
+
when :null
|
34
|
+
result[attr] = nil
|
35
|
+
when :skip
|
36
|
+
result.delete(attr)
|
37
|
+
when :keep
|
38
|
+
result[attr] = value
|
39
|
+
else
|
40
|
+
raise Monolens::Error, "Unexpected error strategy `#{strategy}`"
|
17
41
|
end
|
18
|
-
dup
|
19
42
|
end
|
43
|
+
private :handle_error
|
20
44
|
end
|
21
45
|
end
|
22
46
|
end
|
data/lib/monolens/object.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
module Monolens
|
2
2
|
module Object
|
3
|
+
def extend(options)
|
4
|
+
Extend.new(options)
|
5
|
+
end
|
6
|
+
module_function :extend
|
7
|
+
|
3
8
|
def rename(parts)
|
4
9
|
Rename.new(parts)
|
5
10
|
end
|
@@ -33,3 +38,4 @@ require_relative 'object/transform'
|
|
33
38
|
require_relative 'object/keys'
|
34
39
|
require_relative 'object/values'
|
35
40
|
require_relative 'object/select'
|
41
|
+
require_relative 'object/extend'
|
data/lib/monolens/skip/null.rb
CHANGED
data/lib/monolens/str/split.rb
CHANGED
data/lib/monolens/str/strip.rb
CHANGED
data/lib/monolens/str/upcase.rb
CHANGED
data/lib/monolens/version.rb
CHANGED
data/lib/monolens.rb
CHANGED
@@ -5,6 +5,7 @@ module Monolens
|
|
5
5
|
class << self
|
6
6
|
require_relative 'monolens/version'
|
7
7
|
require_relative 'monolens/error'
|
8
|
+
require_relative 'monolens/error_handler'
|
8
9
|
require_relative 'monolens/lens'
|
9
10
|
|
10
11
|
def define_namespace(name, impl_module)
|
@@ -43,6 +44,10 @@ module Monolens
|
|
43
44
|
end
|
44
45
|
private :chain
|
45
46
|
|
47
|
+
def file_lens(arg)
|
48
|
+
File.new(arg)
|
49
|
+
end
|
50
|
+
|
46
51
|
def leaf_lens(arg)
|
47
52
|
namespace_name, lens_name = arg.to_s.split('.')
|
48
53
|
factor_lens(namespace_name, lens_name, {})
|
@@ -50,6 +55,7 @@ module Monolens
|
|
50
55
|
private :leaf_lens
|
51
56
|
|
52
57
|
def hash_lens(arg)
|
58
|
+
return file_lens(arg) if arg['version'] || arg[:version]
|
53
59
|
raise "Invalid lens #{arg}" unless arg.size == 1
|
54
60
|
|
55
61
|
name, options = arg.to_a.first
|
data/spec/fixtures/coerce.yml
CHANGED
data/spec/fixtures/transform.yml
CHANGED
@@ -1,13 +1,96 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Monolens, 'array.map' do
|
4
|
-
|
5
|
-
|
4
|
+
context 'without options' do
|
5
|
+
subject do
|
6
|
+
Monolens.lens('array.map' => 'str.upcase')
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'joins values with spaces' do
|
10
|
+
input = ['hello', 'world']
|
11
|
+
expected = ['HELLO', 'WORLD']
|
12
|
+
expect(subject.call(input)).to eql(expected)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'default on error' do
|
17
|
+
subject do
|
18
|
+
Monolens.lens('array.map' => {
|
19
|
+
lenses: [ 'str.upcase' ]
|
20
|
+
})
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raise errors' do
|
24
|
+
input = [nil, 'world']
|
25
|
+
expect {
|
26
|
+
subject.call(input)
|
27
|
+
}.to raise_error(Monolens::LensError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'skipping on error' do
|
32
|
+
subject do
|
33
|
+
Monolens.lens('array.map' => {
|
34
|
+
on_error: 'skip',
|
35
|
+
lenses: [ 'str.upcase' ]
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'skips errors' do
|
40
|
+
input = [nil, 'world']
|
41
|
+
expected = ['WORLD']
|
42
|
+
expect(subject.call(input)).to eql(expected)
|
43
|
+
end
|
6
44
|
end
|
7
45
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
46
|
+
context 'nulling on error' do
|
47
|
+
subject do
|
48
|
+
Monolens.lens('array.map' => {
|
49
|
+
on_error: 'null',
|
50
|
+
lenses: [ 'str.upcase' ]
|
51
|
+
})
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'skips errors' do
|
55
|
+
input = [nil, 'world']
|
56
|
+
expected = [nil, 'WORLD']
|
57
|
+
expect(subject.call(input)).to eql(expected)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'on error with :handler' do
|
62
|
+
subject do
|
63
|
+
Monolens.lens('array.map' => {
|
64
|
+
on_error: 'handler',
|
65
|
+
lenses: [ 'str.upcase' ]
|
66
|
+
})
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'collects the error then skips' do
|
70
|
+
input = [nil, 'world']
|
71
|
+
expected = ['WORLD']
|
72
|
+
errs = []
|
73
|
+
got = subject.call(input, error_handler: ->(err){ errs << err })
|
74
|
+
expect(errs.size).to eql(1)
|
75
|
+
expect(got).to eql(expected)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'collecting on error then nulling' do
|
80
|
+
subject do
|
81
|
+
Monolens.lens('array.map' => {
|
82
|
+
on_error: ['handler', 'null'],
|
83
|
+
lenses: [ 'str.upcase' ]
|
84
|
+
})
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'uses the handler' do
|
88
|
+
input = [nil, 'world']
|
89
|
+
expected = [nil, 'WORLD']
|
90
|
+
errs = []
|
91
|
+
got = subject.call(input, error_handler: ->(err){ errs << err })
|
92
|
+
expect(errs.size).to eql(1)
|
93
|
+
expect(got).to eql(expected)
|
94
|
+
end
|
12
95
|
end
|
13
96
|
end
|