monolens 0.1.0 → 0.4.0
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 +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
|