monolens 0.6.2 → 0.6.4

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 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