monolens 0.1.0 → 0.2.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
- SHA256:
3
- metadata.gz: fded1da33ff2b2e68f5c0ec9177d77fed38d12576a7a505e7e50d25dd8c356c8
4
- data.tar.gz: ebcc624d96c03a82b1939a9cd9d76c0902fd170510ca93a670b5fbf02e477f08
2
+ SHA1:
3
+ metadata.gz: 704980b0a35ef47c4ea022a0b2bf39922966a11d
4
+ data.tar.gz: 0a032437e56dbd4d2214ba7b435120ceceb62709
5
5
  SHA512:
6
- metadata.gz: 508ede1d192eb3cc5d5f9a359cb18f627e44b5cdbd4d9c89bc89c90b52787b96061efcd95a01a4855f7477213e33aa56a7178c9afe4ccf16124af9155d54af1d
7
- data.tar.gz: 82138ad714e74d73c880eff43423d01546cfdf59569913d7d20734460dfc6ad08bab6ac7e4e8ae760a3a9de7884716297bd2433bd2a7c88f109d7fbc400b0fbd
6
+ metadata.gz: 2a580e3564f5bd208273e16c0c1da4c261917d371e0e6236bab873920b6a2372b7f36e5a085654f8f2ff79490f762cfeb68a8d38a24265d8cd4da5e983389f6e
7
+ data.tar.gz: '0229785824434dd14c87581f0060a784b623e3154b514bea8548d7cce94bb35563ceb6fdbc45146a883c4ca476c1c329b9adbd6a329ab1382f197377709170a6'
data/README.md CHANGED
@@ -37,13 +37,13 @@ The following monolens file, say `lens.yml`
37
37
  ---
38
38
  version: 1.0
39
39
  lenses:
40
- - map:
41
- - object.transform
40
+ - array.map:
41
+ - object.transform:
42
42
  status:
43
43
  - str.upcase
44
44
  body:
45
45
  - str.strip
46
- - object.rename
46
+ - object.rename:
47
47
  body: description
48
48
  ```
49
49
 
@@ -76,10 +76,11 @@ result = lens.call(input)
76
76
  ## Available lenses
77
77
 
78
78
  ```
79
- core.map - Apply a lens to each member of an Array
80
79
  core.chain - Applies a chain of lenses to an input value
80
+ core.mapping - Converts the input value via a key:value mapping
81
81
 
82
82
  str.strip - Remove leading and trailing spaces of an input string
83
+ str.split - Splits the input string as an array
83
84
  str.downcase - Converts the input string to lowercase
84
85
  str.upcase - Converts the input string to uppercase
85
86
 
@@ -89,11 +90,14 @@ object.rename - Rename some keys of the input object
89
90
  object.transform - Applies specific lenses to specific values of the input object
90
91
  object.keys - Applies a lens to all keys of the input object
91
92
  object.values - Applies a lens to all values of the input object
93
+ object.select - Builds an object by selecting key/values from the input object
92
94
 
93
95
  coerce.date - Coerces the input value to a date
94
96
  coerce.datetime - Coerces the input value to a datetime
95
97
 
96
98
  array.compact - Removes null from the input array
