noms-optconfig 1.5.3
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.
- data/bin/bash-showconfig +53 -0
- data/bin/optconfig.sh +128 -0
- data/bin/ruby-showconfig +50 -0
- data/lib/bashon.rb +346 -0
- data/lib/longopt.rb +239 -0
- data/lib/optconfig/version.rb +5 -0
- data/lib/optconfig.rb +182 -0
- metadata +103 -0
data/bin/bash-showconfig
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2014 Evernote Corp. All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
domain="$1"
|
|
21
|
+
shift 1
|
|
22
|
+
|
|
23
|
+
SCRIPT_VERSION=$(ruby -rubygems -e 'require "optconfig/version"; puts Optconfig::VERSION')
|
|
24
|
+
|
|
25
|
+
echo -n "Using "; which optconfig.sh
|
|
26
|
+
. optconfig.sh
|
|
27
|
+
opt_new_gen $domain "$@"
|
|
28
|
+
|
|
29
|
+
:<<EOF
|
|
30
|
+
=head1 NAME
|
|
31
|
+
|
|
32
|
+
bash_showconfig - Display the standard configuration resulting from given options
|
|
33
|
+
|
|
34
|
+
=head1 SYNOPSIS
|
|
35
|
+
|
|
36
|
+
bash_showconfig domain optspec [options]
|
|
37
|
+
|
|
38
|
+
=head1 DESCRIPTION
|
|
39
|
+
|
|
40
|
+
The standard Optconfig system provides a way to configure program execution
|
|
41
|
+
in the context specified by domain. Optionally the "perl" or "ruby" languages
|
|
42
|
+
can be specified to use those code paths.
|
|
43
|
+
|
|
44
|
+
The optspec is a JSON-serialized option specifier (see L<Optconfig>). Other
|
|
45
|
+
arguments are passed to the optconfig module (perl or ruby) to determine the
|
|
46
|
+
final configuration.
|
|
47
|
+
|
|
48
|
+
=head1 AUTHOR
|
|
49
|
+
|
|
50
|
+
Jeremy Brinkley, E<lt>jbrinkley@evernote.comE<gt>
|
|
51
|
+
|
|
52
|
+
=cut
|
|
53
|
+
EOF
|
data/bin/optconfig.sh
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!bash
|
|
2
|
+
|
|
3
|
+
# Copyright 2013 Proofpoint, Inc. All rights reserved.
|
|
4
|
+
# Copyright 2014 Evernote Corp. All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
# TODO:
|
|
20
|
+
# * --version doesn't really work, calling script continues after printing
|
|
21
|
+
# version.
|
|
22
|
+
# * --help doesn't work (because a) it prints the help on the stdout
|
|
23
|
+
# which is what gets eval'd, and b) it'll never find "-e"
|
|
24
|
+
# opt_new doesn't work, you have to eval it yourself, because otherwise
|
|
25
|
+
# the 'set' command which rearranges the script arguments doesn't work
|
|
26
|
+
# -jdb/20100921
|
|
27
|
+
|
|
28
|
+
opt_new_gen()
|
|
29
|
+
{
|
|
30
|
+
local domain optspec
|
|
31
|
+
domain="$1"
|
|
32
|
+
shift 1
|
|
33
|
+
optspec="$1"
|
|
34
|
+
shift 1
|
|
35
|
+
ruby -rubygems \
|
|
36
|
+
-e 'require "optconfig"' \
|
|
37
|
+
-e 'require "json"' \
|
|
38
|
+
-e 'require "bashon"' \
|
|
39
|
+
-e '$VERSION = "'$SCRIPT_VERSION'"' \
|
|
40
|
+
-e '$0 = "'"$0"'"' \
|
|
41
|
+
-e 'd = ARGV.shift' \
|
|
42
|
+
-e 'opttext = ARGV.shift' \
|
|
43
|
+
-e 'optspec = JSON.load(opttext)' \
|
|
44
|
+
-e 'opt = Optconfig.new(d, optspec)' \
|
|
45
|
+
-e 'puts opt.map { |k,v| v.to_bashon("opt", d, k) }.join(";") + ";" +' \
|
|
46
|
+
-e ' "opt_#{d}_vrb() { local l=\"$1\"; shift 1; test $l -le $(opt_#{d}_verbose) && echo $*; };" + ' \
|
|
47
|
+
-e ' "opt_#{d}_dbg() { local l=\"$1\"; shift 1; test $l -le $(opt_#{d}_debug) && echo \"DBG(#{d}):\" $*; };" + ' \
|
|
48
|
+
-e ' "opt_#{d}_dry() { if opt_#{d}_dry_run; then echo $*; else \"$@\"; fi; };" + ' \
|
|
49
|
+
-e ' "opt_#{d}() { c=\"${1//[^a-zA-Z0-9]/_}\"; shift 1; opt_#{d}_$c \"$@\"; };" + ' \
|
|
50
|
+
-e ' "set -- #{ARGV.map {|a| 39.chr + a + 39.chr}.join(%q( ))}"' \
|
|
51
|
+
"$domain" "$optspec" "$@"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
json2bashon_gen()
|
|
55
|
+
{
|
|
56
|
+
ruby -rubygems \
|
|
57
|
+
-e 'require "bashon"' \
|
|
58
|
+
-e 'require "json"' \
|
|
59
|
+
-e 'name = ARGV.shift' \
|
|
60
|
+
-e 'json_text = ARGV.shift' \
|
|
61
|
+
-e 'obj = JSON.load(json_text)' \
|
|
62
|
+
-e 'puts obj.to_bashon(name)' "$@"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
json2bashon()
|
|
66
|
+
{
|
|
67
|
+
eval `json2bashon_gen "$@"`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
opt_new()
|
|
71
|
+
{
|
|
72
|
+
# This doesn't work, but this is how it should work
|
|
73
|
+
eval `opt_new_gen "$@"`
|
|
74
|
+
echo opt_$1
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
:<<'EOF'
|
|
78
|
+
=head1 NAME
|
|
79
|
+
|
|
80
|
+
optconfig - Bash functions for option parsing
|
|
81
|
+
|
|
82
|
+
=head1 SYNOPSIS
|
|
83
|
+
|
|
84
|
+
. optconfig.sh
|
|
85
|
+
domain=domain
|
|
86
|
+
optspec='{ "force!": false,
|
|
87
|
+
"logfile=s": "/var/log/foo",
|
|
88
|
+
"define=s%": { } }'
|
|
89
|
+
# opt=`opt_new $domain $optspec`
|
|
90
|
+
eval `opt_new_gen $domain "$optspec" "$@"`
|
|
91
|
+
opt=opt_$domain
|
|
92
|
+
|
|
93
|
+
if $opt force
|
|
94
|
+
then
|
|
95
|
+
rm -f $filefoo
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
echo "Message" >>`$opt logfile`
|
|
99
|
+
|
|
100
|
+
for key in `$opt define`
|
|
101
|
+
do
|
|
102
|
+
valfun=`$opt define $key`
|
|
103
|
+
val=`$valfun` # Note this call--all hash values are funs
|
|
104
|
+
echo "$key = $val"
|
|
105
|
+
done
|
|
106
|
+
|
|
107
|
+
=head1 DESCRIPTION
|
|
108
|
+
|
|
109
|
+
This bash "module" implements a common config file and command-line option
|
|
110
|
+
parsing interface, including Optconfig standard options, that is shared with
|
|
111
|
+
the Optconfig Perl module. See that module for details.
|
|
112
|
+
|
|
113
|
+
The initial call is a wrapper around the Ruby optconfig option and config
|
|
114
|
+
parsing library, and the results are serialized using the L<bashon> module.
|
|
115
|
+
|
|
116
|
+
=head1 NOTES
|
|
117
|
+
|
|
118
|
+
Pay careful attention to how L<bashon> serializes values, especially
|
|
119
|
+
collections. In particular the "leaf" value of a collection is always itself a
|
|
120
|
+
function. If the leaf is itself a collection, this will result in many command
|
|
121
|
+
invocations before you reach a simple value.
|
|
122
|
+
|
|
123
|
+
=head1 AUTHOR
|
|
124
|
+
|
|
125
|
+
Jeremy Brinkley, E<lt>jbrinkley@evernote.comE<gt>
|
|
126
|
+
|
|
127
|
+
=cut
|
|
128
|
+
EOF
|
data/bin/ruby-showconfig
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Copyright 2014 Evernote Corp. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
require 'optconfig'
|
|
18
|
+
|
|
19
|
+
$VERSION = Optconfig::VERSION
|
|
20
|
+
|
|
21
|
+
lib_location = $LOADED_FEATURES.find { |f| f =~ /optconfig.rb$/ }
|
|
22
|
+
|
|
23
|
+
puts "Using #{lib_location}"
|
|
24
|
+
|
|
25
|
+
domain = ARGV.shift
|
|
26
|
+
optspec = JSON.load(ARGV.shift)
|
|
27
|
+
opt = Optconfig.new(domain, optspec)
|
|
28
|
+
puts opt.to_json
|
|
29
|
+
|
|
30
|
+
# = NAME
|
|
31
|
+
#
|
|
32
|
+
# showconfig - Display the standard configuration resulting from given options
|
|
33
|
+
#
|
|
34
|
+
# = SYNOPSIS
|
|
35
|
+
#
|
|
36
|
+
# ruby-showconfig domain optspec [options]
|
|
37
|
+
#
|
|
38
|
+
# = DESCRIPTION
|
|
39
|
+
#
|
|
40
|
+
# The standard Optconfig system provides a way to configure program execution
|
|
41
|
+
# in the context specified by domain.
|
|
42
|
+
#
|
|
43
|
+
# The optspec is a JSON-serialized option specifier (see Optconfig). Other
|
|
44
|
+
# arguments are passed to the optconfig module to determine the
|
|
45
|
+
# final configuration.
|
|
46
|
+
#
|
|
47
|
+
# = AUTHOR
|
|
48
|
+
#
|
|
49
|
+
# Jeremy Brinkley, <jbrinkley@evernote.com>
|
|
50
|
+
#
|
data/lib/bashon.rb
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
#!ruby
|
|
2
|
+
# /* Copyright 2013 Proofpoint, Inc. All rights reserved.
|
|
3
|
+
# Copyright 2014 Evernote Corp. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
# */
|
|
17
|
+
|
|
18
|
+
module BashOn
|
|
19
|
+
def name_key(name)
|
|
20
|
+
name.join('_').gsub(/[^a-zA-Z0-9\_]/, '_')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@@manpage = <<EOF
|
|
24
|
+
=head1 NAME
|
|
25
|
+
|
|
26
|
+
bashon - Serialization library for bash object notation
|
|
27
|
+
|
|
28
|
+
=head1 SYNOPSIS
|
|
29
|
+
|
|
30
|
+
require 'bashon'
|
|
31
|
+
|
|
32
|
+
puts var.to_bashon('var')
|
|
33
|
+
|
|
34
|
+
=head1 DESCRIPTION
|
|
35
|
+
|
|
36
|
+
This module enables you to serialize some ruby objects as "bashon" objects
|
|
37
|
+
("bashon meaning bash object notation"). Mostly, this means that bash code is
|
|
38
|
+
emitted that creates functions returning (echoing) the proper value.
|
|
39
|
+
|
|
40
|
+
The name that is passed to the B<to_bashon> method is the name of the function
|
|
41
|
+
that will output the serialized value. It also forms the root of a hierarchy of
|
|
42
|
+
function names that are used for serializing complex (array and hash) types.
|
|
43
|
+
|
|
44
|
+
The output is expected to be evaluated using the L<eval> command in bash.
|
|
45
|
+
|
|
46
|
+
=head2 Types
|
|
47
|
+
|
|
48
|
+
=head3 Nils
|
|
49
|
+
|
|
50
|
+
An unset command is emitted.
|
|
51
|
+
|
|
52
|
+
=head3 Booleans
|
|
53
|
+
|
|
54
|
+
A function is created with the specified name that can be used as a truth
|
|
55
|
+
test. That is, the function exits with a true value when serializing the
|
|
56
|
+
C<true> literal and with a false value when serializing the C<false>
|
|
57
|
+
literal. This enables you to use the resulting functions directly in
|
|
58
|
+
conditionals in bash.
|
|
59
|
+
|
|
60
|
+
=head3 Other simple types
|
|
61
|
+
|
|
62
|
+
A function is created with the specified name that outputs the value of the
|
|
63
|
+
type using B<to_s>.
|
|
64
|
+
|
|
65
|
+
=head3 Complex types
|
|
66
|
+
|
|
67
|
+
The important thing to note about complex types (hashes and arrays) is that
|
|
68
|
+
the values in the collection are always the I<names of functions> that, when
|
|
69
|
+
invoked, will yield a bashon-serialized value. This is especially important
|
|
70
|
+
to remember when the value of a hash entry or array element is itself a
|
|
71
|
+
complex type.
|
|
72
|
+
|
|
73
|
+
=head4 Arrays
|
|
74
|
+
|
|
75
|
+
A function is created with the specified name that outputs a space-separated,
|
|
76
|
+
ordered list of function names. Each function name is also created; when the
|
|
77
|
+
function is invoked, it will yield the value of that element.
|
|
78
|
+
|
|
79
|
+
head4 Hashes
|
|
80
|
+
|
|
81
|
+
A function is created with the specified name that outputs a space-separated
|
|
82
|
+
list of hash keys. When the same function is invoked with a hash key as an
|
|
83
|
+
argument, it outputs the name of a function. That function, when invoked,
|
|
84
|
+
will yield the value of that element.
|
|
85
|
+
|
|
86
|
+
=head1 EXAMPLES
|
|
87
|
+
|
|
88
|
+
In the following example, each example is shown with three blocks: the ruby
|
|
89
|
+
code snippet emitting the serialization, its standard output, and a shell
|
|
90
|
+
session in which the resulting code has been eval'd
|
|
91
|
+
|
|
92
|
+
This shows how a boolean value is serialized:
|
|
93
|
+
|
|
94
|
+
puts true.to_bashon('var')
|
|
95
|
+
|
|
96
|
+
function var { return 0; }
|
|
97
|
+
|
|
98
|
+
$ if var; then echo got it; fi
|
|
99
|
+
got it
|
|
100
|
+
|
|
101
|
+
This shows how a nil value is serialized:
|
|
102
|
+
|
|
103
|
+
puts nil.to_bashon('var')
|
|
104
|
+
|
|
105
|
+
unset var
|
|
106
|
+
|
|
107
|
+
$ if [ -z "$(var)" ]; then echo No var; fi
|
|
108
|
+
No var
|
|
109
|
+
|
|
110
|
+
This shows how a string is serialized and used:
|
|
111
|
+
|
|
112
|
+
puts "catalog data".to_bashon('var')
|
|
113
|
+
|
|
114
|
+
function var { echo catalog data; }
|
|
115
|
+
|
|
116
|
+
$ string=$(var)
|
|
117
|
+
$ echo $string
|
|
118
|
+
catalago data
|
|
119
|
+
|
|
120
|
+
This shows how an array of strings is serialized:
|
|
121
|
+
|
|
122
|
+
puts ["file1", "file2", "file3"].to_bashon('var')
|
|
123
|
+
|
|
124
|
+
function var { echo var_0 var_1 var_2; } ; function var_0 { echo file1; };function var_1 { echo file2; };function var_2 { echo file3; }
|
|
125
|
+
|
|
126
|
+
$ for filefun in $(var); do file=$($filefun); touch $file; done; ls file*
|
|
127
|
+
file1 file2 file3
|
|
128
|
+
|
|
129
|
+
And a hash of strings:
|
|
130
|
+
|
|
131
|
+
var = { "log" => "logfile.txt",
|
|
132
|
+
"err" => "errfile.txt",
|
|
133
|
+
"input" => "data.txt" }
|
|
134
|
+
puts var.to_bashon('var')
|
|
135
|
+
|
|
136
|
+
function var { case "$1" in log) echo var_log;; err) echo var_err;; input) echo var_input;; '') echo log err input;; esac; }; function var_log { echo logfile.txt; };function var_err { echo errfile.txt; };function var_input { echo data.txt; }
|
|
137
|
+
|
|
138
|
+
$ for key in $(var); do valfun=$(var $key); val=$($valfun); echo $key=$val; done
|
|
139
|
+
log=logfile.txt
|
|
140
|
+
err=errfile.txt
|
|
141
|
+
input=data.txt
|
|
142
|
+
$ echo "Error message" >>$($(var err))
|
|
143
|
+
|
|
144
|
+
This shows how to deal with a heterogeneous array:
|
|
145
|
+
|
|
146
|
+
var = ['one', { 'type' => 'number', 'value' => 2 }, false]
|
|
147
|
+
puts var.to_bashon('var')
|
|
148
|
+
|
|
149
|
+
function var { echo var_0 var_1 var_2; } ; function var_0 { echo one; };function var_1 { case "$1" in value) echo var_1_value;; type) echo var_1_type;; '') echo value type;; esac; }; function var_1_value { echo 2; };function var_1_type { echo number; };function var_2 { return 1; }
|
|
150
|
+
|
|
151
|
+
$ for elfun $(var)
|
|
152
|
+
> do
|
|
153
|
+
> $elfun
|
|
154
|
+
|
|
155
|
+
A deeply nested hash:
|
|
156
|
+
|
|
157
|
+
cfg = { "prod" => { "db" => { "host" => "dbhost001",
|
|
158
|
+
"port" => 3389 },
|
|
159
|
+
"url" => "https://api/",
|
|
160
|
+
"log" => { "file" => "/var/log/app.log",
|
|
161
|
+
"debug" => false },
|
|
162
|
+
"notify" => [ "www", "ops" ] },
|
|
163
|
+
"qa" => { "db" => { "host" => "qa02",
|
|
164
|
+
"port" => 18009 },
|
|
165
|
+
"url" => "https://qa02:18008/v2/",
|
|
166
|
+
"log" => { "file" => "~qa/build9/log/app.log",
|
|
167
|
+
"debug" => true },
|
|
168
|
+
"notify" => [ "build", "test" ] }
|
|
169
|
+
}
|
|
170
|
+
puts cfg.to_bashon('cfg')
|
|
171
|
+
|
|
172
|
+
function cfg { case "$1" in prod) echo cfg_prod;; qa) echo cfg_qa;; '') echo prod qa;; esac; }; function cfg_prod { case "$1" in notify) echo cfg_prod_notify;; log) echo cfg_prod_log;; url) echo cfg_prod_url;; db) echo cfg_prod_db;; '') echo notify log url db;; esac; }; function cfg_prod_notify { echo cfg_prod_notify_0 cfg_prod_notify_1; } ; function cfg_prod_notify_0 { echo www; };function cfg_prod_notify_1 { echo ops; };function cfg_prod_log { case "$1" in debug) echo cfg_prod_log_debug;; file) echo cfg_prod_log_file;; '') echo debug file;; esac; }; function cfg_prod_log_debug { return 1; };function cfg_prod_log_file { echo /var/log/app.log; };function cfg_prod_url { echo https://api/; };function cfg_prod_db { case "$1" in port) echo cfg_prod_db_port;; host) echo cfg_prod_db_host;; '') echo port host;; esac; }; function cfg_prod_db_port { echo 3389; };function cfg_prod_db_host { echo dbhost001; };function cfg_qa { case "$1" in notify) echo cfg_qa_notify;; log) echo cfg_qa_log;; url) echo cfg_qa_url;; db) echo cfg_qa_db;; '') echo notify log url db;; esac; }; function cfg_qa_notify { echo cfg_qa_notify_0 cfg_qa_notify_1; } ; function cfg_qa_notify_0 { echo build; };function cfg_qa_notify_1 { echo test; };function cfg_qa_log { case "$1" in debug) echo cfg_qa_log_debug;; file) echo cfg_qa_log_file;; '') echo debug file;; esac; }; function cfg_qa_log_debug { return 0; };function cfg_qa_log_file { echo ~qa/build9/log/app.log; };function cfg_qa_url { echo https://qa02:18008/v2/; };function cfg_qa_db { case "$1" in port) echo cfg_qa_db_port;; host) echo cfg_qa_db_host;; '') echo port host;; esac; }; function cfg_qa_db_port { echo 18009; };function cfg_qa_db_host { echo qa02; }
|
|
173
|
+
|
|
174
|
+
$ env=prod
|
|
175
|
+
$ curl $($(cfg $env) url)/report.sql | \
|
|
176
|
+
> mysql -h $($($(cfg $env) db) host) -p $($($(cfg $env) db) port) | \
|
|
177
|
+
> tee -a $($($(cfg $env) log) file) | \
|
|
178
|
+
> mailx -s "Report" `for m in $($($(cfg $env) notify)); do echo $($m); done`
|
|
179
|
+
$ $($($(cfg $env) log) debug) && echo `date` `whoami`>>$($($(cfg $env) log) file)
|
|
180
|
+
|
|
181
|
+
=head1 BUGS
|
|
182
|
+
|
|
183
|
+
Right now string serialization makes no attempt to quote strings. If the
|
|
184
|
+
string contains a shell metacharacter, results can be unexpected. In fact,
|
|
185
|
+
results can be a bit unexpected anyway. I've tried different quoting schemes
|
|
186
|
+
and they all get real ugly real fast. In fact, things can be unexpected with
|
|
187
|
+
just a run of spaces or a newline.
|
|
188
|
+
|
|
189
|
+
You can't have a space in hash keys, but there's no helpful error message
|
|
190
|
+
until you eval the result. The problem is that it's really hard to return a
|
|
191
|
+
list of things that is useful for a 'for' loop where the list of things might
|
|
192
|
+
contain a space.
|
|
193
|
+
|
|
194
|
+
There's no good way of determining the "type" of something you encounter. You
|
|
195
|
+
can test the output of a function, and if each of the words in it is itself a
|
|
196
|
+
function, it's an array. However, if they're not, there's no way to
|
|
197
|
+
distinguish between a string that has multiple words and a list of hash keys.
|
|
198
|
+
|
|
199
|
+
Possibly arrays should work exactly like hashes, returning a list of indexes
|
|
200
|
+
instead of keys. Right now you can't access an array by index at all.
|
|
201
|
+
|
|
202
|
+
=head1 NOTES
|
|
203
|
+
|
|
204
|
+
Why not use variables instead of functions? I went down that road, but the
|
|
205
|
+
problem is that even bash 4, with its associative arrays, doesn't provide
|
|
206
|
+
enough flexibility to do nested structures, and you run into all kinds of
|
|
207
|
+
quoting difficulties and whatnot when you try to serialize to variables.
|
|
208
|
+
|
|
209
|
+
For example, you can serialize an array of strings in bash like this:
|
|
210
|
+
|
|
211
|
+
declare -a var
|
|
212
|
+
var=([0]=one [1]=two [2]=three)
|
|
213
|
+
|
|
214
|
+
But what if the values in the array are themselves arrays? Or associative
|
|
215
|
+
arrays? Bash can't handle that. Also, all your quoting has to be right in
|
|
216
|
+
order for that assignment to work, which is really difficult to get right
|
|
217
|
+
in a general solution with the possibility of arbitrarily nested structures.
|
|
218
|
+
|
|
219
|
+
Also, I'll note that arrays and associative arrays aren't really that
|
|
220
|
+
convenient in bash. I'm not sure that $(var key1) is really any harder to use
|
|
221
|
+
or read than ${var[key1]}.
|
|
222
|
+
|
|
223
|
+
So, I went with functions, and for consistency's sake serialized everything as
|
|
224
|
+
functions. That way you always know that when you get something back from
|
|
225
|
+
fetching a hash entry, you have to call it, there's no ambiguity about whether
|
|
226
|
+
it's a raw value or a hash.
|
|
227
|
+
|
|
228
|
+
There's another approach to this, which is to serialize everything into some
|
|
229
|
+
kind of text structure and then provide bash functions/commands to operate on
|
|
230
|
+
this blob of text. For example:
|
|
231
|
+
|
|
232
|
+
puts var.to_json
|
|
233
|
+
|
|
234
|
+
var='{"key1":"val1","key2":["val2one","val2two"],"key3":{"subkey1":1,"subkey2":2}}'
|
|
235
|
+
|
|
236
|
+
$ json_get "$var" key1
|
|
237
|
+
val1
|
|
238
|
+
$ json_get "$var" key2
|
|
239
|
+
["val2one","val2two"]
|
|
240
|
+
$ json_get "$var" key2 0
|
|
241
|
+
val2one
|
|
242
|
+
$ json_get "$var" key3
|
|
243
|
+
{"subkey1":1,"subkey2":2}
|
|
244
|
+
$ json_keys "$var"
|
|
245
|
+
key1
|
|
246
|
+
key2
|
|
247
|
+
key3
|
|
248
|
+
$ json_keys "$var" key3
|
|
249
|
+
subkey1
|
|
250
|
+
subkey2
|
|
251
|
+
$ json_keys "$var" key3 subkey2
|
|
252
|
+
2
|
|
253
|
+
|
|
254
|
+
This is viable but I think it's less convenient to use from bash.
|
|
255
|
+
|
|
256
|
+
=head1 AUTHOR
|
|
257
|
+
|
|
258
|
+
Jeremy Brinkley, E<lt>jbrinkley@evernote.comE<gt>
|
|
259
|
+
|
|
260
|
+
=cut
|
|
261
|
+
EOF
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
class Object
|
|
265
|
+
include BashOn
|
|
266
|
+
def to_bashon(*name)
|
|
267
|
+
if name.empty?
|
|
268
|
+
"#{self.to_s}"
|
|
269
|
+
else
|
|
270
|
+
"function #{name_key(name)} { echo #{self.to_bashon()}; }"
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
class TrueClass
|
|
276
|
+
include BashOn
|
|
277
|
+
def to_bashon(*name)
|
|
278
|
+
if name.empty?
|
|
279
|
+
"true"
|
|
280
|
+
else
|
|
281
|
+
"function #{name_key(name)} { return 0; }"
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
class FalseClass
|
|
287
|
+
include BashOn
|
|
288
|
+
def to_bashon(*name)
|
|
289
|
+
if name.empty?
|
|
290
|
+
"false"
|
|
291
|
+
else
|
|
292
|
+
"function #{name_key(name)} { return 1; }"
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
class NilClass
|
|
298
|
+
include BashOn
|
|
299
|
+
def to_bashon(*name)
|
|
300
|
+
if name.empty?
|
|
301
|
+
""
|
|
302
|
+
else
|
|
303
|
+
"unset #{name_key(name)}"
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
class String
|
|
309
|
+
include BashOn
|
|
310
|
+
|
|
311
|
+
def bq
|
|
312
|
+
self.gsub("'", "'\''")
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def to_bashon(*name)
|
|
316
|
+
if name.empty?
|
|
317
|
+
self
|
|
318
|
+
else
|
|
319
|
+
"function #{name_key(name)} { echo '#{self.to_bashon.bq}'; }"
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Enumerable?
|
|
325
|
+
class Array
|
|
326
|
+
include BashOn
|
|
327
|
+
def to_bashon(*name)
|
|
328
|
+
"function #{name_key(name)} { echo " +
|
|
329
|
+
(0 .. (self.nitems-1)).map { |i|
|
|
330
|
+
name_key(name + [i.to_s]) }.join(' ') + "; } " +
|
|
331
|
+
(self.empty? ? ' ' : '; ') +
|
|
332
|
+
(0 .. (self.nitems-1)).map { |i|
|
|
333
|
+
self[i].to_bashon(*(name + [i.to_s])) }.join(';')
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
class Hash
|
|
338
|
+
include BashOn
|
|
339
|
+
def to_bashon(*name)
|
|
340
|
+
"function #{name_key(name)} { case \"$1\" in " +
|
|
341
|
+
self.map { |k, v| "#{k}) echo '#{name_key(name + [k])}';;" }.join(' ') +
|
|
342
|
+
" '') echo '#{self.keys.join(' ')}';;" +
|
|
343
|
+
" esac; }" + (self.empty? ? ' ' : '; ') +
|
|
344
|
+
self.map { |k, v| self[k].to_bashon(*(name + [k])) }.join(';')
|
|
345
|
+
end
|
|
346
|
+
end
|
data/lib/longopt.rb
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!ruby
|
|
2
|
+
# /* Copyright 2013 Proofpoint, Inc. All rights reserved.
|
|
3
|
+
# Copyright 2014 Evernote Corp. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
# */
|
|
17
|
+
|
|
18
|
+
require 'getoptlong'
|
|
19
|
+
|
|
20
|
+
class Longopt < Hash
|
|
21
|
+
|
|
22
|
+
attr_accessor :optspec
|
|
23
|
+
|
|
24
|
+
def Longopt.new_with_args(argv, *optspecs)
|
|
25
|
+
obj = Longopt.allocate
|
|
26
|
+
obj.getopts_with_argv(argv, *optspecs)
|
|
27
|
+
obj
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def getopts_with_args(argv, *optspeclist)
|
|
31
|
+
saveargv = ARGV.dup
|
|
32
|
+
# produces warning
|
|
33
|
+
ARGV.replace(argv)
|
|
34
|
+
getopts(*optspeclist)
|
|
35
|
+
ARGV.replace(saveargv)
|
|
36
|
+
self
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def Longopt.default(opth)
|
|
40
|
+
obj = Longopt.allocate
|
|
41
|
+
obj.merge(opth)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def initialize(*optspecs)
|
|
45
|
+
getopts(*optspecs)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def getopts(*optspecs)
|
|
49
|
+
@optspec = { }
|
|
50
|
+
|
|
51
|
+
if optspecs.size == 1 and optspecs[0].respond_to? :to_ary
|
|
52
|
+
optspecs = optspecs[0]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
optspecs.each do |optspec|
|
|
56
|
+
if m = /^([\w_\-]+)([=:!+].*)/.match(optspec)
|
|
57
|
+
opt = m[1]
|
|
58
|
+
@optspec[opt] = { }
|
|
59
|
+
|
|
60
|
+
act, typ, collect_typ = m[2].split('')
|
|
61
|
+
|
|
62
|
+
case act
|
|
63
|
+
|
|
64
|
+
when '='
|
|
65
|
+
@optspec[opt]['gol-argument-type'] =
|
|
66
|
+
GetoptLong::REQUIRED_ARGUMENT
|
|
67
|
+
@optspec[opt]['type-letter'] = typ
|
|
68
|
+
@optspec[opt]['collection-type'] = collect_typ
|
|
69
|
+
|
|
70
|
+
when ':'
|
|
71
|
+
@optspec[opt]['gol-argument-type'] =
|
|
72
|
+
GetoptLong::OPTIONAL_ARGUMENT
|
|
73
|
+
@optspec[opt]['type-letter'] = typ
|
|
74
|
+
@optspec[opt]['collection-type'] = collect_typ
|
|
75
|
+
|
|
76
|
+
when '!'
|
|
77
|
+
@optspec[opt]['gol-argument-type'] = GetoptLong::NO_ARGUMENT
|
|
78
|
+
@optspec[opt]['type-letter'] = 'b'
|
|
79
|
+
@optspec['no' + opt] = { }
|
|
80
|
+
@optspec['no' + opt]['gol-argument-type'] =
|
|
81
|
+
GetoptLong::NO_ARGUMENT
|
|
82
|
+
|
|
83
|
+
when '+'
|
|
84
|
+
@optspec[opt]['gol-argument-type'] = GetoptLong::NO_ARGUMENT
|
|
85
|
+
@optspec[opt]['type-letter'] = 'a'
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
@optspec[optspec] = { }
|
|
90
|
+
@optspec[optspec]['gol-argument-type'] = GetoptLong::NO_ARGUMENT
|
|
91
|
+
@optspec[optspec]['type-letter'] = 'b'
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
gol_arguments = @optspec.map { |opt, optspec|
|
|
96
|
+
['--' + opt,
|
|
97
|
+
optspec['gol-argument-type']]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
GetoptLong.new(*gol_arguments).each do |k, v|
|
|
101
|
+
k = k.sub(/^--/, '')
|
|
102
|
+
|
|
103
|
+
case k
|
|
104
|
+
|
|
105
|
+
when *booleans
|
|
106
|
+
self[k] = true
|
|
107
|
+
|
|
108
|
+
when *nobooleans
|
|
109
|
+
self[k[2..-1]] = false
|
|
110
|
+
|
|
111
|
+
when *simples
|
|
112
|
+
self[k] = simple_value(k, v)
|
|
113
|
+
|
|
114
|
+
when *lists
|
|
115
|
+
self[k] = [ ] unless key? k
|
|
116
|
+
self[k].push(simple_value(k, v))
|
|
117
|
+
|
|
118
|
+
when *mappings
|
|
119
|
+
self[k] = { } unless key? k
|
|
120
|
+
mkey, mvalue = v.split(/=/, 2)
|
|
121
|
+
self[k][mkey] = simple_value(k, mvalue)
|
|
122
|
+
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def simple_value(opt, optarg)
|
|
129
|
+
case @optspec[opt]['type-letter']
|
|
130
|
+
when 's'
|
|
131
|
+
optarg
|
|
132
|
+
when 'i'
|
|
133
|
+
Integer(optarg)
|
|
134
|
+
when 'f'
|
|
135
|
+
Float(optarg)
|
|
136
|
+
when 'a'
|
|
137
|
+
if key? opt
|
|
138
|
+
rv = self[opt] + 1
|
|
139
|
+
else
|
|
140
|
+
rv = 1
|
|
141
|
+
end
|
|
142
|
+
rv
|
|
143
|
+
else
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def booleans
|
|
149
|
+
@optspec.reject { |k, v| v['type-letter'] != 'b' }.keys
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def nobooleans
|
|
153
|
+
@optspec.reject { |k, v| v['type-letter'] != 'b' }.keys.map { |el| 'no' + el }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def simples
|
|
157
|
+
@optspec.reject { |k, v| ! v['collection-type'].nil? ||
|
|
158
|
+
v['type-letter'].nil? ||
|
|
159
|
+
v['type-letter'] == 'b' }.keys
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def lists
|
|
163
|
+
@optspec.reject { |k, v| v['collection-type'] != '@' }.keys
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def mappings
|
|
167
|
+
@optspec.reject { |k, v| v['collection-type'] != '%' }.keys
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
@@manpage = <<'EOF'
|
|
171
|
+
=head1 NAME
|
|
172
|
+
|
|
173
|
+
Longopt - Convenience class similar to Perl Getopt::Long
|
|
174
|
+
|
|
175
|
+
=head1 SYNOPSIS
|
|
176
|
+
|
|
177
|
+
require 'longoopt'
|
|
178
|
+
|
|
179
|
+
opt = Longopt.new('verbose', 'force!', 'logfile=s',
|
|
180
|
+
'define=s%', 'user=s@')
|
|
181
|
+
|
|
182
|
+
print "Verbose enabled" if opt['verbose']
|
|
183
|
+
|
|
184
|
+
unlink(filefoo) if opt['force']
|
|
185
|
+
|
|
186
|
+
File.open(opt['logfile'], 'w') { |fh| fh << "Log message" }
|
|
187
|
+
|
|
188
|
+
opt['user'].each { |user| notify_user(user) }
|
|
189
|
+
|
|
190
|
+
opt['define'].each { |key, value| puts "#{key} = #{value}" }
|
|
191
|
+
|
|
192
|
+
=head1 DESCRIPTION
|
|
193
|
+
|
|
194
|
+
The Longopt class wraps GetoptLong so that you can use option specifiers
|
|
195
|
+
similar to the Perl Getopt::Long module.
|
|
196
|
+
|
|
197
|
+
Longopt is a Hash subclass.
|
|
198
|
+
|
|
199
|
+
=head2 Class Methods
|
|
200
|
+
|
|
201
|
+
=over 4
|
|
202
|
+
|
|
203
|
+
=item new(*optspecs)
|
|
204
|
+
|
|
205
|
+
Parse ARGV for options and set options according to provided optspecs.
|
|
206
|
+
|
|
207
|
+
=item new_with_args(args, *optspecs)
|
|
208
|
+
|
|
209
|
+
Like new(), but use provided arguments rather than ARGV.
|
|
210
|
+
|
|
211
|
+
=item default(opthash)
|
|
212
|
+
|
|
213
|
+
Create a Longopt object using the provided default option values in the
|
|
214
|
+
opthash. Use getopts(), below, to parse arguments.
|
|
215
|
+
|
|
216
|
+
=back
|
|
217
|
+
|
|
218
|
+
=head2 Object Methods
|
|
219
|
+
|
|
220
|
+
=item getopts(*optspecs)
|
|
221
|
+
|
|
222
|
+
Parse ARGV to set option values according to provided optspecs.
|
|
223
|
+
|
|
224
|
+
=item getopts_with_args(args, *optspecs)
|
|
225
|
+
|
|
226
|
+
Parse args to set option values according to provided optspecs.
|
|
227
|
+
|
|
228
|
+
=back
|
|
229
|
+
|
|
230
|
+
=head1 AUTHOR
|
|
231
|
+
|
|
232
|
+
Jeremy Brinkley, E<lt>jbrinkley@evernote.comE<gt>
|
|
233
|
+
|
|
234
|
+
=cut
|
|
235
|
+
|
|
236
|
+
EOF
|
|
237
|
+
|
|
238
|
+
end
|
|
239
|
+
|
data/lib/optconfig.rb
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!ruby
|
|
2
|
+
# /* Copyright 2013 Proofpoint, Inc. All rights reserved.
|
|
3
|
+
# Copyright 2014 Evernote Corp. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
# */
|
|
17
|
+
|
|
18
|
+
# = NAME
|
|
19
|
+
#
|
|
20
|
+
# Optconfig - Parse options and config files
|
|
21
|
+
#
|
|
22
|
+
# = DESCRIPTION
|
|
23
|
+
#
|
|
24
|
+
# This module implements the Optconfig standardized approach to
|
|
25
|
+
# command-line option parsing and JSON-based configuration file
|
|
26
|
+
# interpretation. See the reference documentation in the
|
|
27
|
+
# Optconfig master distribution for details.
|
|
28
|
+
#
|
|
29
|
+
|
|
30
|
+
require 'rubygems'
|
|
31
|
+
require 'longopt'
|
|
32
|
+
require 'json'
|
|
33
|
+
|
|
34
|
+
class Optconfig < Hash
|
|
35
|
+
|
|
36
|
+
require 'optconfig/version'
|
|
37
|
+
|
|
38
|
+
attr_accessor :domain, :optspec, :config, :default
|
|
39
|
+
|
|
40
|
+
def add_standard_opts(submitted_optspec)
|
|
41
|
+
optspec = submitted_optspec
|
|
42
|
+
standard_opts = {
|
|
43
|
+
'config=s' => nil,
|
|
44
|
+
'debug+' => 0,
|
|
45
|
+
'verbose+' => 0,
|
|
46
|
+
'version' => false,
|
|
47
|
+
'help' => false,
|
|
48
|
+
'dry-run!' => false }
|
|
49
|
+
standard_opts.each_pair do |opt, defval|
|
|
50
|
+
if ! optspec.has_key? opt
|
|
51
|
+
optspec[opt] = defval
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
optspec
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def initialize(domain, submitted_optspec, version=nil)
|
|
58
|
+
@domain = domain
|
|
59
|
+
@optspec = add_standard_opts(submitted_optspec)
|
|
60
|
+
@caller_version = version || $VERSION || 'Unknown version'
|
|
61
|
+
|
|
62
|
+
submitted_optspec.each_pair do |optspec, val|
|
|
63
|
+
opt, dummy = optspec.split(/[\=\+\!]/, 2)
|
|
64
|
+
self[opt] = val
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
cfgfilepath = [ '/usr/local/etc/' + domain + '.conf',
|
|
68
|
+
'/etc/' + domain + '.conf' ]
|
|
69
|
+
|
|
70
|
+
if ENV.has_key? 'HOME' and ! ENV['HOME'].nil?
|
|
71
|
+
cfgfilepath.unshift(ENV['HOME'] + '/.' + domain)
|
|
72
|
+
end
|
|
73
|
+
@config = nil
|
|
74
|
+
|
|
75
|
+
cmdlineopt = Longopt.new(optspec.keys)
|
|
76
|
+
|
|
77
|
+
if cmdlineopt.has_key? 'config'
|
|
78
|
+
@config = cmdlineopt['config']
|
|
79
|
+
raise "File not found: #{cmdlineopt['config']}" unless
|
|
80
|
+
File.exist? cmdlineopt['config']
|
|
81
|
+
read_config(cmdlineopt['config'])
|
|
82
|
+
else
|
|
83
|
+
cfgfilepath.each do |file|
|
|
84
|
+
if File.readable? file
|
|
85
|
+
@config = file
|
|
86
|
+
read_config(file)
|
|
87
|
+
break
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
cmdlineopt.each_pair do |opt, val|
|
|
93
|
+
merge_cmdlineopt(opt, val)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if self.has_key? 'version' and self['version']
|
|
97
|
+
puts @caller_version
|
|
98
|
+
Process.exit(0)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if self.has_key? 'help' and self['help']
|
|
102
|
+
help_pattern = /(?:^=head1 +SYNOPSIS|^# *=+ *SYNOPSIS)(.*?)(?:^=head1|^# *=+)/m
|
|
103
|
+
myscript = File.expand_path($0)
|
|
104
|
+
begin
|
|
105
|
+
script_text = File.open(myscript, 'r') { |fh| fh.read }
|
|
106
|
+
if /This file was generated by RubyGems/.match script_text
|
|
107
|
+
# Well, thank you RubyGems. Now I have to guess where
|
|
108
|
+
# my code ended up.
|
|
109
|
+
#
|
|
110
|
+
# The ruby gems wrapper for these scripts has some
|
|
111
|
+
# special way of restricting versions here; I'm hoping
|
|
112
|
+
# that since that has been done (and I no longer have
|
|
113
|
+
# access to the special first argument that overrides
|
|
114
|
+
# the version) that the fact that the 'gem' call has
|
|
115
|
+
# already been called with that version will help
|
|
116
|
+
# the following work correctly. Because there's no
|
|
117
|
+
# real other alternative.
|
|
118
|
+
#
|
|
119
|
+
# This 'parsing' is really a load of crap. Sure hope
|
|
120
|
+
# Rubygems never changes anything.
|
|
121
|
+
# -jdb/20141010
|
|
122
|
+
if m = /^\s*load +(.*)$/.match(script_text)
|
|
123
|
+
script_file = eval("version = nil\n" + m[1])
|
|
124
|
+
script_text = File.open(script_file, 'r') { |fh| fh.read }
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
m = help_pattern.match(script_text)
|
|
128
|
+
if m
|
|
129
|
+
puts m[1].gsub(/^# ?/m, '').strip
|
|
130
|
+
else
|
|
131
|
+
puts 'No help'
|
|
132
|
+
end
|
|
133
|
+
rescue Errno::ENOENT
|
|
134
|
+
puts "No help (could not search #{myscript})"
|
|
135
|
+
end
|
|
136
|
+
Process.exit(0)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def read_config(file)
|
|
141
|
+
fileconfig = File.open(file) { |fh| JSON.load(fh) }
|
|
142
|
+
fileconfig.each_pair do |opt, val|
|
|
143
|
+
self[opt] = val
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def merge_cmdlineopt(opt, val)
|
|
148
|
+
if self.has_key? opt
|
|
149
|
+
if self[opt].respond_to? :keys
|
|
150
|
+
if val.respond_to? :keys
|
|
151
|
+
# Both hashes, merge
|
|
152
|
+
val.each_pair { |k, v| self[opt][k] = v }
|
|
153
|
+
else
|
|
154
|
+
self[opt] = val
|
|
155
|
+
end
|
|
156
|
+
elsif self[opt].respond_to? :unshift
|
|
157
|
+
if val.respond_to? :each and val.respond_to? :reverse
|
|
158
|
+
val.reverse.each { |v| self[opt].unshift(v) }
|
|
159
|
+
else
|
|
160
|
+
self[opt] = val
|
|
161
|
+
end
|
|
162
|
+
else
|
|
163
|
+
self[opt] = val
|
|
164
|
+
end
|
|
165
|
+
else
|
|
166
|
+
self[opt] = val
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
self[opt]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def vrb(level, *msg)
|
|
173
|
+
puts msg.join("\n") if self['verbose'] >= level
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def dbg(level, *msg)
|
|
177
|
+
if self['debug'] >= level
|
|
178
|
+
puts "DBG(#{@domain}): " + msg.join("DBG(#{@domain}): ")
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: noms-optconfig
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.5.3
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Jeremy Brinkley
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2016-02-03 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: bundler
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '1.7'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '1.7'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: rake
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ~>
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '10.0'
|
|
38
|
+
type: :development
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ~>
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '10.0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: rspec
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
50
|
+
requirements:
|
|
51
|
+
- - ~>
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.11'
|
|
54
|
+
type: :development
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ~>
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '2.11'
|
|
62
|
+
description: Optconfig presents a standardized way to parse configuration files and
|
|
63
|
+
command-line arguments
|
|
64
|
+
email:
|
|
65
|
+
- jbrinkley@evernote.com
|
|
66
|
+
executables:
|
|
67
|
+
- ruby-showconfig
|
|
68
|
+
extensions: []
|
|
69
|
+
extra_rdoc_files: []
|
|
70
|
+
files:
|
|
71
|
+
- lib/optconfig.rb
|
|
72
|
+
- lib/optconfig/version.rb
|
|
73
|
+
- lib/longopt.rb
|
|
74
|
+
- lib/bashon.rb
|
|
75
|
+
- bin/ruby-showconfig
|
|
76
|
+
- bin/optconfig.sh
|
|
77
|
+
- bin/bash-showconfig
|
|
78
|
+
homepage: http://github.com/evernote/optconfig
|
|
79
|
+
licenses:
|
|
80
|
+
- Apache-2
|
|
81
|
+
post_install_message:
|
|
82
|
+
rdoc_options: []
|
|
83
|
+
require_paths:
|
|
84
|
+
- lib
|
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
|
+
none: false
|
|
87
|
+
requirements:
|
|
88
|
+
- - ! '>='
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
|
+
none: false
|
|
93
|
+
requirements:
|
|
94
|
+
- - ! '>='
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
requirements: []
|
|
98
|
+
rubyforge_project:
|
|
99
|
+
rubygems_version: 1.8.23
|
|
100
|
+
signing_key:
|
|
101
|
+
specification_version: 3
|
|
102
|
+
summary: Parse commmand-line options and config files
|
|
103
|
+
test_files: []
|