monolens 0.2.0 → 0.3.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/monolens/array/compact.rb +2 -2
  3. data/lib/monolens/array/join.rb +2 -2
  4. data/lib/monolens/array/map.rb +45 -6
  5. data/lib/monolens/array.rb +2 -2
  6. data/lib/monolens/coerce/date.rb +20 -6
  7. data/lib/monolens/coerce/date_time.rb +20 -6
  8. data/lib/monolens/coerce/string.rb +13 -0
  9. data/lib/monolens/coerce.rb +6 -3
  10. data/lib/monolens/core/chain.rb +2 -2
  11. data/lib/monolens/core/mapping.rb +2 -2
  12. data/lib/monolens/error.rb +9 -2
  13. data/lib/monolens/file.rb +2 -7
  14. data/lib/monolens/lens/fetch_support.rb +21 -0
  15. data/lib/monolens/lens/location.rb +17 -0
  16. data/lib/monolens/lens/options.rb +41 -0
  17. data/lib/monolens/lens.rb +39 -23
  18. data/lib/monolens/object/keys.rb +8 -10
  19. data/lib/monolens/object/rename.rb +3 -3
  20. data/lib/monolens/object/select.rb +34 -16
  21. data/lib/monolens/object/transform.rb +34 -12
  22. data/lib/monolens/object/values.rb +34 -10
  23. data/lib/monolens/skip/null.rb +1 -1
  24. data/lib/monolens/str/downcase.rb +2 -2
  25. data/lib/monolens/str/split.rb +2 -2
  26. data/lib/monolens/str/strip.rb +3 -1
  27. data/lib/monolens/str/upcase.rb +2 -2
  28. data/lib/monolens/version.rb +1 -1
  29. data/spec/fixtures/coerce.yml +3 -2
  30. data/spec/fixtures/transform.yml +5 -4
  31. data/spec/monolens/array/test_map.rb +89 -6
  32. data/spec/monolens/coerce/test_date.rb +29 -4
  33. data/spec/monolens/coerce/test_datetime.rb +29 -4
  34. data/spec/monolens/coerce/test_string.rb +15 -0
  35. data/spec/monolens/core/test_mapping.rb +25 -0
  36. data/spec/monolens/lens/test_options.rb +73 -0
  37. data/spec/monolens/object/test_keys.rb +54 -22
  38. data/spec/monolens/object/test_rename.rb +1 -1
  39. data/spec/monolens/object/test_select.rb +109 -4
  40. data/spec/monolens/object/test_transform.rb +93 -6
  41. data/spec/monolens/object/test_values.rb +110 -12
  42. data/spec/monolens/test_error_traceability.rb +60 -0
  43. data/spec/monolens/test_lens.rb +1 -1
  44. data/spec/test_readme.rb +7 -5
  45. metadata +9 -2
@@ -1,15 +1,102 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Monolens, 'object.transform' do
4
- subject do
5
- Monolens.lens('object.transform' => { firstname: 'str.upcase' })
4
+ context 'with default options' do
5
+ subject do
6
+ Monolens.lens('object.transform' => {
7
+ defn: { firstname: 'str.upcase' }
8
+ })
9
+ end
10
+
11
+ it 'works as expected' do
12
+ expect(subject.call(firstname: 'Bernard')).to eql(firstname: 'BERNARD')
13
+ end
14
+
15
+ it 'works as expected on an object with String keys' do
16
+ expect(subject.call('firstname' => 'Bernard')).to eql('firstname' => 'BERNARD')
17
+ end
18
+
19
+ it 'raises an error if input object does not have a key' do
20
+ expect {
21
+ subject.call(lastname: 'Lambeau')
22
+ }.to raise_error(Monolens::LensError, /firstname/)
23
+ end
24
+ end
25
+
26
+ context 'with on_missing: skip' do
27
+ subject do
28
+ Monolens.lens('object.transform' => {
29
+ on_missing: :skip,
30
+ defn: { firstname: 'str.upcase' }
31
+ })
32
+ end
33
+
34
+ it 'works as expected' do
35
+ expect(subject.call(firstname: 'Bernard')).to eql(firstname: 'BERNARD')
36
+ end
37
+
38
+ it 'skpis if missing' do
39
+ expect(subject.call(lastname: 'Lambeau')).to eql(lastname: 'Lambeau')
40
+ end
6
41
  end
7
42
 
8
- it 'works as expected' do
9
- expect(subject.call(firstname: 'Bernard')).to eql(firstname: 'BERNARD')
43
+ context 'with on_missing: null' do
44
+ subject do
45
+ Monolens.lens('object.transform' => {
46
+ on_missing: :null,
47
+ defn: { firstname: 'str.upcase' }
48
+ })
49
+ end
50
+
51
+ it 'works as expected' do
52
+ expect(subject.call(firstname: 'Bernard')).to eql(firstname: 'BERNARD')
53
+ end
54
+
55
+ it 'skpis if missing' do
56
+ expect(subject.call(lastname: 'Lambeau')).to eql(firstname: nil, lastname: 'Lambeau')
57
+ end
10
58
  end