99
+ array.join - Joins values of the input array as a string
100
+ array.map - Apply a lens to each member of an Array
97
101
  ```
98
102
 
99
103
  ## Public API
@@ -0,0 +1,13 @@
1
+ module Monolens
2
+ module Array
3
+ class Join
4
+ include Lens
5
+
6
+ def call(arg, *rest)
7
+ is_array!(arg)
8
+
9
+ arg.join(option(:separator, ' '))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,5 @@
1
1
  module Monolens
2
- module Core
2
+ module Array
3
3
  class Map
4
4
  include Lens
5
5
 
@@ -5,7 +5,19 @@ module Monolens
5
5
  end
6
6
  module_function :compact
7
7
 
8
+ def join(options = {})
9
+ Join.new(options)
10
+ end
11
+ module_function :join
12
+
13
+ def map(lens)
14
+ Map.new(lens)
15
+ end
16
+ module_function :map
17
+
8
18
  Monolens.define_namespace 'array', self
9
19
  end
10
20
  end
11
21
  require_relative 'array/compact'
22
+ require_relative 'array/join'
23
+ require_relative 'array/map'
@@ -0,0 +1,15 @@
1
+ module Monolens
2
+ module Core
3
+ class Mapping
4
+ include Lens
5
+
6
+ def call(arg, *rest)
7
+ option(:values, {}).fetch(arg) do
8
+ raise LensError if option(:fail_if_missing)
9
+
10
+ option(:default)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/monolens/core.rb CHANGED
@@ -5,13 +5,13 @@ module Monolens
5
5
  end
6
6
  module_function :chain
7
7
 
8
- def map(lens)
9
- Map.new(lens)
8
+ def mapping(options)
9
+ Mapping.new(options)
10
10
  end
11
- module_function :map
11
+ module_function :mapping
12
12
 
13
13
  Monolens.define_namespace 'core', self
14
14
  end
15
15
  end
16
16
  require_relative 'core/chain'
17
- require_relative 'core/map'
17
+ require_relative 'core/mapping'
data/lib/monolens/lens.rb CHANGED
@@ -12,11 +12,18 @@ module Monolens
12
12
  [ attr, arg[attr] ]
13
13
  elsif arg.key?(attr_s = attr.to_s)
14
14
  [ attr_s, arg[attr_s] ]
15
+ elsif arg.key?(attr_sym = attr.to_sym)
16
+ [ attr_sym, arg[attr_sym] ]
15
17
  else
16
18
  [ attr, nil ]
17
19
  end
18
20
  end
19
21
 
22
+ def option(name, default = nil)
23
+ _, option = fetch_on(name, options)
24
+ option.nil? ? default : option
25
+ end
26
+
20
27
  def is_string!(arg)
21
28
  return if arg.is_a?(::String)
22
29
 
@@ -0,0 +1,30 @@
1
+ module Monolens
2
+ module Object
3
+ class Select
4
+ include Lens
5
+
6
+ def initialize(selection)
7
+ super({})
8
+ @selection = selection
9
+ end
10
+
11
+ def call(arg, *rest)
12
+ is_hash!(arg)
13
+
14
+ result = {}
15
+ @selection.each_pair do |new_attr, selector|
16
+ is_array = selector.is_a?(::Array)
17
+ is_symbol = false
18
+ values = Array(selector).map do |old_attr|
19
+ actual, fetched = fetch_on(old_attr, arg)
20
+ is_symbol ||= actual.is_a?(Symbol)
21
+ fetched
22
+ end
23
+ new_attr = is_symbol ? new_attr.to_sym : new_attr.to_s
24
+ result[new_attr] = is_array ? values : values.first
25
+ end
26
+ result
27
+ end
28
+ end
29
+ end
30
+ end
@@ -20,6 +20,11 @@ module Monolens
20
20
  end
21
21
  module_function :values
22
22
 
23
+ def select(lens)
24
+ Select.new(lens)
25
+ end
26
+ module_function :select
27
+
23
28
  Monolens.define_namespace 'object', self
24
29
  end
25
30
  end
@@ -27,3 +32,4 @@ require_relative 'object/rename'
27
32
  require_relative 'object/transform'
28
33
  require_relative 'object/keys'
29
34
  require_relative 'object/values'
35
+ require_relative 'object/select'
@@ -0,0 +1,14 @@
1
+ module Monolens
2
+ module Str
3
+ class Split
4
+ include Lens
5
+
6
+ def call(arg, *rest)
7
+ is_string!(arg)
8
+
9
+ sep = option(:separator)
10
+ sep ? arg.split(sep) : arg.split
11
+ end
12
+ end
13
+ end
14
+ end
data/lib/monolens/str.rb CHANGED
@@ -1,23 +1,29 @@
1
1
  module Monolens
2
2
  module Str
3
+ def downcase(options = {})
4
+ Downcase.new(options)
5
+ end
6
+ module_function :downcase
7
+
3
8
  def strip(options = {})
4
9
  Strip.new(options)
5
10
  end
6
11
  module_function :strip
7
12
 
13
+ def split(options = {})
14
+ Split.new(options)
15
+ end
16
+ module_function :split
17
+
8
18
  def upcase(options = {})
9
19
  Upcase.new(options)
10
20
  end
11
21
  module_function :upcase
12
22
 
13
- def downcase(options = {})
14
- Downcase.new(options)
15
- end
16
- module_function :downcase
17
-
18
23
  Monolens.define_namespace 'str', self
19
24
  end
20
25
  end
26
+ require_relative 'str/downcase'
21
27
  require_relative 'str/strip'
28
+ require_relative 'str/split'
22
29
  require_relative 'str/upcase'
23
- require_relative 'str/downcase'
@@ -1,7 +1,7 @@
1
1
  module Monolens
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 1
4
+ MINOR = 2
5
5
  TINY = 0
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
data/lib/monolens.rb CHANGED
@@ -24,7 +24,7 @@ module Monolens
24
24
  end
25
25
 
26
26
  def load_yaml(yaml)
27
- Monolens::File.new(YAML.load(yaml))
27
+ Monolens::File.new(YAML.safe_load(yaml))
28
28
  end
29
29
 
30
30
  def lens(arg)
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'array.compact' do
4
+ subject do
5
+ Monolens.lens('array.compact')
6
+ end
7
+
8
+ it 'removes nils' do
9
+ expect(subject.call([nil, 'notnil'])).to eql(['notnil'])
10
+ end
11
+
12
+ it 'supports empty arrays' do
13
+ expect(subject.call([])).to eql([])
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'array.join' do
4
+ context 'when used without options' do
5
+ subject do
6
+ Monolens.lens('array.join')
7
+ end
8
+
9
+ it 'joins values with spaces' do
10
+ expect(subject.call(['hello', 'world'])).to eql('hello world')
11
+ end
12
+
13
+ it 'supports empty arrays' do
14
+ expect(subject.call([])).to eql('')
15
+ end
16
+ end
17
+
18
+ context 'when specifying the separator' do
19
+ subject do
20
+ Monolens.lens('array.join' => { separator: ', ' })
21
+ end
22
+
23
+ it 'joins values with it' do
24
+ expect(subject.call(['hello', 'world'])).to eql('hello, world')
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'array.map' do
4
+ subject do
5
+ Monolens.lens('array.map' => 'str.upcase')
6
+ end
7
+
8
+ it 'joins values with spaces' do
9
+ input = ['hello', 'world']
10
+ expected = ['HELLO', 'WORLD']
11
+ expect(subject.call(input)).to eql(expected)
12
+ end
13
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'core.mapping' do
4
+ let(:mapping) do
5
+ { 'values' => { 'todo' => 'open' }}
6
+ end
7
+
8
+ context 'with default options' do
9
+ subject do
10
+ Monolens.lens('core.mapping' => mapping)
11
+ end
12
+
13
+ it 'replaces the value by its mapped' do
14
+ expect(subject.call('todo')).to eql('open')
15
+ end
16
+
17
+ it 'returns nil if not found' do
18
+ expect(subject.call('nosuchone')).to eql(nil)
19
+ end
20
+ end
21
+
22
+ context 'specifying a default value' do
23
+ subject do
24
+ Monolens.lens('core.mapping' => mapping.merge('default' => 'foo'))
25
+ end
26
+
27
+ it 'replaces the value by its mapped' do
28
+ expect(subject.call('todo')).to eql('open')
29
+ end
30
+
31
+ it 'returns the default if not found' do
32
+ expect(subject.call('nosuchone')).to eql('foo')
33
+ end
34
+ end
35
+
36
+ context 'lets raise if not found' do
37
+ subject do
38
+ Monolens.lens('core.mapping' => mapping.merge('fail_if_missing' => true))
39
+ end
40
+
41
+ it 'replaces the value by its mapped' do
42
+ expect(subject.call('todo')).to eql('open')
43
+ end
44
+
45
+ it 'raises if not found' do
46
+ expect {
47
+ subject.call('nosuchone')
48
+ }.to raise_error(Monolens::LensError)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'object.select' do
4
+ context 'when using symbols in the definition' do
5
+ subject do
6
+ Monolens.lens('object.select' => {
7
+ name: [:firstname, :lastname],
8
+ status: :priority
9
+ })
10
+ end
11
+
12
+ it 'works as expected' do
13
+ input = {
14
+ firstname: 'Bernard',
15
+ lastname: 'Lambeau',
16
+ priority: 12
17
+ }
18
+ expected = {
19
+ name: ['Bernard', 'Lambeau'],
20
+ status: 12
21
+ }
22
+ expect(subject.call(input)).to eql(expected)
23
+ end
24
+
25
+ it 'works as with Strings' do
26
+ input = {
27
+ 'firstname' => 'Bernard',
28
+ 'lastname' => 'Lambeau',
29
+ 'priority' => 12
30
+ }
31
+ expected = {
32
+ 'name' => ['Bernard', 'Lambeau'],
33
+ 'status' => 12
34
+ }
35
+ expect(subject.call(input)).to eql(expected)
36
+ end
37
+ end
38
+
39
+ context 'when using strings in the definition' do
40
+ subject do
41
+ Monolens.lens('object.select' => {
42
+ 'name' => ['firstname', 'lastname'],
43
+ 'status' => 'priority'
44
+ })
45
+ end
46
+
47
+ it 'works as expected with Symbols' do
48
+ input = {
49
+ firstname: 'Bernard',
50
+ lastname: 'Lambeau',
51
+ priority: 12
52
+ }
53
+ expected = {
54
+ name: ['Bernard', 'Lambeau'],
55
+ status: 12
56
+ }
57
+ expect(subject.call(input)).to eql(expected)
58
+ end
59
+
60
+ it 'works with Strings' do
61
+ input = {
62
+ 'firstname' => 'Bernard',
63
+ 'lastname' => 'Lambeau',
64
+ 'priority' => 12
65
+ }
66
+ expected = {
67
+ 'name' => ['Bernard', 'Lambeau'],
68
+ 'status' => 12
69
+ }
70
+ expect(subject.call(input)).to eql(expected)
71
+ end
72
+ end
73
+ end
@@ -17,7 +17,7 @@ describe Monolens, 'skip.null' do
17
17
 
18
18
  context 'when used in a Map' do
19
19
  subject do
20
- Monolens.lens('map' => ['skip.null', 'str.upcase'])
20
+ Monolens.lens('array.map' => ['skip.null', 'str.upcase'])
21
21
  end
22
22
 
23
23
  it 'maps nils' do
@@ -27,7 +27,7 @@ describe Monolens, 'skip.null' do
27
27
 
28
28
  context 'when used in a Map, but we want no nils' do
29
29
  subject do
30
- Monolens.lens(['array.compact', { 'map' => ['skip.null', 'str.upcase'] }])
30
+ Monolens.lens(['array.compact', { 'array.map' => ['skip.null', 'str.upcase'] }])
31
31
  end
32
32
 
33
33
  it 'works' do
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'str.downcase' do
4
+ subject do
5
+ Monolens.lens('str.downcase')
6
+ end
7
+
8
+ it 'converts to lowercase' do
9
+ input = 'FOO'
10
+ expected = 'foo'
11
+ expect(subject.call(input)).to eql(expected)
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'str.split' do
4
+ context 'without options' do
5
+ subject do
6
+ Monolens.lens('str.split')
7
+ end
8
+
9
+ it 'splits a string as an array using space separator' do
10
+ input = 'foo bar'
11
+ expected = ['foo', 'bar']
12
+ expect(subject.call(input)).to eql(expected)
13
+ end
14
+
15
+ it 'is greedy on spaces and splits on carriage returns and tabs' do
16
+ input = "foo bar\nbaz\tboz"
17
+ expected = ['foo', 'bar', 'baz', 'boz']
18
+ expect(subject.call(input)).to eql(expected)
19
+ end
20
+ end
21
+
22
+ context 'when specifying the separator' do
23
+ subject do
24
+ Monolens.lens('str.split' => { separator: ',' })
25
+ end
26
+
27
+ it 'uses it' do
28
+ input = 'foo,bar'
29
+ expected = ['foo', 'bar']
30
+ expect(subject.call(input)).to eql(expected)
31
+ end
32
+
33
+ it 'does not make whitespace magic' do
34
+ input = "foo, bar"
35
+ expected = ['foo', ' bar']
36
+ expect(subject.call(input)).to eql(expected)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'str.strip' do
4
+ subject do
5
+ Monolens.lens('str.strip')
6
+ end
7
+
8
+ it 'strips leading and trailing spaces' do
9
+ input = ' foo '
10
+ expected = 'foo'
11
+ expect(subject.call(input)).to eql(expected)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Monolens, 'str.upcase' do
4
+ subject do
5
+ Monolens.lens('str.upcase')
6
+ end
7
+
8
+ it 'converts to uppercase' do
9
+ input = 'foo'
10
+ expected = 'FOO'
11
+ expect(subject.call(input)).to eql(expected)
12
+ end
13
+ end
data/spec/test_readme.rb CHANGED
@@ -21,7 +21,7 @@ describe "What's said in README" do
21
21
  ---
22
22
  version: 1.0
23
23
  lenses:
24
- - map:
24
+ - array.map:
25
25
  - object.transform:
26
26
  status:
27
27
  - str.upcase
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.1.0
4
+ version: 0.2.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-04 00:00:00.000000000 Z
11
+ date: 2022-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -65,38 +65,50 @@ files:
65
65
  - lib/monolens.rb
66
66
  - lib/monolens/array.rb
67
67
  - lib/monolens/array/compact.rb
68
+ - lib/monolens/array/join.rb
69
+ - lib/monolens/array/map.rb
68
70
  - lib/monolens/coerce.rb
69
71
  - lib/monolens/coerce/date.rb
70
72
  - lib/monolens/coerce/date_time.rb
71
73
  - lib/monolens/core.rb
72
74
  - lib/monolens/core/chain.rb
73
- - lib/monolens/core/map.rb
75
+ - lib/monolens/core/mapping.rb
74
76
  - lib/monolens/error.rb
75
77
  - lib/monolens/file.rb
76
78
  - lib/monolens/lens.rb
77
79
  - lib/monolens/object.rb
78
80
  - lib/monolens/object/keys.rb
79
81
  - lib/monolens/object/rename.rb
82
+ - lib/monolens/object/select.rb
80
83
  - lib/monolens/object/transform.rb
81
84
  - lib/monolens/object/values.rb
82
85
  - lib/monolens/skip.rb
83
86
  - lib/monolens/skip/null.rb
84
87
  - lib/monolens/str.rb
85
88
  - lib/monolens/str/downcase.rb
89
+ - lib/monolens/str/split.rb
86
90
  - lib/monolens/str/strip.rb
87
91
  - lib/monolens/str/upcase.rb
88
92
  - lib/monolens/version.rb
89
93
  - spec/fixtures/coerce.yml
90
94
  - spec/fixtures/simple.yml
91
95
  - spec/fixtures/transform.yml
96
+ - spec/monolens/array/test_compact.rb
97
+ - spec/monolens/array/test_join.rb
98
+ - spec/monolens/array/test_map.rb
92
99
  - spec/monolens/coerce/test_date.rb
93
100
  - spec/monolens/coerce/test_datetime.rb
94
- - spec/monolens/core/test_map.rb
101
+ - spec/monolens/core/test_mapping.rb
95
102
  - spec/monolens/object/test_keys.rb
96
103
  - spec/monolens/object/test_rename.rb
104
+ - spec/monolens/object/test_select.rb
97
105
  - spec/monolens/object/test_transform.rb
98
106
  - spec/monolens/object/test_values.rb
99
107
  - spec/monolens/skip/test_null.rb
108
+ - spec/monolens/str/test_downcase.rb
109
+ - spec/monolens/str/test_split.rb
110
+ - spec/monolens/str/test_strip.rb
111
+ - spec/monolens/str/test_upcase.rb
100
112
  - spec/monolens/test_lens.rb
101
113
  - spec/spec_helper.rb
102
114
  - spec/test_monolens.rb
@@ -122,7 +134,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
134
  - !ruby/object:Gem::Version
123
135
  version: '0'
124
136
  requirements: []
125
- rubygems_version: 3.1.4
137
+ rubyforge_project:
138
+ rubygems_version: 2.6.14.4
126
139
  signing_key:
127
140
  specification_version: 4
128
141
  summary: Data transformations inspired by Cambria lenses
@@ -1,11 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Monolens, 'map' do
4
- subject do
5
- Monolens.lens('map' => ['str.upcase'])
6
- end
7
-
8
- it 'maps as expected' do
9
- expect(subject.call(['foo','bar'])).to eql(['FOO','BAR'])
10
- end
11
- end