monolens 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 30152f626d5675640e96816c7036d5a6dbac8a25
4
- data.tar.gz: a26d4e5d0715bd511aea96da758fd12d36e2a70b
3
+ metadata.gz: b2a9267753f8725ed91fc71b3c9b995a11d3b0c1
4
+ data.tar.gz: 4aaaf0247bcb4f2195052a416ebe7742bfd2b72a
5
5
  SHA512:
6
- metadata.gz: 7b27ab005aaf2a6bf1abdc8c9284072ce1307498b7edad606526198c3e4ae003d0ddda72eb532266b7b737fbb04146959903868c50e16be4fbb8f1a073f20e9b
7
- data.tar.gz: 8fd2b3ad776ff72da243ba29ab7fc2bd5fd05e88b8d34c93cd38c362142b83ff4c8164559af707d3ea314c39af3b9c415c1716841c269ab73bf9e06b852c883e
6
+ metadata.gz: 99df61206eb26141c13a7a364fc9f5af97ade23d9a50e92e82cd8b2980cb88790e36402a852116d800a2caebe71af7b1bb90e4ad3dc8fdd450360d1ce63fe5e6
7
+ data.tar.gz: a93491cec013f9993299ee227c2055c0c93a9f224c5da6a2526afbb95fe2516bf1b29250670c769667cd93bb018e613e3ae21b98d545a62109602e2fa2c1f843
@@ -47,10 +47,19 @@ module Monolens
47
47
  end
48
48
 
49
49
  def read_file(file)
50
- content = ::File.read(file)
51
50
  case ::File.extname(file)
52
- when /json$/ then JSON.parse(content)
53
- when /ya?ml$/ then YAML.safe_load(content)
51
+ when /json$/
52
+ content = ::File.read(file)
53
+ JSON.parse(content)
54
+ when /ya?ml$/
55
+ content = ::File.read(file)
56
+ YAML.safe_load(content)
57
+ when /csv$/
58
+ require 'bmg'
59
+ Bmg.csv(file).to_a
60
+ when /xlsx?$/
61
+ require 'bmg'
62
+ Bmg.excel(file).to_a
54
63
  else
55
64
  fail!("Unable to use #{file}")
56
65
  end
@@ -5,11 +5,31 @@ module Monolens
5
5
 
6
6
  def call(arg, world = {})
7
7
  option(:values, {}).fetch(arg) do
8
- fail!("Unrecognized value `#{arg}`", world) if option(:fail_if_missing)
8
+ on_missing(arg, world)
9
+ end
10
+ end
11
+
12
+ private
9
13
 
10
- option(:default)
14
+ def on_missing(arg, world)
15
+ strategy = option(:on_missing, :fail)
16
+ case strategy.to_sym
17
+ when :fail
18
+ fail!("Unrecognized value `#{arg}`", world)
19
+ when :default
20
+ option(:default, nil)
21
+ when :null
22
+ nil
23
+ when :fallback
24
+ missing_fallback = ->(arg, world) do
25
+ raise Monolens::Error, "Unexpected missing fallback handler"
26
+ end
27
+ option(:fallback, missing_fallback).call(self, arg, world)
28
+ else
29
+ raise Monolens::Error, "Unexpected missing strategy `#{strategy}`"
11
30
  end
12
31
  end
32
+ private :on_missing
13
33
  end
14
34
  end
15
35
  end
data/lib/monolens/lens.rb CHANGED
@@ -11,6 +11,11 @@ module Monolens
11
11
  end
12
12
  attr_reader :options
13
13
 
14
+ def fail!(msg, world)
15
+ location = world[:location]&.to_a || []
16
+ raise Monolens::LensError.new(msg, location)
17
+ end
18
+
14
19
  protected
15
20
 
16
21
  def option(name, default = nil)
@@ -58,10 +63,5 @@ module Monolens
58
63
 
59
64
  fail!("Array expected, got #{arg.class}", world)
60
65
  end
61
-
62
- def fail!(msg, world)
63
- location = world[:location]&.to_a || []
64
- raise Monolens::LensError.new(msg, location)
65
- end
66
66
  end
67
67
  end
@@ -9,20 +9,12 @@ module Monolens
9
9
  result = {}
