opto 1.8.7 → 1.9.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/.travis.yml +11 -8
- data/README.md +75 -8
- data/lib/opto/group.rb +55 -20
- data/lib/opto/option.rb +53 -14
- data/lib/opto/resolver.rb +0 -22
- data/lib/opto/resolvers/condition.rb +0 -4
- data/lib/opto/resolvers/evaluate.rb +1 -5
- data/lib/opto/resolvers/interpolate.rb +8 -12
- data/lib/opto/resolvers/yaml.rb +45 -0
- data/lib/opto/types/group.rb +28 -0
- data/lib/opto/version.rb +1 -1
- data/opto.gemspec +2 -2
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8d5546d31ee73e05adce64402dca900e55ef7159fcb277327fedf8bee0db2d00
|
4
|
+
data.tar.gz: 8dcaaf5040b306b4997f01124f2597a500c230f0a32d2ca5071c9d80e2bb536c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 683dd71495e9ea808933e58416d81fb8ebc815ec0b34834bf14e4d4a7595ce478417287369184928033b6b309ff89797a39a4da0418f5f324a314b09308e7fe0
|
7
|
+
data.tar.gz: b4012a61a1a01b1a0ba7ef545956435939d329cd60400f37966b700a4ccc7e0f10c7223b19dd1a985a6383f5ce1b2d399c9914a411cdeb2d15b22597e4a383c9
|
data/.travis.yml
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.
|
4
|
-
- 2.
|
5
|
-
- 2.
|
6
|
-
- 2.4.1
|
3
|
+
- 2.3
|
4
|
+
- 2.4
|
5
|
+
- 2.5
|
7
6
|
env:
|
8
7
|
- secure: "bCu1bVzr6SP+gAbMo88GvcHHbhO3LcnJtWp+YuSZ5qW4EHhdBw+DOQeOlzoKHtYhNhptmb/65pZZctRTE6jOOSZzpOUHaeqaqHZpELldkCMu7rE8bXuCV4OfhG0CKWvURS0x5p5F5onEx1a1sjiu+MVEUqktAPDAcdTBNBW1irRwvnSifgjcZRLzfm1aRs1fqzBJpHaAXyD++2GQaWyXDtQsEsc/Eyhmnkac5Y2cTeQiYlDluqBeB885q9K16ruLp2NWx/rnR8RGMP1LSnrye/GEo2mRWOWy1MyIEwNDcVaA6MVDY2GBIQUoXg4PBrZAleVIsO3LzwR4oorFCaSgWxzqYI67g5Kb2zn8fW9Yu5lDcFEJKIHQAwBrlq3n3Mi5it/IxGQDWXQ/2fjBxCwuilvI0WTzOH0m4g8Uf6jHaQLFC6ahdWDjfs6aCzdPpbJls26/r39NIB5k/6ZO29NZkxGBsQ7E7m4wurr7a9ksLODgPOyPiB8/4Txu35Llp+IuJpbshrlkRIMwEIFVWiLi6MZhRBkxaRzlkJQ0bS3RD9W4z9UJyoBujmgXMrv0eHNTGCOC+/4tvrA+pXzJwmFQJA6TnenoDl9hc6o03gyfH/YvmMgrtJLbJ8rGoLRaY8HsQwHWgimpCl1o043kO8/UkhhS7J4wyQXYwqGnA2PC/j0="
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
bundler_args: --with test development --jobs=3 --retry=3 --path=${BUNDLE_PATH:-vendor/bundle}
|
9
|
+
before_install:
|
10
|
+
- gem update --system
|
11
|
+
- gem install bundler
|
12
|
+
script: bundle exec rspec spec/
|
13
|
+
cache:
|
14
|
+
bundler: true
|
12
15
|
deploy:
|
13
16
|
provider: rubygems
|
14
17
|
api_key: $GEM_TOKEN
|
15
18
|
gem: opto
|
16
19
|
on:
|
17
20
|
tags: true
|
18
|
-
rvm: 2.
|
21
|
+
rvm: 2.5
|
data/README.md
CHANGED
@@ -45,10 +45,12 @@ require 'opto'
|
|
45
45
|
foo_username:
|
46
46
|
type: string
|
47
47
|
required: true
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
validate:
|
49
|
+
min_length: 1
|
50
|
+
max_length: 30
|
51
|
+
transform:
|
52
|
+
- strip # remove leading / trailing whitespace
|
53
|
+
- upcase # make UPCASE
|
52
54
|
from:
|
53
55
|
env: FOO_USER # read value from ENV variable FOO_USER
|
54
56
|
```
|
@@ -72,16 +74,18 @@ require 'opto'
|
|
72
74
|
foo_instances:
|
73
75
|
type: integer
|
74
76
|
default: 1
|
75
|
-
|
76
|
-
|
77
|
+
validate:
|
78
|
+
min: 1
|
79
|
+
max: 30
|
77
80
|
```
|
78
81
|
|
79
82
|
```yaml
|
80
83
|
# Uri validator
|
81
84
|
host_url:
|
82
85
|
type: uri
|
83
|
-
|
84
|
-
|
86
|
+
validate:
|
87
|
+
schemes:
|
88
|
+
- file # only allow file:/// uris
|
85
89
|
```
|
86
90
|
|
87
91
|
## Resolvers
|
@@ -343,6 +347,14 @@ end
|
|
343
347
|
from: prompter
|
344
348
|
```
|
345
349
|
|
350
|
+
You can also use procs:
|
351
|
+
|
352
|
+
```ruby
|
353
|
+
group = Opto::Group.new(
|
354
|
+
resolvers: { prompt: proc { |hint, option| print "Enter #{hint} (default: #{option.default}): "; gets }
|
355
|
+
)
|
356
|
+
```
|
357
|
+
|
346
358
|
## Subclassing a predefined type handler, setter, etc
|
347
359
|
|
348
360
|
```ruby
|
@@ -459,6 +471,23 @@ Global validations:
|
|
459
471
|
}
|
460
472
|
```
|
461
473
|
|
474
|
+
### group
|
475
|
+
|
476
|
+
Allows nesting of Opto::Groups:
|
477
|
+
|
478
|
+
```yaml
|
479
|
+
subgroup:
|
480
|
+
type: group
|
481
|
+
value:
|
482
|
+
subvariable:
|
483
|
+
type: string
|
484
|
+
value: world
|
485
|
+
greeting:
|
486
|
+
type: string
|
487
|
+
from:
|
488
|
+
interpolate: hello, ${subgroup.subvariable} # becomes hello, world
|
489
|
+
```
|
490
|
+
|
462
491
|
## Default resolvers
|
463
492
|
Hint is the value that gets passed to the resolver when doing for example: `env: FOO` (FOO is the hint)
|
464
493
|
|
@@ -598,6 +627,44 @@ When an "else" is not defined and none of the conditions match, a null value wil
|
|
598
627
|
|
599
628
|
The syntax for conditionals and complex conditionals is documented above in the chapter about conditionals.
|
600
629
|
|
630
|
+
### yaml
|
631
|
+
|
632
|
+
Hint is a hash defining filename or variable containing YAML source and optionally a key
|
633
|
+
|
634
|
+
Example:
|
635
|
+
```yaml
|
636
|
+
# Read a string value from a key in YAML file
|
637
|
+
str:
|
638
|
+
type: string
|
639
|
+
from:
|
640
|
+
yaml:
|
641
|
+
file: .env
|
642
|
+
key: STR
|
643
|
+
|
644
|
+
# Read an array from a nested key in a YAML file
|
645
|
+
str2:
|
646
|
+
type: array
|
647
|
+
from:
|
648
|
+
yaml:
|
649
|
+
file: variables.yml
|
650
|
+
key: variables.str2.value # assuming { variables: { str2: { value: ["abcd", "defg"] } } }
|
651
|
+
|
652
|
+
# Read a YAML file into a string
|
653
|
+
yaml_content:
|
654
|
+
type: string
|
655
|
+
from:
|
656
|
+
file: /etc/config.yml
|
657
|
+
|
658
|
+
# Read YAML content from a variable and fetch a key from it
|
659
|
+
str3:
|
660
|
+
type: string
|
661
|
+
from:
|
662
|
+
yaml:
|
663
|
+
variable: yaml_content
|
664
|
+
key: settings.cpu_arch
|
665
|
+
```
|
666
|
+
|
667
|
+
|
601
668
|
## Default setters
|
602
669
|
|
603
670
|
### env
|
data/lib/opto/group.rb
CHANGED
@@ -11,35 +11,52 @@ module Opto
|
|
11
11
|
|
12
12
|
using Opto::Extension::HashStringOrSymbolKey
|
13
13
|
|
14
|
-
attr_reader :options, :defaults
|
15
|
-
|
16
14
|
extend Forwardable
|
17
15
|
|
18
16
|
# Initialize a new Option Group. You can also pass in :defaults.
|
19
17
|
#
|
20
18
|
# @param [Array<Hash,Opto::Option>,Hash,NilClass] opts An array of Option definition hashes or Option objects or a hash like { var_name: { opts } }.
|
21
19
|
# @return [Opto::Group]
|
22
|
-
def initialize(*
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
raise TypeError, "Invalid type #{options.first.class} for Opto::Group.new"
|
20
|
+
def initialize(*opts)
|
21
|
+
case opts.first
|
22
|
+
when NilClass
|
23
|
+
when Hash
|
24
|
+
defaults.merge!(opts.first.delete(:defaults)) if opts.first.key?(:defaults)
|
25
|
+
setters.merge!(opts.first.delete(:setters)) if opts.first.key?(:setters)
|
26
|
+
resolvers.merge!(opts.first.delete(:resolvers)) if opts.first.key?(:resolvers)
|
27
|
+
options.concat(opts.first.map {|k,v| Option.new({name: k.to_s, group: self}.merge(v))})
|
28
|
+
when ::Array
|
29
|
+
if opts.last.is_a?(Hash) && !opts.last.key?(:type)
|
30
|
+
opts.pop.tap do |settings|
|
31
|
+
defaults.merge!(settings[:defaults]) if settings.key?(:defaults)
|
32
|
+
setters.merge!(settings[:setters]) if settings.key?(:setters)
|
33
|
+
resolvers.merge!(settings[:resolvers]) if settings.key?(:resolvers)
|
37
34
|
end
|
35
|
+
end
|
36
|
+
if opts.first.kind_of?(Array)
|
37
|
+
options.concat(opts.first.map { |opt| opt.kind_of?(Opto::Option) ? opt : Option.new(opt.merge(group: self)) })
|
38
|
+
end
|
38
39
|
else
|
39
|
-
|
40
|
+
raise TypeError, "Invalid type #{opts.class} for Opto::Group.new"
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
44
|
+
def options
|
45
|
+
@options ||= []
|
46
|
+
end
|
47
|
+
|
48
|
+
def setters
|
49
|
+
@setters ||= {}
|
50
|
+
end
|
51
|
+
|
52
|
+
def resolvers
|
53
|
+
@resolvers ||= {}
|
54
|
+
end
|
55
|
+
|
56
|
+
def defaults
|
57
|
+
@defaults ||= {}
|
58
|
+
end
|
59
|
+
|
43
60
|
# Are all options valid? (Option value passes validation)
|
44
61
|
# @return [Boolean]
|
45
62
|
def valid?
|
@@ -91,7 +108,25 @@ module Opto
|
|
91
108
|
# @param [String] option_name
|
92
109
|
# @return [Opto::Option]
|
93
110
|
def option(option_name)
|
94
|
-
|
111
|
+
if option_name.to_s.include?('.')
|
112
|
+
parts = option_name.to_s.split('.')
|
113
|
+
var_name = parts.pop
|
114
|
+
group = parts.inject(self) do |base, part|
|
115
|
+
grp = base.option(part).value
|
116
|
+
if grp.nil?
|
117
|
+
raise NameError, "No such group: #{base.name}.#{part}"
|
118
|
+
elsif grp.kind_of?(Opto::Group)
|
119
|
+
grp
|
120
|
+
else
|
121
|
+
raise TypeError, "Is not a group: #{base.name}.#{part}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
group = self
|
126
|
+
var_name = option_name
|
127
|
+
end
|
128
|
+
|
129
|
+
group.options.find { |opt| opt.name == var_name }
|
95
130
|
end
|
96
131
|
|
97
132
|
# Get a value of a member by option name
|
@@ -180,6 +215,6 @@ module Opto
|
|
180
215
|
end
|
181
216
|
end
|
182
217
|
|
183
|
-
def_delegators
|
218
|
+
def_delegators :options, *(::Array.instance_methods - [:__send__, :object_id, :to_h, :to_a, :is_a?, :kind_of?, :instance_of?, :self, :inspect, :nil?])
|
184
219
|
end
|
185
220
|
end
|
data/lib/opto/option.rb
CHANGED
@@ -86,12 +86,27 @@ module Opto
|
|
86
86
|
@only_if = opts.delete(:only_if)
|
87
87
|
@from = normalize_from_to(opts.delete(:from))
|
88
88
|
@to = normalize_from_to(opts.delete(:to))
|
89
|
-
|
89
|
+
validations = opts.delete(:validate).to_h
|
90
|
+
transforms = opts.delete(:transform)
|
91
|
+
transforms = case transforms
|
92
|
+
when NilClass then {}
|
93
|
+
when Hash then transforms
|
94
|
+
when Array then
|
95
|
+
transforms.each_with_object({}) { |t, hash| hash[t] = true }
|
96
|
+
else
|
97
|
+
raise TypeError, 'Transform has to be a hash or an array'
|
98
|
+
end
|
99
|
+
@type_options = opts.merge(validations).merge(transforms)
|
100
|
+
@tried_resolve = false
|
90
101
|
|
91
102
|
set_initial(val) if val
|
92
103
|
deep_merge_defaults
|
93
104
|
end
|
94
105
|
|
106
|
+
def has_group?
|
107
|
+
!group.nil?
|
108
|
+
end
|
109
|
+
|
95
110
|
def deep_merge_defaults
|
96
111
|
return nil unless group && group.defaults
|
97
112
|
if group.defaults[:from]
|
@@ -140,7 +155,7 @@ module Opto
|
|
140
155
|
# Returns true if this field should not be processed because of the conditionals
|
141
156
|
# @return [Boolean]
|
142
157
|
def skip?
|
143
|
-
return false
|
158
|
+
return false unless has_group?
|
144
159
|
return true if group.any_true?(skip_if)
|
145
160
|
return true unless group.all_true?(only_if)
|
146
161
|
false
|
@@ -150,7 +165,8 @@ module Opto
|
|
150
165
|
# @param [String] option_name
|
151
166
|
def value_of(option_name)
|
152
167
|
return value if option_name == self.name
|
153
|
-
|
168
|
+
return nil unless has_group?
|
169
|
+
group.value_of(option_name)
|
154
170
|
end
|
155
171
|
|
156
172
|
# Run validators
|
@@ -181,11 +197,11 @@ module Opto
|
|
181
197
|
# Accessor to defined resolvers for this option.
|
182
198
|
# @return [Array<Opto::Resolver>]
|
183
199
|
def resolvers
|
184
|
-
@resolvers ||= from.merge(default: self).map { |origin, hint| Resolver.for(origin)
|
200
|
+
@resolvers ||= from.merge(default: self).map { |origin, hint| { origin: origin, hint: hint, resolver: ((has_group? && group.resolvers[origin]) || Resolver.for(origin)) } }
|
185
201
|
end
|
186
202
|
|
187
203
|
def setters
|
188
|
-
@setters ||= to.map { |target, hint| Setter.for(target)
|
204
|
+
@setters ||= to.map { |target, hint| { target: target, hint: hint, setter: ((has_group? && group.setters[target]) || Setter.for(target)) } }
|
189
205
|
end
|
190
206
|
|
191
207
|
# True if this field is defined as required: true
|
@@ -194,32 +210,55 @@ module Opto
|
|
194
210
|
handler.required?
|
195
211
|
end
|
196
212
|
|
213
|
+
def tried_resolve?
|
214
|
+
@tried_resolve
|
215
|
+
end
|
216
|
+
|
217
|
+
def set_tried
|
218
|
+
@tried_resolve = true
|
219
|
+
end
|
220
|
+
|
221
|
+
def unset_tried!
|
222
|
+
@tried_resolve = false
|
223
|
+
end
|
224
|
+
|
197
225
|
# Run resolvers
|
198
226
|
# @raise [TypeError, ArgumentError]
|
199
227
|
def resolve
|
200
|
-
|
228
|
+
return nil if tried_resolve?
|
229
|
+
resolvers.each do |resolver_config|
|
201
230
|
begin
|
202
|
-
|
231
|
+
resolver = resolver_config[:resolver]
|
232
|
+
if resolver.respond_to?(:call)
|
233
|
+
result = resolver.call(resolver_config[:hint], self)
|
234
|
+
else
|
235
|
+
result = resolver.new(resolver_config[:hint], self).resolve
|
236
|
+
end
|
203
237
|
rescue StandardError => ex
|
204
|
-
raise ex, "Resolver '#{
|
238
|
+
raise ex, "Resolver '#{resolver_config[:origin]}' for '#{name}' : #{ex.message}"
|
205
239
|
end
|
206
240
|
unless result.nil?
|
207
|
-
@origin =
|
241
|
+
@origin = resolver_config[:origin]
|
208
242
|
return result
|
209
243
|
end
|
210
244
|
end
|
211
245
|
nil
|
246
|
+
ensure
|
247
|
+
set_tried
|
212
248
|
end
|
213
249
|
|
214
250
|
# Run setters
|
215
251
|
def output
|
216
|
-
setters.each do |
|
252
|
+
setters.each do |setter_config|
|
217
253
|
begin
|
218
|
-
setter
|
219
|
-
setter.
|
220
|
-
|
254
|
+
setter = setter_config[:setter]
|
255
|
+
if setter.respond_to?(:call)
|
256
|
+
setter.call(setter_config[:hint], value, self)
|
257
|
+
else
|
258
|
+
setter.new(setter_config[:hint], self).set(value)
|
259
|
+
end
|
221
260
|
rescue StandardError => ex
|
222
|
-
raise ex, "Setter '#{
|
261
|
+
raise ex, "Setter '#{setter_config[:target]}' for '#{name}' : #{ex.message}"
|
223
262
|
end
|
224
263
|
end
|
225
264
|
end
|
data/lib/opto/resolver.rb
CHANGED
@@ -43,28 +43,6 @@ module Opto
|
|
43
43
|
def initialize(hint = nil, option = nil)
|
44
44
|
@hint = hint
|
45
45
|
@option = option
|
46
|
-
@tried = false
|
47
|
-
end
|
48
|
-
|
49
|
-
def tried?
|
50
|
-
!!@tried
|
51
|
-
end
|
52
|
-
|
53
|
-
def set_tried
|
54
|
-
@tried = true
|
55
|
-
end
|
56
|
-
|
57
|
-
def reset_tried
|
58
|
-
@tried = false
|
59
|
-
end
|
60
|
-
|
61
|
-
def try_resolve
|
62
|
-
return nil if tried?
|
63
|
-
set_tried
|
64
|
-
self.respond_to?(:before) && self.before
|
65
|
-
result = resolve
|
66
|
-
self.respond_to?(:after) && self.after
|
67
|
-
result
|
68
46
|
end
|
69
47
|
|
70
48
|
# This is a "base" class, you're supposed to inherit from this in your resolver and define a #resolve method.
|
@@ -10,7 +10,7 @@ module Opto
|
|
10
10
|
|
11
11
|
def resolve
|
12
12
|
raise TypeError, "String required" unless hint.kind_of?(String)
|
13
|
-
interpolated_hint = hint.gsub(/(?<!\$)\$(?!\$)\{
|
13
|
+
interpolated_hint = hint.gsub(/(?<!\$)\$(?!\$)\{?[\w\.]+\}?/) do |v|
|
14
14
|
var = v.tr('${}', '')
|
15
15
|
if option.group.nil? || option.group.option(var).nil?
|
16
16
|
raise RuntimeError, "Variable #{var} not declared"
|
@@ -27,10 +27,6 @@ module Opto
|
|
27
27
|
raise TypeError, "Syntax error: '#{interpolated_hint}' does not look like a number or a calculation"
|
28
28
|
end
|
29
29
|
end
|
30
|
-
|
31
|
-
def after
|
32
|
-
reset_tried
|
33
|
-
end
|
34
30
|
end
|
35
31
|
end
|
36
32
|
end
|
@@ -14,20 +14,16 @@ module Opto
|
|
14
14
|
|
15
15
|
def resolve
|
16
16
|
raise TypeError, "String expected" unless hint.kind_of?(String)
|
17
|
-
hint.gsub(/(?<!\$)\$(?!\$)\{
|
17
|
+
hint.gsub(/(?<!\$)\$(?!\$)\{?[\w\.]+\}?/) do |v|
|
18
18
|
var = v.tr('${}', '')
|
19
|
-
if option.group.nil? || option.group.option(var).nil?
|
20
|
-
raise RuntimeError, "Variable #{var} not declared"
|
21
|
-
end
|
22
|
-
if option.value_of(var).nil?
|
23
|
-
raise RuntimeError, "No value for #{var}, note that the order is meaningful"
|
24
|
-
end
|
25
|
-
option.value_of(var)
|
26
|
-
end
|
27
|
-
end
|
28
19
|
|
29
|
-
|
30
|
-
|
20
|
+
raise RuntimeError, "Variable #{var} not declared" if option.group.nil?
|
21
|
+
opt = option.group.option(var)
|
22
|
+
raise RuntimeError, "Variable #{var} not declared" if opt.nil?
|
23
|
+
value = opt.value
|
24
|
+
raise RuntimeError, "No value for #{var}, note that the order is meaningful" if value.nil?
|
25
|
+
value
|
26
|
+
end
|
31
27
|
end
|
32
28
|
end
|
33
29
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'opto/extensions/snake_case'
|
2
|
+
require 'opto/extensions/hash_string_or_symbol_key'
|
3
|
+
|
4
|
+
module Opto
|
5
|
+
module Resolvers
|
6
|
+
# Loads values from YAML files
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
# from:
|
10
|
+
# yaml:
|
11
|
+
# file: foofoo.yml
|
12
|
+
# key: foo
|
13
|
+
class Yaml < Opto::Resolver
|
14
|
+
|
15
|
+
using Opto::Extension::HashStringOrSymbolKey
|
16
|
+
|
17
|
+
def resolve
|
18
|
+
raise TypeError, "Hash expected" unless hint.kind_of?(Hash)
|
19
|
+
|
20
|
+
require 'yaml' unless Kernel.const_defined?(:YAML)
|
21
|
+
|
22
|
+
if hint[:file]
|
23
|
+
yaml = YAML.safe_load(::File.read(hint[:file]), [], [], true, hint[:file])
|
24
|
+
elsif hint[:variable]
|
25
|
+
raise TypeError, "Option not in a group" unless option.has_group?
|
26
|
+
other_opt = option.group.option(hint[:variable])
|
27
|
+
raise ArgumentError, "No such option: #{hint[:variable]}" if other_opt.nil?
|
28
|
+
yaml = YAML.safe_load(other_opt.value.to_s, [], [], true, hint[:variable])
|
29
|
+
else
|
30
|
+
raise TypeError, "Missing file/variable definition"
|
31
|
+
end
|
32
|
+
if hint[:key]
|
33
|
+
raise TypeError, "Source is not a hash" unless yaml.kind_of?(Hash)
|
34
|
+
if yaml.key?(hint[:key])
|
35
|
+
yaml[hint[:key]]
|
36
|
+
elsif hint[:key].include?('.')
|
37
|
+
yaml.dig(*hint[:key].split('.'))
|
38
|
+
end
|
39
|
+
else
|
40
|
+
yaml
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative '../type'
|
2
|
+
|
3
|
+
module Opto
|
4
|
+
module Types
|
5
|
+
# A subgroup
|
6
|
+
class Group < Opto::Type
|
7
|
+
using Opto::Extension::HashStringOrSymbolKey
|
8
|
+
|
9
|
+
true_when do |value|
|
10
|
+
value.kind_of?(Opto::Group) && !value.empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
sanitizer :init do |value|
|
14
|
+
if options[:variables]
|
15
|
+
Opto::Group.new(options[:variables])
|
16
|
+
elsif value.kind_of?(::Hash) || value.kind_of?(::Array)
|
17
|
+
Opto::Group.new(value)
|
18
|
+
elsif value.kind_of?(Opto::Group)
|
19
|
+
value
|
20
|
+
elsif value.nil?
|
21
|
+
Opto::Group.new
|
22
|
+
else
|
23
|
+
raise TypeError, "Invalid type #{value.class.name} for a group"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/opto/version.rb
CHANGED
data/opto.gemspec
CHANGED
@@ -18,9 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.required_ruby_version = ">= 2.
|
21
|
+
spec.required_ruby_version = ">= 2.3.0"
|
22
22
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.12"
|
24
|
-
spec.add_development_dependency "rake", "
|
24
|
+
spec.add_development_dependency "rake", ">= 10.0"
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.0"
|
26
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kimmo Lehto
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,14 +28,14 @@ dependencies:
|
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '10.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
@@ -83,12 +83,14 @@ files:
|
|
83
83
|
- lib/opto/resolvers/random_string.rb
|
84
84
|
- lib/opto/resolvers/random_uuid.rb
|
85
85
|
- lib/opto/resolvers/variable.rb
|
86
|
+
- lib/opto/resolvers/yaml.rb
|
86
87
|
- lib/opto/setter.rb
|
87
88
|
- lib/opto/setters/environment_variable.rb
|
88
89
|
- lib/opto/type.rb
|
89
90
|
- lib/opto/types/array.rb
|
90
91
|
- lib/opto/types/boolean.rb
|
91
92
|
- lib/opto/types/enum.rb
|
93
|
+
- lib/opto/types/group.rb
|
92
94
|
- lib/opto/types/integer.rb
|
93
95
|
- lib/opto/types/string.rb
|
94
96
|
- lib/opto/types/uri.rb
|
@@ -106,7 +108,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
106
108
|
requirements:
|
107
109
|
- - ">="
|
108
110
|
- !ruby/object:Gem::Version
|
109
|
-
version: 2.
|
111
|
+
version: 2.3.0
|
110
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
113
|
requirements:
|
112
114
|
- - ">="
|
@@ -114,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
116
|
version: '0'
|
115
117
|
requirements: []
|
116
118
|
rubyforge_project:
|
117
|
-
rubygems_version: 2.
|
119
|
+
rubygems_version: 2.7.7
|
118
120
|
signing_key:
|
119
121
|
specification_version: 4
|
120
122
|
summary: Option validator / resolver
|