monolens 0.6.2 → 0.6.4

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
  SHA256:
3
- metadata.gz: e0de27df8fbe83531b35a1b3c4b92d94669108ef10f17df849d9127a07698ad2
4
- data.tar.gz: abf82eebdd9511578b0cd73524430335f9b9c8ecb60fe74598eec3e6c0d5f5e5
3
+ metadata.gz: 808d9126b39444b34dde2d81926a680af37ad5351994e6b640a035b3555807fb
4
+ data.tar.gz: f2eab3c0f2911e83096e7a72520d9812575830c38192ad98b0f609b68f6ab814
5
5
  SHA512:
6
- metadata.gz: 3909ab4f03d872d0083884ddea46e3d2546caeef268c83e1145204789050c89e34cd219161a672e07a5b34727c40f21a23be501b85ea7aaed2dd9c1eb54abf66
7
- data.tar.gz: 394d89daddae630912035d43eefc6ab1a591a45eee3b305b203983cc40c673ad83feb65a3c9510d9a6534311f5c70af7726c83356a64cc6c35a4d2e6949512ee
6
+ metadata.gz: 50586006606bd32d8f1cf2d35c885c906883d61da10a7746f63ca7cf1e66e6fd81624025def49287400edee1554e86c69e7f27573bc8ad9311872be8a545270a
7
+ data.tar.gz: acdb391077a3c35b1888fb2cb6f8bbddf8be6d5ba68001f6297a0686079fc5b1aa13077eca3079fa8d98c4d56d804da43ca0ccb5c912b579117f815d312a673f
@@ -0,0 +1,13 @@
1
+ module Monolens
2
+ module Coerce
3
+ class Array
4
+ include Lens
5
+
6
+ signature(Type::Any, Type::Array)
7
+
8
+ def call(arg, world = {})
9
+ Array(arg)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -22,6 +22,11 @@ module Monolens
22
22
  end
23
23
  module_function :string
24
24
 
25
+ def array(options, registry)
26
+ Array.new(options, registry)
27
+ end
28
+ module_function :array
29
+
25
30
  Monolens.define_namespace 'coerce', self
26
31
  end
27
32
  end
@@ -29,3 +34,4 @@ require_relative 'coerce/date'
29
34
  require_relative 'coerce/date_time'
30
35
  require_relative 'coerce/integer'
31
36
  require_relative 'coerce/string'
