Skip to content

Commit

Permalink
Add core.digs
Browse files Browse the repository at this point in the history
  • Loading branch information
blambeau committed Jul 17, 2024
1 parent 488f05a commit 0d030cd
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 0.6.4

* Add `str.nullIfEmpty` that converts empty string by a null
* Add `core.digs`
* Add a :concat strategy to `object.select`

## 0.6.3 - 2024-07-09
Expand Down
43 changes: 43 additions & 0 deletions documentation/stdlib/core/digs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# core.digs

Plural form of core.dig, returns an array with multiple
digs at once

```
core.digs: Object -> [Any]
on_missing: null|fail = fail
defn: [[String|Integer]] = []
```

This lens can be used to extract multiple values along some `defn`
paths in the input object.

## Example

Applying the following lens:

```yaml
---
core.digs:
defn:
- ['foo']
- ['hobbies', 1, 'name']
```
to the following input:
```yaml
---
foo: Hello
hobbies:
- { name: 'Programming' }
- { name: 'Databases' }
```
will return:
```yaml
---
- Hello
- Databases
```
6 changes: 6 additions & 0 deletions lib/monolens/stdlib/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ def dig(options, registry)
end
module_function :dig

def digs(options, registry)
Digs.new(options, registry)
end
module_function :digs

def literal(options, registry)
Literal.new(options, registry)
end
Expand All @@ -27,5 +32,6 @@ def mapping(options, registry)
end
require_relative 'core/chain'
require_relative 'core/dig'
require_relative 'core/digs'
require_relative 'core/mapping'
require_relative 'core/literal'
63 changes: 63 additions & 0 deletions lib/monolens/stdlib/core/digs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module Monolens
module Core
class Digs
include Lens

signature(Type::Diggable, Type::Any, {
defn: [
Type::Array.of(Type::Array.of(Type::Any.of(Type::Integer, Type::String))),
true
],
on_missing: [Type::Strategy.missing(%w{fail null}), false]
})

def call(arg, world = {})
to_dig = option(:defn, [])
to_dig.map {|digging|
digging.inject(arg) do |memo, part|
dig_on(part, memo, world)
end
}
end

private

def path
option(:defn, []).join('.')
end

def dig_on(attr, arg, world)
if arg.is_a?(::Array)
index = attr.to_i
on_missing(world) if index >= arg.size
arg[index]
elsif arg.is_a?(::Hash)
actual, value = fetch_on(attr, arg)
on_missing(world) unless actual
value
elsif arg
if attr.is_a?(::Integer)
is_array!(arg, world)
else
is_hash!(arg, world)
end
else
on_missing(world)
end
end

def on_missing(world)
strategy = option(:on_missing, :fail)
case strategy.to_sym
when :fail
fail!("Unable to find #{path}", world)
when :null
nil
else
raise Monolens::Error, "Unexpected missing strategy `#{strategy}`"
end
end
private :on_missing
end
end
end
87 changes: 87 additions & 0 deletions spec/monolens/stdlib/core/test_digs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
require 'spec_helper'

describe Monolens, "core.digs" do
let(:lens) do
Monolens.lens('core.digs' => {
defn: [
['foo'],
['hobbies', 1, 'name'],
]
})
end

it 'works' do
input = {
hobbies: [
{ name: 'programming' },
{ name: 'music' }
],
foo: 'Hello'
}
expected = ['Hello', 'music']
expect(lens.call(input)).to eql(expected)
end

describe 'error handling' do
let(:lens) do
Monolens.lens({
'array.map' => {
lenses: {
'core.digs' => {
on_missing: on_missing,
defn: [
['foo'],
['hobbies', 1, 'name'],
]
}.compact
}
}
})
end

subject do
begin
lens.call(input)
rescue Monolens::LensError => ex
ex
end
end

context 'default behavior' do
let(:on_missing) do
nil
end

let(:input) do
[{
hobbies: [
{ name: 'programming' }
]
}]
end

it 'fails as expected' do
expect(subject).to be_a(Monolens::LensError)
expect(subject.location).to eql([0])
end
end

context 'on_missing: null' do
let(:on_missing) do
:null
end

let(:input) do
[{
hobbies: [
{ name: 'programming' }
]
}]
end

it 'works' do
expect(subject).to eql([[nil, nil]])
end
end
end
end

0 comments on commit 0d030cd

Please sign in to comment.