key-value-parser 0.0.1
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 +7 -0
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +183 -0
- data/key-value-parser.gemspec +19 -0
- data/lib/key_value_parser.rb +54 -0
- data/test/test_key_value_parser.rb +112 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d2e00095ab20a6984d6ad8a602c5e1c71181a509
|
4
|
+
data.tar.gz: 0dd0fc09982b0d55bfdbabbda65b3eee147d702d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9457a9ebe4c75ce381ef2f0e007827e3cd6ea396f944a7aa5db0037fb7972b21e3c7b22f7c8ca9f0895ab9e52f89d49c210cd08ca69f94407fe27438355c58d4
|
7
|
+
data.tar.gz: 78f7b302139bad8523391c15e603835ef44d6fff3dce1b4eab48080efcfcf2dcdbf156a80319bd1eca7e9dd1d427c6eeb00c67ac2fac2b8490258e7f96fcb063
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2016 Mickael Riga
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
data/README.md
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
Key Value Parser
|
2
|
+
================
|
3
|
+
|
4
|
+
The primary purpose of `KeyValueParser` is to have a
|
5
|
+
simple class to be able to parse command line arguments
|
6
|
+
which are all formated as `key:value`.
|
7
|
+
|
8
|
+
For example you could call a script like this:
|
9
|
+
|
10
|
+
```
|
11
|
+
$ ruby my-script.rb port=3000 host=localhost user=bob
|
12
|
+
```
|
13
|
+
|
14
|
+
Then in your script you would do:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
# my-script.rb
|
18
|
+
|
19
|
+
require 'key_value_parser'
|
20
|
+
|
21
|
+
options = KeyValueParser.new.parse(ARGV)
|
22
|
+
options[:port] == 3000
|
23
|
+
options[:host] == 'localhost'
|
24
|
+
options[:user] == 'bob'
|
25
|
+
```
|
26
|
+
|
27
|
+
Since `KeyValueParser` is quite opinionated, it is absolutely
|
28
|
+
not a replacement for `OptionParser` which aim is to be able to
|
29
|
+
parse Unix style arguments.
|
30
|
+
|
31
|
+
Also `KeyValueParser` is not limited to command line arguments.
|
32
|
+
It can potentially parse any array which is a list of Key/Value
|
33
|
+
strings. For example you can parse a list of HTTP header lines.
|
34
|
+
|
35
|
+
The following sections are list of features in order to understand
|
36
|
+
what `KeyValueParser` can and cannot do.
|
37
|
+
|
38
|
+
Default Values
|
39
|
+
--------------
|
40
|
+
|
41
|
+
When you create the parser instance, you can pass a `Hash` with
|
42
|
+
default values.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
# $ ruby my-script.rb host=www.domain.com
|
46
|
+
parser = KeyValueParser.new host: 'localhost', port: 3000
|
47
|
+
options = parser.parse(ARGV)
|
48
|
+
options[:port] == 3000
|
49
|
+
options[:host] == 'www.domain.com'
|
50
|
+
```
|
51
|
+
|
52
|
+
Typecasted Values
|
53
|
+
-----------------
|
54
|
+
|
55
|
+
As you may have noticed in previous examples, values are
|
56
|
+
typecasted. So far it does typecast integers and booleans.
|
57
|
+
Also if you don't give a value, the parser will interpret
|
58
|
+
it as a flag and set it to `true`.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
# $ ruby myscript.rb port=3000 broadcast=false running
|
62
|
+
options = KeyValueParser.new.parse(ARGV)
|
63
|
+
options[:port] == 3000
|
64
|
+
options[:broadcast] == false
|
65
|
+
options[:running] == true
|
66
|
+
```
|
67
|
+
|
68
|
+
If you don't want to cast values, you can use the second of
|
69
|
+
`KeyValueParse#parse` which is a list of options. Just set
|
70
|
+
`typecast_values` to `false`.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
parser = KeyValueparser.new({}, {typecast_values: false})
|
74
|
+
```
|
75
|
+
|
76
|
+
Typecasting is still very basic and will most likely evolve,
|
77
|
+
but it will move in this direction.
|
78
|
+
|
79
|
+
Normalized Keys
|
80
|
+
---------------
|
81
|
+
|
82
|
+
Keys of the resulting `Hash` are normalized. Dash in the middle
|
83
|
+
of words are replaced by underscores. Double dash in front of a
|
84
|
+
word are also removed. It allows you to have unix style dash
|
85
|
+
arguments.
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
# $ ruby my-script.rb --user-name="bob mould"
|
89
|
+
options = KeyValueParser.new.parse(ARGV)
|
90
|
+
options[:user_name] == 'bob'
|
91
|
+
```
|
92
|
+
|
93
|
+
Because of the way command line arguments are created for `ARGV`
|
94
|
+
you can even surround an argument with quotes in order to have
|
95
|
+
spaces in the arguments value.
|
96
|
+
|
97
|
+
There is nothing yet for unix style single dash arguments.
|
98
|
+
I could remove the dash and treat it like a single letter key,
|
99
|
+
but the true purpose of single letter arguments is to be an
|
100
|
+
alternative to a longer argument name. If you have a simple idea
|
101
|
+
to implement this without too much hassle, please send me a pull
|
102
|
+
request.
|
103
|
+
|
104
|
+
If you don't want the keys to be normalized, there is an option
|
105
|
+
for this.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# $ ruby my-script.rb --user-name=bob
|
109
|
+
parser = KeyValueParser.new({}, {normalize_keys: false})
|
110
|
+
options = parser.parse(ARGV)
|
111
|
+
options['--user-name'] == 'bob'
|
112
|
+
```
|
113
|
+
|
114
|
+
Array Arguments
|
115
|
+
---------------
|
116
|
+
|
117
|
+
You can have array values if you chain more than one `=<value>`
|
118
|
+
after the key. And the values are still typecasted unless you
|
119
|
+
disable it.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
# $ ruby my-script.rb heroes=batman=robin ids=1=2
|
123
|
+
options = KeyValueParser.new.parse(ARGV)
|
124
|
+
options[:heroes] == ['batman', 'robin']
|
125
|
+
options[:ids] == [1, 2]
|
126
|
+
```
|
127
|
+
|
128
|
+
You can also disable this option:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
# $ ruby my-script.rb heroes=batman=robin
|
132
|
+
parser = KeyValueParser.new({}, {array_values: false})
|
133
|
+
options = parser.parse(ARGV)
|
134
|
+
options[:heroes] == 'batman=robin'
|
135
|
+
```
|
136
|
+
|
137
|
+
Please note that so far this is the only way to make an array.
|
138
|
+
Setting multiple times the same value would just result
|
139
|
+
in the key being set to the last value.
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
# $ ruby my-script.rb heroes=batman heroes=robin
|
143
|
+
options = KeyValueParser.new.parse(ARGV)
|
144
|
+
options[:heroes] == 'robin'
|
145
|
+
```
|
146
|
+
|
147
|
+
Parsing More Than ARGV
|
148
|
+
----------------------
|
149
|
+
|
150
|
+
By default, the separator allows you to have an equal sign `=`
|
151
|
+
or a colon `:`. And the regexp allows spaces around the separator.
|
152
|
+
The surrounded spaces are if you want to parse a list of keys/values
|
153
|
+
from a file for example. Let say you have a file containing a
|
154
|
+
key/value per line.
|
155
|
+
|
156
|
+
```
|
157
|
+
user: Bob Mould
|
158
|
+
number: 42
|
159
|
+
```
|
160
|
+
|
161
|
+
Which looks like a subset of YAML.
|
162
|
+
Then you can parse the file for settings.
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
parser = KeyValueParser.new
|
166
|
+
settings = parser.parse File.readlines('settings.conf')
|
167
|
+
```
|
168
|
+
Everything works the same, typecasting, normalizing keys, defaults, arrays...
|
169
|
+
You can also set another separator. Anything which works with
|
170
|
+
`String#split` will do.
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
# settings.conf:
|
174
|
+
# author | Kevin Smith
|
175
|
+
# characters | Jay | Silent Bob
|
176
|
+
|
177
|
+
parser = KeyValueParser.new({stars: 5}, {separator: /\s*\|\s*/})
|
178
|
+
settings = parser.parse File.readlines('settings.conf')
|
179
|
+
settings[:author] == 'Kevin Smith'
|
180
|
+
settings[:characters] == ['Jay', 'Silent Bob']
|
181
|
+
settings[:stars] == 5
|
182
|
+
```
|
183
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
|
3
|
+
s.authors = ["Mickael Riga"]
|
4
|
+
s.email = ["mig@mypeplum.com"]
|
5
|
+
s.homepage = "https://github.com/mig-hub/key-value-parser"
|
6
|
+
s.licenses = ['MIT']
|
7
|
+
|
8
|
+
s.name = 'key-value-parser'
|
9
|
+
s.version = '0.0.1'
|
10
|
+
s.summary = "Key Value Parser"
|
11
|
+
s.description = "KeyValueParser parses arrays of string options like 'machine=coconut'. It is mainly done for parsing ARGV, but can be used for other things."
|
12
|
+
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
s.files = `git ls-files`.split("\n").sort
|
15
|
+
s.test_files = s.files.grep(/^test\//)
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class KeyValueParser
|
2
|
+
|
3
|
+
def initialize defaults={}, options={}
|
4
|
+
@defaults = defaults
|
5
|
+
@options = {
|
6
|
+
separator: /\s*[=:]\s*/,
|
7
|
+
normalize_keys: true,
|
8
|
+
typecast_values: true,
|
9
|
+
array_values: true
|
10
|
+
}.merge(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse kvs
|
14
|
+
@defaults.merge(
|
15
|
+
Hash[
|
16
|
+
kvs.map do |a|
|
17
|
+
k, *v = a.split(@options[:separator], @options[:array_values] ? 0 : 2)
|
18
|
+
if v.size==1
|
19
|
+
v = v[0]
|
20
|
+
elsif v.size==0
|
21
|
+
v = nil
|
22
|
+
end
|
23
|
+
[
|
24
|
+
@options[:normalize_keys] ? normalize_key(k) : k,
|
25
|
+
@options[:typecast_values] ? typecast(v) : v
|
26
|
+
]
|
27
|
+
end
|
28
|
+
]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def normalize_key k
|
35
|
+
k.sub(/^--/,'').gsub(/[\s\-]+/, '_').to_sym
|
36
|
+
end
|
37
|
+
|
38
|
+
def typecast v
|
39
|
+
if v.is_a? Array
|
40
|
+
return v.map{|item| typecast(item)}
|
41
|
+
end
|
42
|
+
if v=='true' or v.nil?
|
43
|
+
v = true
|
44
|
+
elsif v=='false'
|
45
|
+
v = false
|
46
|
+
elsif v=~/^\d+$/
|
47
|
+
v = v.to_i
|
48
|
+
else
|
49
|
+
v
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'key_value_parser'
|
3
|
+
|
4
|
+
class TestKeyValueParser < Minitest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@parser = KeyValueParser.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_parses_kvs
|
11
|
+
kvs = ['user:mig', 'machine:coconut']
|
12
|
+
expected = {user: 'mig', machine: 'coconut'}
|
13
|
+
assert_equal expected, @parser.parse(kvs)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_equal_works_as_separator
|
17
|
+
kvs = ['user=mig', 'machine=coconut']
|
18
|
+
expected = {user: 'mig', machine: 'coconut'}
|
19
|
+
assert_equal expected, @parser.parse(kvs)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_spaces_work_in_separator
|
23
|
+
kvs = ['user= mig', 'machine = coconut']
|
24
|
+
expected = {user: 'mig', machine: 'coconut'}
|
25
|
+
assert_equal expected, @parser.parse(kvs)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_booleans_are_typecasted
|
29
|
+
kvs = ['dead:false', 'running:true']
|
30
|
+
expected = {dead: false, running: true}
|
31
|
+
assert_equal expected, @parser.parse(kvs)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_integers_are_typecasted
|
35
|
+
kvs = ['machine:coconut', 'size:11']
|
36
|
+
expected = {machine: 'coconut', size: 11}
|
37
|
+
assert_equal expected, @parser.parse(kvs)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_nil_value_typecasted_to_true
|
41
|
+
kvs = ['running']
|
42
|
+
expected = {running: true}
|
43
|
+
assert_equal expected, @parser.parse(kvs)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_parser_can_have_defaults
|
47
|
+
kvs = ['user:mig']
|
48
|
+
expected = {user: 'mig', role: 'dev'}
|
49
|
+
parser = KeyValueParser.new user: 'marty', role: 'dev'
|
50
|
+
assert_equal expected, parser.parse(kvs)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_separator_can_be_changed
|
54
|
+
kvs = ['user|mig', 'machine|coconut']
|
55
|
+
expected = {user: 'mig', machine: 'coconut'}
|
56
|
+
wrong_parser = KeyValueParser.new
|
57
|
+
refute_equal expected, wrong_parser.parse(kvs)
|
58
|
+
parser = KeyValueParser.new({}, separator: '|')
|
59
|
+
assert_equal expected, parser.parse(kvs)
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_keys_are_normalized
|
63
|
+
kvs = ['user-name=mig', 'machine_name=coconut', 'why not=yes']
|
64
|
+
expected = {user_name: 'mig', machine_name: 'coconut', why_not: 'yes'}
|
65
|
+
assert_equal expected, @parser.parse(kvs)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_unix_double_dash_keys_are_normalized
|
69
|
+
kvs = ['--user-name=mig', '--running']
|
70
|
+
expected = {user_name: 'mig', running: true}
|
71
|
+
assert_equal expected, @parser.parse(kvs)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_value_is_updated_if_key_is_referenced_multiple_times
|
75
|
+
kvs = ['user:bad', 'user:mig']
|
76
|
+
expected = {user: 'mig'}
|
77
|
+
assert_equal expected, @parser.parse(kvs)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_arrays
|
81
|
+
kvs = ['users:mig:why', 'numbers:3:5', 'request:false:POST:500']
|
82
|
+
expected = {
|
83
|
+
users: ['mig', 'why'],
|
84
|
+
numbers: [3,5],
|
85
|
+
request: [false, 'POST', 500]
|
86
|
+
}
|
87
|
+
assert_equal expected, @parser.parse(kvs)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_normalize_key_can_be_skipped
|
91
|
+
kvs = ['--user-name=mig', '--running']
|
92
|
+
expected = {'--user-name' => 'mig', '--running' => true}
|
93
|
+
parser = KeyValueParser.new({}, {normalize_keys: false})
|
94
|
+
assert_equal expected, parser.parse(kvs)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_typecast_can_be_skipped
|
98
|
+
kvs = ['user:mig', 'size:5', 'crazy:false:maybe', 'dev']
|
99
|
+
expected = {user: 'mig', size: '5', crazy: ['false','maybe'], dev: nil}
|
100
|
+
parser = KeyValueParser.new({}, {typecast_values: false})
|
101
|
+
assert_equal expected, parser.parse(kvs)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_arrays_can_be_avoided
|
105
|
+
kvs = ['users:mig=why', 'size:5:6', 'running']
|
106
|
+
expected = {users: 'mig=why', size: '5:6', running: true}
|
107
|
+
parser = KeyValueParser.new({}, {array_values: false})
|
108
|
+
assert_equal expected, parser.parse(kvs)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: key-value-parser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mickael Riga
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-24 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: KeyValueParser parses arrays of string options like 'machine=coconut'.
|
14
|
+
It is mainly done for parsing ARGV, but can be used for other things.
|
15
|
+
email:
|
16
|
+
- mig@mypeplum.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".gitignore"
|
22
|
+
- Gemfile
|
23
|
+
- LICENSE
|
24
|
+
- README.md
|
25
|
+
- key-value-parser.gemspec
|
26
|
+
- lib/key_value_parser.rb
|
27
|
+
- test/test_key_value_parser.rb
|
28
|
+
homepage: https://github.com/mig-hub/key-value-parser
|
29
|
+
licenses:
|
30
|
+
- MIT
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.4.5.1
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: Key Value Parser
|
52
|
+
test_files:
|
53
|
+
- test/test_key_value_parser.rb
|