37
+ require_relative 'coerce/array'
@@ -0,0 +1,63 @@
1
+ module Monolens
2
+ module Core
3
+ class Digs
4
+ include Lens
5
+
6
+ signature(Type::Diggable, Type::Any, {
7
+ defn: [
8
+ Type::Array.of(Type::Array.of(Type::Any.of(Type::Integer, Type::String))),
9
+ true
10
+ ],
11
+ on_missing: [Type::Strategy.missing(%w{fail null}), false]
12
+ })
13
+
14
+ def call(arg, world = {})
15
+ to_dig = option(:defn, [])
16
+ to_dig.map {|digging|
17
+ digging.inject(arg) do |memo, part|
18
+ dig_on(part, memo, world)
19
+ end
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ def path
26
+ option(:defn, []).join('.')
27
+ end
28
+
29
+ def dig_on(attr, arg, world)
30
+ if arg.is_a?(::Array)
31
+ index = attr.to_i
32
+ on_missing(world) if index >= arg.size
33
+ arg[index]
34
+ elsif arg.is_a?(::Hash)
35
+ actual, value = fetch_on(attr, arg)
36
+ on_missing(world) unless actual
37
+ value
38
+ elsif arg
39
+ if attr.is_a?(::Integer)
40
+ is_array!(arg, world)
41
+ else
42
+ is_hash!(arg, world)
43
+ end
44
+ else
45
+ on_missing(world)
46
+ end
47
+ end
48
+
49
+ def on_missing(world)
50
+ strategy = option(:on_missing, :fail)
51
+ case strategy.to_sym
52
+ when :fail
53
+ fail!("Unable to find #{path}", world)
54
+ when :null
55
+ nil
56
+ else
57
+ raise Monolens::Error, "Unexpected missing strategy `#{strategy}`"
58
+ end
59
+ end
60
+ private :on_missing
61
+ end
62
+ end
63
+ end
@@ -12,6 +12,11 @@ module Monolens
12
12
  end
13
13
  module_function :dig
14
14
 
15
+ def digs(options, registry)
16
+ Digs.new(options, registry)
17
+ end
18
+ module_function :digs
19
+
15
20
  def literal(options, registry)
16
21
  Literal.new(options, registry)
17
22
  end
@@ -27,5 +32,6 @@ module Monolens
27
32
  end
28
33
  require_relative 'core/chain'
29
34
  require_relative 'core/dig'
35
+ require_relative 'core/digs'
30
36
  require_relative 'core/mapping'
31
37
  require_relative 'core/literal'
@@ -4,7 +4,8 @@ module Monolens
4
4
  include Lens
5
5
 
6
6
  signature(Type::Object, Type::Object, {
7
- strategy: [Type::Strategy.selection(%w{all first}), false],
7
+ strategy: [Type::Strategy.selection(%w{all first concat}), false],
8
+ separator: [Type::String, false],
8
9
  defn: [Type::Any.of(
9
10
  Type::Array.of(Type::Name),
10
11
  Type::Map.of(Type::Name, Type::Any.of(Type::Array.of(Type::Name), Type::Name))
@@ -41,13 +42,20 @@ module Monolens
41
42
  end
42
43
 
43
44
  def do_array_select(arg, selector, world)
44
- case option(:strategy, :all).to_sym
45
+ case strategy = option(:strategy, :all).to_sym
45
46
  when :all
46
47
  selector.each_with_object([]) do |old_attr, values|
47
48
  catch (:skip) do
48
49
  values << do_single_select(arg, old_attr, world)
49
50
  end
50
51
  end
52
+ when :concat
53
+ values = selector.each_with_object([]) do |old_attr, values|
54
+ catch (:skip) do
55
+ values << do_single_select(arg, old_attr, world)
56
+ end
57
+ end
58
+ values.join(option(:separator, ' '))
51
59
  when :first
52
60
  selector.each do |old_attr|
53
61
  actual, fetched = fetch_on(old_attr, arg)
@@ -0,0 +1,15 @@
1
+ module Monolens
2
+ module Str
3
+ class NullIfEmpty
4
+ include Lens
5
+
6
+ signature(Type::String, Type::String)
7
+
8
+ def call(arg, world = {})
9
+ is_string!(arg, world)
10
+
11
+ arg.to_s.strip.empty? ? nil : arg
12
+ end
13
+ end
14
+ end
15
+ end
@@ -7,6 +7,11 @@ module Monolens
7
7
  end
8
8
  module_function :downcase
9
9
 
10
+ def nullIfEmpty(options, registry)
11
+ NullIfEmpty.new(options, registry)
12
+ end
13
+ module_function :nullIfEmpty
14
+
10
15
  def strip(options, registry)
11
16
  Strip.new(options, registry)
12
17
  end
@@ -29,3 +34,4 @@ require_relative 'str/downcase'
29
34
  require_relative 'str/strip'
30
35
  require_relative 'str/split'
31
36
  require_relative 'str/upcase'
37
+ require_relative 'str/null_if_empty'
@@ -2,7 +2,7 @@ module Monolens
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 6
5
- TINY = 2
5
+ TINY = 4
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'coerce.array' do
4
+ subject do
5
+ Monolens.lens('coerce.array')
6
+ end
7
+
8
+ it 'works' do
9
+ expect(subject.call(12)).to eql([12])
10
+ expect(subject.call(nil)).to eql([])
11
+ expect(subject.call([12, 13])).to eql([12, 13])
12
+ end
13
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, "core.digs" do
4
+ let(:lens) do
5
+ Monolens.lens('core.digs' => {
6
+ defn: [
7
+ ['foo'],
8
+ ['hobbies', 1, 'name'],
9
+ ]
10
+ })
11
+ end
12
+
13
+ it 'works' do
14
+ input = {
15
+ hobbies: [
16
+ { name: 'programming' },
17
+ { name: 'music' }
18
+ ],
19
+ foo: 'Hello'
20
+ }
21
+ expected = ['Hello', 'music']
22
+ expect(lens.call(input)).to eql(expected)
23
+ end
24
+
25
+ describe 'error handling' do
26
+ let(:lens) do
27
+ Monolens.lens({
28
+ 'array.map' => {
29
+ lenses: {
30
+ 'core.digs' => {
31
+ on_missing: on_missing,
32
+ defn: [
33
+ ['foo'],
34
+ ['hobbies', 1, 'name'],
35
+ ]
36
+ }.compact
37
+ }
38
+ }
39
+ })
40
+ end
41
+
42
+ subject do
43
+ begin
44
+ lens.call(input)
45
+ rescue Monolens::LensError => ex
46
+ ex
47
+ end
48
+ end
49
+
50
+ context 'default behavior' do
51
+ let(:on_missing) do
52
+ nil
53
+ end
54
+
55
+ let(:input) do
56
+ [{
57
+ hobbies: [
58
+ { name: 'programming' }
59
+ ]
60
+ }]
61
+ end
62
+
63
+ it 'fails as expected' do
64
+ expect(subject).to be_a(Monolens::LensError)
65
+ expect(subject.location).to eql([0])
66
+ end
67
+ end
68
+
69
+ context 'on_missing: null' do
70
+ let(:on_missing) do
71
+ :null
72
+ end
73
+
74
+ let(:input) do
75
+ [{
76
+ hobbies: [
77
+ { name: 'programming' }
78
+ ]
79
+ }]
80
+ end
81
+
82
+ it 'works' do
83
+ expect(subject).to eql([[nil, nil]])
84
+ end
85
+ end
86
+ end
87
+ end
@@ -160,6 +160,80 @@ describe Monolens, 'object.select' do
160
160
  end
161
161
  end
162
162
 
163
+ context 'when using strategy: concat' do
164
+ subject do
165
+ Monolens.lens('object.select' => {
166
+ defn: {
167
+ name: [:firstname, :lastname],
168
+ status: :priority
169
+ },
170
+ strategy: 'concat',
171
+ separator: "\n",
172
+ on_missing: on_missing
173
+ }.compact)
174
+ end
175
+
176
+ context 'with :skip as on_missing' do
177
+ let(:on_missing) do
178
+ :skip
179
+ end
180
+
181
+ it 'works as expected with first only is present' do
182
+ input = {
183
+ firstname: 'Bernard',
184
+ priority: 12
185
+ }
186
+ expected = {
187
+ name: 'Bernard',
188
+ status: 12
189
+ }
190
+ expect(subject.call(input)).to eql(expected)
191
+ end
192
+
193
+ it 'works as expected when second only is present' do
194
+ input = {
195
+ lastname: 'Lambeau',
196
+ priority: 12
197
+ }
198
+ expected = {
199
+ name: 'Lambeau',
200
+ status: 12
201
+ }
202
+ expect(subject.call(input)).to eql(expected)
203
+ end
204
+
205
+ it 'works as expected when both are present' do
206
+ input = {
207
+ firstname: 'Bernard',
208
+ lastname: 'Lambeau',
209
+ priority: 12
210
+ }
211
+ expected = {
212
+ name: "Bernard\nLambeau",
213
+ status: 12
214
+ }
215
+ expect(subject.call(input)).to eql(expected)
216
+ end
217
+ end
218
+
219
+ context 'with on_missing: null' do
220
+ let(:on_missing) do
221
+ :null
222
+ end
223
+
224
+ it 'works as expected when missing' do
225
+ input = {
226
+ priority: 12
227
+ }
228
+ expected = {
229
+ name: "\n",
230
+ status: 12
231
+ }
232
+ expect(subject.call(input)).to eql(expected)
233
+ end
234
+ end
235
+ end
236
+
163
237
  context 'when using an array as selection' do
164
238
  subject do
165
239
  Monolens.lens('object.select' => {
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.6.2
4
+ version: 0.6.4
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-09-29 00:00:00.000000000 Z
11
+ date: 2024-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -130,6 +130,7 @@ files:
130
130
  - lib/monolens/stdlib/check.rb
131
131
  - lib/monolens/stdlib/check/not_empty.rb
132
132
  - lib/monolens/stdlib/coerce.rb
133
+ - lib/monolens/stdlib/coerce/array.rb
133
134
  - lib/monolens/stdlib/coerce/date.rb
134
135
  - lib/monolens/stdlib/coerce/date_time.rb
135
136
  - lib/monolens/stdlib/coerce/integer.rb
@@ -137,6 +138,7 @@ files:
137
138
  - lib/monolens/stdlib/core.rb
138
139
  - lib/monolens/stdlib/core/chain.rb
139
140
  - lib/monolens/stdlib/core/dig.rb
141
+ - lib/monolens/stdlib/core/digs.rb
140
142
  - lib/monolens/stdlib/core/literal.rb
141
143
  - lib/monolens/stdlib/core/mapping.rb
142
144
  - lib/monolens/stdlib/object.rb
@@ -152,6 +154,7 @@ files:
152
154
  - lib/monolens/stdlib/skip/null.rb
153
155
  - lib/monolens/stdlib/str.rb
154
156
  - lib/monolens/stdlib/str/downcase.rb
157
+ - lib/monolens/stdlib/str/null_if_empty.rb
155
158
  - lib/monolens/stdlib/str/split.rb
156
159
  - lib/monolens/stdlib/str/strip.rb
157
160
  - lib/monolens/stdlib/str/upcase.rb
@@ -193,11 +196,13 @@ files:
193
196
  - spec/monolens/stdlib/array/test_join.rb
194
197
  - spec/monolens/stdlib/array/test_map.rb
195
198
  - spec/monolens/stdlib/check/test_not_empty.rb
199
+ - spec/monolens/stdlib/coerce/test_array.rb
196
200
  - spec/monolens/stdlib/coerce/test_date.rb
197
201
  - spec/monolens/stdlib/coerce/test_datetime.rb
198
202
  - spec/monolens/stdlib/coerce/test_integer.rb
199
203
  - spec/monolens/stdlib/coerce/test_string.rb
200
204
  - spec/monolens/stdlib/core/test_dig.rb
205
+ - spec/monolens/stdlib/core/test_digs.rb
201
206
  - spec/monolens/stdlib/core/test_literal.rb
202
207
  - spec/monolens/stdlib/core/test_mapping.rb
203
208
  - spec/monolens/stdlib/object/test_allbut.rb
@@ -242,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
242
247
  - !ruby/object:Gem::Version
243
248
  version: '0'
244
249
  requirements: []
245
- rubygems_version: 3.1.4
250
+ rubygems_version: 3.3.27
246
251
  signing_key:
247
252
  specification_version: 4
248
253
  summary: Data transformations inspired by Cambria lenses