10
10
  is_symbol = arg.keys.any?{|k| k.is_a?(Symbol) }
11
11
  defn.each_pair do |new_attr, selector|
12
+ new_attr = is_symbol ? new_attr.to_sym : new_attr.to_s
13
+
12
14
  deeper(world, new_attr) do |w|
13
- is_array = selector.is_a?(::Array)
14
- values = []
15
- Array(selector).each do |old_attr|
16
- actual, fetched = fetch_on(old_attr, arg)
17
- if actual.nil?
18
- on_missing(old_attr, values, w)
19
- else
20
- values << fetched
21
- end
22
- end
23
- new_attr = is_symbol ? new_attr.to_sym : new_attr.to_s
24
- unless values.empty?
25
- result[new_attr] = is_array ? values : values.first
15
+ catch (:skip) do
16
+ value = do_select(arg, selector, w)
17
+ result[new_attr] = value
26
18
  end
27
19
  end
28
20
  end
@@ -31,6 +23,42 @@ module Monolens
31
23
 
32
24
  private
33
25
 
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|
44
+ actual, fetched = fetch_on(old_attr, arg)
45
+ return fetched if actual
46
+ end
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
+
34
62
  def defn
35
63
  defn = option(:defn, {})
36
64
  defn = defn.each_with_object({}) do |attr, memo|
@@ -47,9 +75,9 @@ module Monolens
47
75
  when :null
48
76
  values << nil
49
77
  when :skip
50
- nil
78
+ throw :skip
51
79
  else
52
- raise Monolens::Error, "Unexpected missing strategy `#{strategy}`"
80
+ raise Monolens::Error, "Unexpected on_missing strategy `#{strategy}`"
53
81
  end
54
82
  end
55
83
  private :on_missing
@@ -30,7 +30,7 @@ module Monolens
30
30
 
31
31
  def on_missing(result, attr, world)
32
32
  strategy = option(:on_missing, :fail)
33
- case strategy.to_sym
33
+ case strategy&.to_sym
34
34
  when :fail
35
35
  fail!("Expected `#{attr}` to be defined", world)
36
36
  when :null
@@ -1,7 +1,7 @@
1
1
  module Monolens
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 4
4
+ MINOR = 5
5
5
  TINY = 0
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Monolens, 'core.mapping' do
4
4
  let(:mapping) do
5
- { 'values' => { 'todo' => 'open' }}
5
+ { 'values' => { 'todo' => 'open' } }
6
6
  end
7
7
 
8
8
  context 'with default options' do
@@ -14,14 +14,16 @@ describe Monolens, 'core.mapping' do
14
14
  expect(subject.call('todo')).to eql('open')
15
15
  end
16
16
 
17
- it 'returns nil if not found' do
18
- expect(subject.call('nosuchone')).to eql(nil)
17
+ it 'raises if not found' do
18
+ expect {
19
+ subject.call('nosuchone')
20
+ }.to raise_error(Monolens::LensError)
19
21
  end
20
22
  end
21
23
 
22
- context 'specifying a default value' do
24
+ context 'on_missing: default' do
23
25
  subject do
24
- Monolens.lens('core.mapping' => mapping.merge('default' => 'foo'))
26
+ Monolens.lens('core.mapping' => mapping.merge('on_missing' => 'default', 'default' => 'foo'))
25
27
  end
26
28
 
27
29
  it 'replaces the value by its mapped' do
@@ -33,19 +35,34 @@ describe Monolens, 'core.mapping' do
33
35
  end
34
36
  end
35
37
 
36
- context 'lets raise if not found' do
38
+ context 'on_missing: null' do
37
39
  subject do
38
- Monolens.lens('core.mapping' => mapping.merge('fail_if_missing' => true))
40
+ Monolens.lens('core.mapping' => mapping.merge('on_missing' => 'null'))
39
41
  end
40
42
 
41
43
  it 'replaces the value by its mapped' do
42
44
  expect(subject.call('todo')).to eql('open')
43
45
  end
44
46
 