11
59
 
12
- it 'works as expected on an object with String keys' do
13
- expect(subject.call('firstname' => 'Bernard')).to eql('firstname' => 'BERNARD')
60
+ describe 'error traceability' do
61
+ let(:lens) do
62
+ Monolens.lens({
63
+ 'array.map' => {
64
+ :lenses => {
65
+ 'object.transform' => {
66
+ defn: { firstname: 'str.upcase' }
67
+ }
68
+ }
69
+ }
70
+ })
71
+ end
72
+
73
+ subject do
74
+ lens.call(input)
75
+ nil
76
+ rescue Monolens::LensError => ex
77
+ ex
78
+ end
79
+
80
+ context 'when missing key' do
81
+ let(:input) do
82
+ [{}]
83
+ end
84
+
85
+ it 'correctly updates the location' do
86
+ expect(subject.location).to eql([0])
87
+ end
88
+ end
89
+
90
+ context 'when an error down the line' do
91
+ let(:input) do
92
+ [{
93
+ firstname: nil
94
+ }]
95
+ end
96
+
97
+ it 'correctly updates the location' do
98
+ expect(subject.location).to eql([0, :firstname])
99
+ end
100
+ end
14
101
  end
15
102
  end
@@ -1,19 +1,117 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Monolens, 'object.values' do
4
- subject do
5
- Monolens.lens('object.values' => ['str.upcase'])
4
+ context 'with default options' do
5
+ subject do
6
+ Monolens.lens('object.values' => ['str.upcase'])
7
+ end
8
+
9
+ it 'works as expected' do
10
+ input = {
11
+ firstname: 'Bernard',
12
+ lastname: 'Lambeau'
13
+ }
14
+ expected = {
15
+ firstname: 'BERNARD',
16
+ lastname: 'LAMBEAU'
17
+ }
18
+ expect(subject.call(input)).to eql(expected)
19
+ end
20
+
21
+ it 'raises an error on any problem' do
22
+ input = {
23
+ firstname: nil,
24
+ lastname: 'Lambeau'
25
+ }
26
+ expect {
27
+ subject.call(input)
28
+ }.to raise_error(Monolens::LensError)
29
+ end
30
+ end
31
+
32
+ context 'with on_error: skip' do
33
+ subject do
34
+ Monolens.lens('object.values' => {
35
+ 'on_error' => 'skip',
36
+ 'lenses' => ['str.upcase']
37
+ })
38
+ end
39
+
40
+ it 'skips key/value when an error occurs' do
41
+ input = {
42
+ firstname: nil,
43
+ lastname: 'Lambeau'
44
+ }
45
+ expected = {
46
+ lastname: 'LAMBEAU'
47
+ }
48
+ expect(subject.call(input)).to eql(expected)
49
+ end
50
+ end
51
+
52
+ context 'with on_error: null' do
53
+ subject do
54
+ Monolens.lens('object.values' => {
55
+ 'on_error' => 'null',
56
+ 'lenses' => ['str.upcase']
57
+ })
58
+ end
59
+
60
+ it 'uses nil as value' do
61
+ input = {
62
+ firstname: 12,
63
+ lastname: 'Lambeau'
64
+ }
65
+ expected = {
66
+ firstname: nil,
67
+ lastname: 'LAMBEAU'
68
+ }
69
+ expect(subject.call(input)).to eql(expected)
70
+ end
71
+ end
72
+
73
+ context 'with on_error: keep' do
74
+ subject do
75
+ Monolens.lens('object.values' => {
76
+ 'on_error' => 'keep',
77
+ 'lenses' => ['str.upcase']
78
+ })
79
+ end
80
+
81
+ it 'uses nil as value' do
82
+ input = {
83
+ firstname: 12,
84
+ lastname: 'Lambeau'
85
+ }
86
+ expected = {
87
+ firstname: 12,
88
+ lastname: 'LAMBEAU'
89
+ }
90
+ expect(subject.call(input)).to eql(expected)
91
+ end
6
92
  end
7
93
 
8
- it 'works as expected' do
9
- input = {
10
- firstname: 'Bernard',
11
- lastname: 'Lambeau'
12
- }
13
- expected = {
14
- firstname: 'BERNARD',
15
- lastname: 'LAMBEAU'
16
- }
17
- expect(subject.call(input)).to eql(expected)
94
+ describe 'error traceability' do
95
+ let(:lens) do
96
+ Monolens.lens('object.values' => ['str.upcase'])
97
+ end
98
+
99
+ subject do
100
+ lens.call(input)
101
+ nil
102
+ rescue Monolens::LensError => ex
103
+ ex
104
+ end
105
+
106
+ let(:input) do
107
+ {
108
+ 'firstname' => 'Bernard',
109
+ 'lastname' => nil
110
+ }
111
+ end
112
+
113
+ it 'correctly updates the location' do
114
+ expect(subject.location).to eql(['lastname'])
115
+ end
18
116
  end
