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.
@@ -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
@@ -0,0 +1,7 @@
1
+ .DS_STORE
2
+ *.swp
3
+ *.sass-cache
4
+ pkg/
5
+ Gemfile.lock
6
+ .bundle/
7
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+ gem 'minitest', '>=5.8.0'
4
+
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
+
@@ -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