45
- it 'raises if not found' do
46
- expect {
47
- subject.call('nosuchone')
48
- }.to raise_error(Monolens::LensError)
47
+ it 'returns nil if missing' do
48
+ expect(subject.call('nosuchone')).to eql(nil)
49
+ end
50
+ end
51
+
52
+ context 'on_missing: fallback' do
53
+ subject do
54
+ Monolens.lens('core.mapping' => mapping.merge(
55
+ 'on_missing' => 'fallback',
56
+ 'fallback' => ->(lens, arg, world) { 'foo' }
57
+ ))
58
+ end
59
+
60
+ it 'replaces the value by its mapped' do
61
+ expect(subject.call('todo')).to eql('open')
62
+ end
63
+
64
+ it 'returns nil if missing' do
65
+ expect(subject.call('nosuchone')).to eql('foo')
49
66
  end
50
67
  end
51
68
 
@@ -54,7 +71,7 @@ describe Monolens, 'core.mapping' do
54
71
  Monolens.lens({
55
72
  'array.map' => {
56
73
  :lenses => {
57
- 'core.mapping' => mapping.merge('fail_if_missing' => true)
74
+ 'core.mapping' => mapping.merge('on_missing' => 'fail')
58
75
  }
59
76
  }
60
77
  })
@@ -75,6 +75,91 @@ describe Monolens, 'object.select' do
75
75
  end
76
76
  end
77
77
 
78
+ context 'when using strategy: first' do
79
+ subject do
80
+ Monolens.lens('object.select' => {
81
+ defn: {
82
+ name: [:firstname, :lastname],
83
+ status: :priority
84
+ },
85
+ strategy: 'first',
86
+ on_missing: on_missing
87
+ }.compact)
88
+ end
89
+
90
+ context 'without on_missing' do
91
+ let(:on_missing) do
92
+ nil
93
+ end
94
+
95
+ it 'works as expected when first option is present' do
96
+ input = {
97
+ firstname: 'Bernard',
98
+ priority: 12
99
+ }
100
+ expected = {
101
+ name: 'Bernard',
102
+ status: 12
103
+ }
104
+ expect(subject.call(input)).to eql(expected)
105
+ end
106
+
107
+ it 'works as expected when second is present' do
108
+ input = {
109
+ lastname: 'Lambeau',
110
+ priority: 12
111
+ }
112
+ expected = {
113
+ name: 'Lambeau',
114
+ status: 12
115
+ }
116
+ expect(subject.call(input)).to eql(expected)
117
+ end
118
+
119
+ it 'fails when none is present' do
120
+ input = {
121
+ priority: 12
122
+ }
123
+ expect {
124
+ subject.call(input)
125
+ }.to raise_error(Monolens::Error)
126
+ end
127
+ end
128
+
129
+ context 'with on_missing: skip' do
130
+ let(:on_missing) do
131
+ :skip
132
+ end
133
+
134
+ it 'works as expected when missing' do
135
+ input = {
136
+ priority: 12
137
+ }
138
+ expected = {
139
+ status: 12
140
+ }
141
+ expect(subject.call(input)).to eql(expected)
142
+ end
143
+ end
144
+
145
+ context 'with on_missing: null' do
146
+ let(:on_missing) do
147
+ :null
148
+ end
149
+
150
+ it 'works as expected when missing' do
151
+ input = {
152
+ priority: 12
153
+ }
154
+ expected = {
155
+ name: nil,
156
+ status: 12
157
+ }
158
+ expect(subject.call(input)).to eql(expected)
159
+ end
160
+ end
161
+ end
162
+
78
163
  context 'when using an array as selection' do
79
164
  subject do
80
165
  Monolens.lens('object.select' => {
@@ -186,7 +271,6 @@ describe Monolens, 'object.select' do
186
271
 
187
272
  subject do
188
273
  lens.call(input)
189
- nil
190
274
  rescue Monolens::LensError => ex
191
275
  ex
192
276
  end
@@ -196,7 +280,7 @@ describe Monolens, 'object.select' do
196
280
  end
197
281
 
198
282
  it 'correctly updates the location' do
199
- expect(subject.location).to eql([:status])
283
+ expect(subject.location).to eql(['status'])
200
284
  end
201
285
  end
202
286
  end
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.4.0
4
+ version: 0.5.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-12 00:00:00.000000000 Z
11
+ date: 2022-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bmg
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: Data transformations inspired by Cambria lenses
56
70
  email: blambeau@gmail.com
57
71
  executables: []