19
117
  end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'error traceability' do
4
+ context 'on a leaf monolens' do
5
+ let(:lens) do
6
+ Monolens.lens('str.upcase')
7
+ end
8
+
9
+ subject do
10
+ begin
11
+ lens.call(nil)
12
+ rescue => ex
13
+ ex
14
+ end
15
+ end
16
+
17
+ it 'works as expected' do
18
+ expect(subject).to be_a(Monolens::LensError)
19
+ expect(subject.location).to eql([])
20
+ end
21
+ end
22
+
23
+ context 'on array.map' do
24
+ let(:lens) do
25
+ Monolens.lens('array.map' => 'str.upcase')
26
+ end
27
+
28
+ subject do
29
+ begin
30
+ lens.call(['foo', nil])
31
+ rescue => ex
32
+ ex
33
+ end
34
+ end
35
+
36
+ it 'works as expected' do
37
+ expect(subject).to be_a(Monolens::LensError)
38
+ expect(subject.location).to eql([1])
39
+ end
40
+ end
41
+
42
+ context 'on array.map => object.values' do
43
+ let(:lens) do
44
+ Monolens.lens('array.map' => { lenses: { 'object.values' => 'str.upcase' } })
45
+ end
46
+
47
+ subject do
48
+ begin
49
+ lens.call([{ hello: 'foo' }, { hello: nil }])
50
+ rescue Monolens::LensError => ex
51
+ ex
52
+ end
53
+ end
54
+
55
+ it 'works as expected' do
56
+ expect(subject).to be_a(Monolens::LensError)
57
+ expect(subject.location).to eql([1, :hello])
58
+ end
59
+ end
60
+ end
@@ -16,7 +16,7 @@ describe Monolens, '.lens' do
16
16
  it 'preserves options' do
17
17
  got = Monolens.lens(:"coerce.date" => { formats: ['%Y'] })
18
18
  expect(got).to be_a(Monolens::Coerce::Date)
19
- expect(got.options).to eql({ formats: ['%Y'] })
19
+ expect(got.options.to_h).to eql({ formats: ['%Y'] })
20
20
  end
21
21
 
22
22
  it 'allows using an Array, factors a Chain with coercion recursion' do
data/spec/test_readme.rb CHANGED
@@ -23,12 +23,14 @@ describe "What's said in README" do
23
23
  lenses:
24
24
  - array.map:
25
25
  - object.transform:
26
- status:
27
- - str.upcase
28
- body:
29
- - str.strip
26
+ defn:
27
+ status:
28
+ - str.upcase
29
+ body:
30
+ - str.strip
30
31
  - object.rename:
31
- body: description
32
+ defn:
33
+ body: description
32
34
  YML
33
35
  }
34
36
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monolens
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-05 00:00:00.000000000 Z
11
+ date: 2022-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -70,12 +70,16 @@ files:
70
70
  - lib/monolens/coerce.rb
71
71
  - lib/monolens/coerce/date.rb
72
72
  - lib/monolens/coerce/date_time.rb
73
+ - lib/monolens/coerce/string.rb
73
74
  - lib/monolens/core.rb
74
75
  - lib/monolens/core/chain.rb
75
76
  - lib/monolens/core/mapping.rb
76
77
  - lib/monolens/error.rb
77
78
  - lib/monolens/file.rb
78
79
  - lib/monolens/lens.rb
80
+ - lib/monolens/lens/fetch_support.rb
81
+ - lib/monolens/lens/location.rb
82
+ - lib/monolens/lens/options.rb
79
83
  - lib/monolens/object.rb
80
84
  - lib/monolens/object/keys.rb
81
85
  - lib/monolens/object/rename.rb
@@ -98,7 +102,9 @@ files:
98
102
  - spec/monolens/array/test_map.rb
99
103
  - spec/monolens/coerce/test_date.rb
100
104
  - spec/monolens/coerce/test_datetime.rb
105
+ - spec/monolens/coerce/test_string.rb
101
106
  - spec/monolens/core/test_mapping.rb
107
+ - spec/monolens/lens/test_options.rb
102
108
  - spec/monolens/object/test_keys.rb
103
109
  - spec/monolens/object/test_rename.rb
104
110
  - spec/monolens/object/test_select.rb
@@ -109,6 +115,7 @@ files:
109
115
  - spec/monolens/str/test_split.rb
110
116
  - spec/monolens/str/test_strip.rb
111
117
  - spec/monolens/str/test_upcase.rb
118
+ - spec/monolens/test_error_traceability.rb
112
119
  - spec/monolens/test_lens.rb
113
120
  - spec/spec_helper.rb
114
121
  - spec/test_monolens.rb