monolens 0.1.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +14 -4
- data/bin/monolens +11 -0
- data/lib/monolens/array/compact.rb +2 -2
- data/lib/monolens/array/join.rb +13 -0
- data/lib/monolens/array/map.rb +57 -0
- data/lib/monolens/array.rb +12 -0
- data/lib/monolens/coerce/date.rb +22 -6
- data/lib/monolens/coerce/date_time.rb +30 -6
- data/lib/monolens/coerce/integer.rb +15 -0
- data/lib/monolens/coerce/string.rb +13 -0
- data/lib/monolens/coerce.rb +12 -3
- data/lib/monolens/command.rb +87 -0
- data/lib/monolens/core/chain.rb +2 -2
- data/lib/monolens/core/dig.rb +52 -0
- data/lib/monolens/core/mapping.rb +15 -0
- data/lib/monolens/core.rb +10 -4
- data/lib/monolens/error.rb +9 -2
- data/lib/monolens/error_handler.rb +21 -0
- data/lib/monolens/file.rb +2 -7
- data/lib/monolens/lens/fetch_support.rb +19 -0
- data/lib/monolens/lens/location.rb +17 -0
- data/lib/monolens/lens/options.rb +41 -0
- data/lib/monolens/lens.rb +41 -18
- data/lib/monolens/object/extend.rb +53 -0
- data/lib/monolens/object/keys.rb +8 -10
- data/lib/monolens/object/rename.rb +3 -3
- data/lib/monolens/object/select.rb +58 -0
- data/lib/monolens/object/transform.rb +34 -12
- data/lib/monolens/object/values.rb +34 -10
- data/lib/monolens/object.rb +12 -0
- data/lib/monolens/skip/null.rb +1 -1
- data/lib/monolens/str/downcase.rb +2 -2
- data/lib/monolens/str/split.rb +14 -0
- data/lib/monolens/str/strip.rb +3 -1
- data/lib/monolens/str/upcase.rb +2 -2
- data/lib/monolens/str.rb +12 -6
- data/lib/monolens/version.rb +1 -1
- data/lib/monolens.rb +7 -1
- data/spec/fixtures/coerce.yml +3 -2
- data/spec/fixtures/transform.yml +5 -4
- data/spec/monolens/array/test_compact.rb +15 -0
- data/spec/monolens/array/test_join.rb +27 -0
- data/spec/monolens/array/test_map.rb +96 -0
- data/spec/monolens/coerce/test_date.rb +34 -4
- data/spec/monolens/coerce/test_datetime.rb +70 -7
- data/spec/monolens/coerce/test_integer.rb +46 -0
- data/spec/monolens/coerce/test_string.rb +15 -0
- data/spec/monolens/command/map-upcase.lens.yml +5 -0
- data/spec/monolens/command/names-with-null.json +5 -0
- data/spec/monolens/command/names.json +4 -0
- data/spec/monolens/command/robust-map-upcase.lens.yml +7 -0
- data/spec/monolens/core/test_dig.rb +78 -0
- data/spec/monolens/core/test_mapping.rb +76 -0
- data/spec/monolens/lens/test_options.rb +73 -0
- data/spec/monolens/object/test_extend.rb +94 -0
- data/spec/monolens/object/test_keys.rb +54 -22
- data/spec/monolens/object/test_rename.rb +1 -1
- data/spec/monolens/object/test_select.rb +202 -0
- data/spec/monolens/object/test_transform.rb +93 -6
- data/spec/monolens/object/test_values.rb +110 -12
- data/spec/monolens/skip/test_null.rb +2 -2
- data/spec/monolens/str/test_downcase.rb +13 -0
- data/spec/monolens/str/test_split.rb +39 -0
- data/spec/monolens/str/test_strip.rb +13 -0
- data/spec/monolens/str/test_upcase.rb +13 -0
- data/spec/monolens/test_command.rb +128 -0
- data/spec/monolens/test_error_traceability.rb +60 -0
- data/spec/monolens/test_lens.rb +1 -1
- data/spec/test_readme.rb +8 -6
- metadata +39 -5
- data/lib/monolens/core/map.rb +0 -18
- data/spec/monolens/core/test_map.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 30152f626d5675640e96816c7036d5a6dbac8a25
|
4
|
+
data.tar.gz: a26d4e5d0715bd511aea96da758fd12d36e2a70b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b27ab005aaf2a6bf1abdc8c9284072ce1307498b7edad606526198c3e4ae003d0ddda72eb532266b7b737fbb04146959903868c50e16be4fbb8f1a073f20e9b
|
7
|
+
data.tar.gz: 8fd2b3ad776ff72da243ba29ab7fc2bd5fd05e88b8d34c93cd38c362142b83ff4c8164559af707d3ea314c39af3b9c415c1716841c269ab73bf9e06b852c883e
|
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,24 +76,32 @@ result = lens.call(input)
|
|
76
76
|
## Available lenses
|
77
77
|
|
78
78
|
```
|
79
|
-
core.
|
79
|
+
core.dig - Extract from the input value (object or array) using a path.
|
80
80
|
core.chain - Applies a chain of lenses to an input value
|
81
|
+
core.mapping - Converts the input value via a key:value mapping
|
81
82
|
|
82
83
|
str.strip - Remove leading and trailing spaces of an input string
|
84
|
+
str.split - Splits the input string as an array
|
83
85
|
str.downcase - Converts the input string to lowercase
|
84
86
|
str.upcase - Converts the input string to uppercase
|
85
87
|
|
86
88
|
skip.null - Aborts the current lens transformation if nil
|
87
89
|
|
90
|
+
object.extend - Adds key/value(s) to the input object
|
88
91
|
object.rename - Rename some keys of the input object
|
89
92
|
object.transform - Applies specific lenses to specific values of the input object
|
90
93
|
object.keys - Applies a lens to all keys of the input object
|
91
94
|
object.values - Applies a lens to all values of the input object
|
95
|
+
object.select - Builds an object by selecting key/values from the input object
|
92
96
|
|
93
97
|
coerce.date - Coerces the input value to a date
|
94
98
|
coerce.datetime - Coerces the input value to a datetime
|
99
|
+
coerce.string - Coerces the input value to a string (aka to_s)
|
100
|
+
coerce.integer - Coerces the input value to an integer
|
95
101
|
|
96
102
|
array.compact - Removes null from the input array
|
103
|
+
array.join - Joins values of the input array as a string
|
104
|
+
array.map - Apply a lens to each member of an Array
|
97
105
|
```
|
98
106
|
|
99
107
|
## Public API
|
@@ -110,6 +118,8 @@ Anyway, the public interface will cover at least the following:
|
|
110
118
|
|
111
119
|
* Exception classes: `Monolens::Error`, `Monolens::LensError`
|
112
120
|
|
121
|
+
* bin/monolens, its args, options and general behavior
|
122
|
+
|
113
123
|
Everything else is condidered private and may change any time
|
114
124
|
(i.e. even on patch releases).
|
115
125
|
|
data/bin/monolens
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
retried = false
|
3
|
+
begin
|
4
|
+
require 'monolens'
|
5
|
+
require 'monolens/command'
|
6
|
+
rescue LoadError
|
7
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
8
|
+
to_retry, retried = !retried, true
|
9
|
+
to_retry ? retry : raise
|
10
|
+
end
|
11
|
+
Monolens::Command.call(ARGV)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Monolens
|
2
|
+
module Array
|
3
|
+
class Map
|
4
|
+
include Lens
|
5
|
+
|
6
|
+
def initialize(arg)
|
7
|
+
options, lenses = case arg
|
8
|
+
when ::Hash
|
9
|
+
opts = arg.dup; opts.delete(:lenses)
|
10
|
+
_, ls = fetch_on(:lenses, arg)
|
11
|
+
raise ArgumentError, 'Lenses are required' if ls.nil?
|
12
|
+
[ opts, ls ]
|
13
|
+
else
|
14
|
+
[{}, arg]
|
15
|
+
end
|
16
|
+
super(options)
|
17
|
+
@lenses = Monolens.lens(lenses)
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(arg, world = {})
|
21
|
+
is_enumerable!(arg, world)
|
22
|
+
|
23
|
+
result = []
|
24
|
+
arg.each_with_index do |a, i|
|
25
|
+
deeper(world, i) do |w|
|
26
|
+
begin
|
27
|
+
result << @lenses.call(a, w)
|
28
|
+
rescue Monolens::LensError => ex
|
29
|
+
strategy = option(:on_error, :fail)
|
30
|
+
handle_error(strategy, ex, result, world)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_error(strategy, ex, result, world)
|
38
|
+
strategy = strategy.to_sym unless strategy.is_a?(::Array)
|
39
|
+
case strategy
|
40
|
+
when ::Array
|
41
|
+
strategy.each{|s| handle_error(s, ex, result, world) }
|
42
|
+
when :handler
|
43
|
+
error_handler!(world).call(ex)
|
44
|
+
when :fail
|
45
|
+
raise
|
46
|
+
when :null
|
47
|
+
result << nil
|
48
|
+
when :skip
|
49
|
+
nil
|
50
|
+
else
|
51
|
+
raise Monolens::Error, "Unexpected error strategy `#{strategy}`"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
private :handle_error
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/monolens/array.rb
CHANGED
@@ -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(options)
|
14
|
+
Map.new(options)
|
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'
|
data/lib/monolens/coerce/date.rb
CHANGED
@@ -5,13 +5,21 @@ module Monolens
|
|
5
5
|
class Date
|
6
6
|
include Lens
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
DEFAULT_FORMATS = [
|
9
|
+
nil
|
10
|
+
]
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
def call(arg, world = {})
|
13
|
+
return arg if arg.is_a?(::Date)
|
14
|
+
|
15
|
+
is_string!(arg, world)
|
16
|
+
|
17
|
+
date = nil
|
18
|
+
first_error = nil
|
19
|
+
formats = @options.fetch(:formats, DEFAULT_FORMATS)
|
20
|
+
formats.each do |format|
|
13
21
|
begin
|
14
|
-
return date =
|
22
|
+
return date = strptime(arg, format)
|
15
23
|
rescue ArgumentError => ex
|
16
24
|
first_error ||= ex
|
17
25
|
rescue ::Date::Error => ex
|
@@ -19,7 +27,15 @@ module Monolens
|
|
19
27
|
end
|
20
28
|
end
|
21
29
|
|
22
|
-
|
30
|
+
fail!(first_error.message, world)
|
31
|
+
end
|
32
|
+
|
33
|
+
def strptime(arg, format = nil)
|
34
|
+
if format.nil?
|
35
|
+
::Date.strptime(arg)
|
36
|
+
else
|
37
|
+
::Date.strptime(arg, format)
|
38
|
+
end
|
23
39
|
end
|
24
40
|
end
|
25
41
|
end
|
@@ -5,13 +5,21 @@ module Monolens
|
|
5
5
|
class DateTime
|
6
6
|
include Lens
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
DEFAULT_FORMATS = [
|
9
|
+
nil
|
10
|
+
]
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
def call(arg, world = {})
|
13
|
+
return arg if arg.is_a?(::DateTime)
|
14
|
+
|
15
|
+
is_string!(arg, world)
|
16
|
+
|
17
|
+
date = nil
|
18
|
+
first_error = nil
|
19
|
+
formats = @options.fetch(:formats, DEFAULT_FORMATS)
|
20
|
+
formats.each do |format|
|
13
21
|
begin
|
14
|
-
return date =
|
22
|
+
return date = strptime(arg, format)
|
15
23
|
rescue ArgumentError => ex
|
16
24
|
first_error ||= ex
|
17
25
|
rescue ::Date::Error => ex
|
@@ -19,7 +27,23 @@ module Monolens
|
|
19
27
|
end
|
20
28
|
end
|
21
29
|
|
22
|
-
|
30
|
+
fail!(first_error.message, world)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def strptime(arg, format = nil)
|
36
|
+
parsed = if format.nil?
|
37
|
+
parser.parse(arg)
|
38
|
+
else
|
39
|
+
parser.strptime(arg, format)
|
40
|
+
end
|
41
|
+
parsed = parsed.to_datetime if parsed.respond_to?(:to_datetime)
|
42
|
+
parsed
|
43
|
+
end
|
44
|
+
|
45
|
+
def parser
|
46
|
+
option(:parser, ::DateTime)
|
23
47
|
end
|
24
48
|
end
|
25
49
|
end
|
data/lib/monolens/coerce.rb
CHANGED
@@ -5,16 +5,25 @@ module Monolens
|
|
5
5
|
end
|
6
6
|
module_function :date
|
7
7
|
|
8
|
+
def integer(options = {})
|
9
|
+
Integer.new(options)
|
10
|
+
end
|
11
|
+
module_function :integer
|
12
|
+
|
8
13
|
def datetime(options = {})
|
9
14
|
DateTime.new(options)
|
10
15
|
end
|
11
16
|
module_function :datetime
|
12
17
|
|
18
|
+
def string(options = {})
|
19
|
+
String.new(options)
|
20
|
+
end
|
21
|
+
module_function :string
|
22
|
+
|
13
23
|
Monolens.define_namespace 'coerce', self
|
14
24
|
end
|
15
25
|
end
|
16
|
-
require_relative 'str/strip'
|
17
|
-
require_relative 'str/upcase'
|
18
|
-
require_relative 'str/downcase'
|
19
26
|
require_relative 'coerce/date'
|
20
27
|
require_relative 'coerce/date_time'
|
28
|
+
require_relative 'coerce/integer'
|
29
|
+
require_relative 'coerce/string'
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'date'
|
3
|
+
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Monolens
|
7
|
+
class Command
|
8
|
+
def initialize(argv, stdin, stdout, stderr)
|
9
|
+
@argv = argv
|
10
|
+
@stdin = stdin
|
11
|
+
@stdout = stdout
|
12
|
+
@stderr = stderr
|
13
|
+
@pretty = false
|
14
|
+
end
|
15
|
+
attr_reader :argv, :stdin, :stdout, :stderr
|
16
|
+
attr_reader :pretty
|
17
|
+
|
18
|
+
def self.call(argv, stdin = $stdin, stdout = $stdout, stderr = $stderr)
|
19
|
+
new(argv, stdin, stdout, stderr).call
|
20
|
+
end
|
21
|
+
|
22
|
+
def call
|
23
|
+
lens, input = options.parse!(argv)
|
24
|
+
show_help_and_exit if lens.nil? || input.nil?
|
25
|
+
|
26
|
+
lens, input = read_file(lens), read_file(input)
|
27
|
+
error_handler = ErrorHandler.new
|
28
|
+
lens = Monolens.lens(lens)
|
29
|
+
result = lens.call(input, error_handler: error_handler)
|
30
|
+
|
31
|
+
unless error_handler.empty?
|
32
|
+
stderr.puts(error_handler.report)
|
33
|
+
end
|
34
|
+
|
35
|
+
if result
|
36
|
+
output = if pretty
|
37
|
+
JSON.pretty_generate(result)
|
38
|
+
else
|
39
|
+
result.to_json
|
40
|
+
end
|
41
|
+
|
42
|
+
stdout.puts output
|
43
|
+
end
|
44
|
+
rescue Monolens::LensError => ex
|
45
|
+
stderr.puts("[#{ex.location.join('/')}] #{ex.message}")
|
46
|
+
do_exit(-2)
|
47
|
+
end
|
48
|
+
|
49
|
+
def read_file(file)
|
50
|
+
content = ::File.read(file)
|
51
|
+
case ::File.extname(file)
|
52
|
+
when /json$/ then JSON.parse(content)
|
53
|
+
when /ya?ml$/ then YAML.safe_load(content)
|
54
|
+
else
|
55
|
+
fail!("Unable to use #{file}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def fail!(msg)
|
60
|
+
stderr.puts(msg)
|
61
|
+
do_exit(1)
|
62
|
+
end
|
63
|
+
|
64
|
+
def do_exit(status)
|
65
|
+
exit(status)
|
66
|
+
end
|
67
|
+
|
68
|
+
def show_help_and_exit
|
69
|
+
stdout.puts options
|
70
|
+
do_exit(0)
|
71
|
+
end
|
72
|
+
|
73
|
+
def options
|
74
|
+
@options ||= OptionParser.new do |opts|
|
75
|
+
opts.banner = 'Usage: monolens [options] LENS INPUT'
|
76
|
+
|
77
|
+
opts.on('--version', 'Show version and exit') do
|
78
|
+
stdout.puts "Monolens v#{VERSION} - (c) Enspirit #{Date.today.year}"
|
79
|
+
do_exit(0)
|
80
|
+
end
|
81
|
+
opts.on('-p', '--[no-]pretty', 'Show version and exit') do |pretty|
|
82
|
+
@pretty = pretty
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/monolens/core/chain.rb
CHANGED
@@ -8,12 +8,12 @@ module Monolens
|
|
8
8
|
@lenses = lenses
|
9
9
|
end
|
10
10
|
|
11
|
-
def call(arg,
|
11
|
+
def call(arg, world = {})
|
12
12
|
result = arg
|
13
13
|
@lenses.each do |lens|
|
14
14
|
done = false
|
15
15
|
catch(:skip) do
|
16
|
-
result = lens.call(result,
|
16
|
+
result = lens.call(result, world)
|
17
17
|
done = true
|
18
18
|
end
|
19
19
|
break unless done
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Monolens
|
2
|
+
module Core
|
3
|
+
class Dig
|
4
|
+
include Lens
|
5
|
+
|
6
|
+
def call(arg, world = {})
|
7
|
+
option(:defn, []).inject(arg) do |memo, part|
|
8
|
+
dig_on(part, memo, world)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def path
|
15
|
+
option(:defn, []).join('.')
|
16
|
+
end
|
17
|
+
|
18
|
+
def dig_on(attr, arg, world)
|
19
|
+
if arg.is_a?(::Array)
|
20
|
+
index = attr.to_i
|
21
|
+
on_missing(world) if index >= arg.size
|
22
|
+
arg[index]
|
23
|
+
elsif arg.is_a?(::Hash)
|
24
|
+
actual, value = fetch_on(attr, arg)
|
25
|
+
on_missing(world) unless actual
|
26
|
+
value
|
27
|
+
elsif arg
|
28
|
+
if attr.is_a?(::Integer)
|
29
|
+
is_array!(arg, world)
|
30
|
+
else
|
31
|
+
is_hash!(arg, world)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
on_missing(world)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def on_missing(world)
|
39
|
+
strategy = option(:on_missing, :fail)
|
40
|
+
case strategy.to_sym
|
41
|
+
when :fail
|
42
|
+
fail!("Unable to find #{path}", world)
|
43
|
+
when :null
|
44
|
+
nil
|
45
|
+
else
|
46
|
+
raise Monolens::Error, "Unexpected missing strategy `#{strategy}`"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
private :on_missing
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Monolens
|
2
|
+
module Core
|
3
|
+
class Mapping
|
4
|
+
include Lens
|
5
|
+
|
6
|
+
def call(arg, world = {})
|
7
|
+
option(:values, {}).fetch(arg) do
|
8
|
+
fail!("Unrecognized value `#{arg}`", world) 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,19 @@ module Monolens
|
|
5
5
|
end
|
6
6
|
module_function :chain
|
7
7
|
|
8
|
-
def
|
9
|
-
|
8
|
+
def dig(options)
|
9
|
+
Dig.new(options)
|
10
10
|
end
|
11
|
-
module_function :
|
11
|
+
module_function :dig
|
12
|
+
|
13
|
+
def mapping(options)
|
14
|
+
Mapping.new(options)
|
15
|
+
end
|
16
|
+
module_function :mapping
|
12
17
|
|
13
18
|
Monolens.define_namespace 'core', self
|
14
19
|
end
|
15
20
|
end
|
16
21
|
require_relative 'core/chain'
|
17
|
-
require_relative 'core/
|
22
|
+
require_relative 'core/dig'
|
23
|
+
require_relative 'core/mapping'
|
data/lib/monolens/error.rb
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
module Monolens
|
2
|
-
class Error < StandardError
|
3
|
-
|
2
|
+
class Error < StandardError
|
3
|
+
end
|
4
|
+
class LensError < Error
|
5
|
+
def initialize(message, location = [])
|
6
|
+
super(message)
|
7
|
+
@location = location
|
8
|
+
end
|
9
|
+
attr_reader :location
|
10
|
+
end
|
4
11
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Monolens
|
2
|
+
class ErrorHandler
|
3
|
+
def initialize
|
4
|
+
@errors = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(error)
|
8
|
+
@errors << error
|
9
|
+
end
|
10
|
+
|
11
|
+
def empty?
|
12
|
+
@errors.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def report
|
16
|
+
@errors
|
17
|
+
.map{|err| "[#{err.location.join('/')}] #{err.message}" }
|
18
|
+
.join("\n")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/monolens/file.rb
CHANGED
@@ -2,13 +2,8 @@ module Monolens
|
|
2
2
|
class File
|
3
3
|
include Lens
|
4
4
|
|
5
|
-
def
|
6
|
-
|
7
|
-
options[:lenses] = Monolens.lens(options[:lenses])
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(*args, &bl)
|
11
|
-
options[:lenses].call(*args, &bl)
|
5
|
+
def call(arg, world = {})
|
6
|
+
option(:lenses).call(arg, world)
|
12
7
|
end
|
13
8
|
end
|
14
9
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Monolens
|
2
|
+
module Lens
|
3
|
+
module FetchSupport
|
4
|
+
def fetch_on(attr, arg, default = nil)
|
5
|
+
if arg.key?(attr)
|
6
|
+
[ attr, arg[attr] ]
|
7
|
+
elsif arg.key?(attr_s = attr.to_s)
|
8
|
+
[ attr_s, arg[attr_s] ]
|
9
|
+
elsif arg.key?(attr_sym = attr.to_sym)
|
10
|
+
[ attr_sym, arg[attr_sym] ]
|
11
|
+
elsif default
|
12
|
+
[ attr, default ]
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Monolens
|
2
|
+
module Lens
|
3
|
+
class Options
|
4
|
+
include FetchSupport
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@options = case options
|
8
|
+
when Hash
|
9
|
+
options.dup
|
10
|
+
else
|
11
|
+
{ lenses: options }
|
12
|
+
end
|
13
|
+
actual, lenses = fetch_on(:lenses, @options)
|
14
|
+
@options[actual] = Monolens.lens(lenses) if actual && lenses
|
15
|
+
@options.freeze
|
16
|
+
end
|
17
|
+
attr_reader :options
|
18
|
+
private :options
|
19
|
+
|
20
|
+
NO_DEFAULT = Object.new.freeze
|
21
|
+
|
22
|
+
def fetch(key, default = NO_DEFAULT, on = @options)
|
23
|
+
if on.key?(key)
|
24
|
+
on[key]
|
25
|
+
elsif on.key?(s = key.to_s)
|
26
|
+
on[s]
|
27
|
+
elsif on.key?(sym = key.to_sym)
|
28
|
+
on[sym]
|
29
|
+
elsif default != NO_DEFAULT
|
30
|
+
default
|
31
|
+
else
|
32
|
+
raise Error, "Missing option #{key}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_h
|
37
|
+
@options.dup
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|