help_parser 6.5.0 → 7.0.200907
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 +4 -4
- data/README.md +116 -72
- data/lib/help_parser.rb +1 -1
- data/lib/help_parser/completion.rb +24 -21
- data/lib/help_parser/constants.rb +7 -12
- data/lib/help_parser/exceptions.rb +4 -4
- data/lib/help_parser/k2t2r.rb +5 -2
- data/lib/help_parser/options.rb +14 -10
- data/lib/help_parser/parseh.rb +4 -4
- data/lib/help_parser/validations.rb +10 -8
- metadata +9 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7df1feedeed249286efe5cd5f1e2cc187a91d348eab654d5a6f110292d0c3476
|
4
|
+
data.tar.gz: a3f142a3d2cf4c6bf038b25e3552dfc3f56bfbb3469749386994b0763ccaea4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3df75b2ae4640bc268e86774facbc7f60e74fda4d58c3262022e3a571cb3d716af2be92e7d1652e2ffa5469a1852da4a412e582141f155fe849f37e62d4e4dc
|
7
|
+
data.tar.gz: 447dfb4939662fec618c4c315f104785e1923a6a625b82ed028981a9ff98c35c9049b23586501c8bd06e244f21de8da4cec2c491bf815a17d3c2d38f93495e4e
|
data/README.md
CHANGED
@@ -1,83 +1,127 @@
|
|
1
|
-
# Help Parser
|
1
|
+
# Help Parser VII: Deader
|
2
2
|
|
3
|
+
* [VERSION 7.0.200907](https://github.com/carlosjhr64/Ruby-HelpParser/releases)
|
3
4
|
* [github](https://www.github.com/carlosjhr64/Ruby-HelpParser)
|
4
5
|
* [rubygems](https://rubygems.org/gems/help_parser)
|
5
6
|
|
6
7
|
## DESCRIPTION:
|
7
|
-
Welcome to the best Help Parser of all!
|
8
|
-
Tweeker!
|
9
|
-
Which do you find most helpful?
|
10
|
-
Hard?
|
11
|
-
I prefer easy.
|
12
8
|
|
13
|
-
|
9
|
+
Can't help YOU???
|
10
|
+
You're not the first to say that...
|
11
|
+
|
12
|
+
I will parse your help!
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
14
|
+
## SYNOPSIS:
|
15
|
+
<!-- The following PREVIEW has been approved for ALL PROGRAMMERS by CarlosJHR64.
|
16
|
+
For the README validator that checks against me lying....
|
17
|
+
```ruby
|
18
|
+
unless File.basename($PROGRAM_NAME) == 'deader'
|
19
|
+
# For example's sake say
|
20
|
+
$PROGRAM_NAME = 'deader'
|
21
|
+
# and ARGV is
|
22
|
+
ARGV.concat ["-\-age", "-\-date=2020-09-07", 'invoke', 'the', 'command']
|
23
|
+
# and proceed as if run as:
|
24
|
+
# awesome -\-name=Doe -\-value a b c
|
25
|
+
end
|
26
|
+
```
|
27
|
+
The following gem has been rated
|
28
|
+
| M | Mature |
|
29
|
+
-->
|
30
|
+
|
31
|
+
> Who ever you are, you were meant to find me today...
|
32
|
+
> there is no turning back!
|
33
|
+
> Above all, don't invoke the command!
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
require "help_parser"
|
37
|
+
|
38
|
+
HELP = <<-HELP
|
39
|
+
# <= Hash here, parser skips
|
40
|
+
# HelpParser: Deader command example #
|
41
|
+
Usage:
|
42
|
+
deader :options+ [<args>+]
|
43
|
+
deader [:alternate] <arg=FLOAT>
|
44
|
+
deader literal <arg1=WORD> <arg2=WORD> <arg3=WORD>
|
45
|
+
Options:
|
46
|
+
-v --version \t Give version and quit
|
47
|
+
-h --help \t Give help and quit
|
48
|
+
-s --long \t Short long synonyms
|
49
|
+
--command invoke \t Defaulted
|
50
|
+
--date=DATE \t Typed
|
51
|
+
--age=INTEGER 80 \t Typed and Defaulted
|
52
|
+
-a --all=YN y \t Short, long, typed, and defaulted
|
53
|
+
--to_be
|
54
|
+
--not_to_be
|
55
|
+
Exclusive:
|
56
|
+
to_be not_to_be \t Tells parser these are mutually exclusive keys
|
57
|
+
Inclusive:
|
58
|
+
date age \t Tells parser any of these must include all of these
|
59
|
+
Alternate:
|
60
|
+
--invoke
|
61
|
+
--wut
|
62
|
+
Types:
|
63
|
+
WORD /^[A-Za-z]+$/
|
64
|
+
DATE /^\\d\\d\\d\\d-\\d\\d-\\d\\d$/
|
65
|
+
INTEGER /^\\d+$/
|
66
|
+
FLOAT /^\\d+\\.\\d+$/
|
67
|
+
YN /^[YNyn]$/
|
68
|
+
# <= Hash here, parser breaks out
|
69
|
+
# Notes #
|
70
|
+
Don't invoke the command.
|
71
|
+
HELP
|
72
|
+
|
73
|
+
VERSION = "1.2.3"
|
74
|
+
|
75
|
+
OPTIONS = HelpParser[VERSION, HELP] #~> HelpParser
|
76
|
+
|
77
|
+
# Macros:
|
78
|
+
HelpParser.strings?(:args) # for OPTIONS.args : Array(String) | Nil
|
79
|
+
HelpParser.int?(:age) # for OPTIONS.age? : Integer | Nil
|
80
|
+
HelpParser.float(:arg) # for options.arg : Float
|
81
|
+
HelpParser.string(:arg1, :arg2, :arg3) # for OPTIONS.arg1, etc : String
|
82
|
+
#=> [:arg1, :arg2, :arg3]
|
83
|
+
|
84
|
+
## If run as:
|
85
|
+
## deader --age --date=2020-09-07 invoke the command
|
86
|
+
OPTIONS.age #=> 80
|
87
|
+
OPTIONS.args #=> ["invoke", "the", "command"]
|
88
|
+
OPTIONS.arg? and OPTIONS.arg #=> false
|
89
|
+
```
|
90
|
+
|
91
|
+
YOU HAVE INVOKED THE COMMAND...
|
92
|
+
YOUR HELP BELONGS TO ME!!!
|
93
|
+
|
94
|
+
## Features
|
95
|
+
|
96
|
+
* $DEBUG=true on --debug
|
97
|
+
* $VERBOSE=true on --verbose
|
98
|
+
* -h and --help simultaneously will check help string for errors
|
80
99
|
|
81
100
|
## INSTALL:
|
82
101
|
|
83
102
|
$ sudo gem install help_parser
|
103
|
+
|
104
|
+
## LICENSE:
|
105
|
+
|
106
|
+
(The MIT License)
|
107
|
+
|
108
|
+
Copyright (c) 2020 CarlosJHR64
|
109
|
+
|
110
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
111
|
+
a copy of this software and associated documentation files (the
|
112
|
+
'Software'), to deal in the Software without restriction, including
|
113
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
114
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
115
|
+
permit persons to whom the Software is furnished to do so, subject to
|
116
|
+
the following conditions:
|
117
|
+
|
118
|
+
The above copyright notice and this permission notice shall be
|
119
|
+
included in all copies or substantial portions of the Software.
|
120
|
+
|
121
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
122
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
123
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
124
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
125
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
126
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
127
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/help_parser.rb
CHANGED
@@ -3,28 +3,33 @@ module HelpParser
|
|
3
3
|
def initialize(hash, specs)
|
4
4
|
@hash,@specs = hash,specs
|
5
5
|
@cache = NoDupHash.new
|
6
|
-
usage if @specs.has_key?(USAGE)
|
6
|
+
usage or diagnose if @specs.has_key?(USAGE)
|
7
7
|
pad
|
8
|
-
types
|
8
|
+
types if @specs.has_key?(TYPES)
|
9
9
|
end
|
10
10
|
|
11
|
+
# Which usage does the user's command options match?
|
11
12
|
def usage
|
12
13
|
@specs[USAGE].each do |cmd|
|
13
14
|
begin
|
14
15
|
i = matches(cmd)
|
15
16
|
raise NoMatch unless @hash.size==i
|
16
17
|
@cache.each{|k,v|@hash[k]=v} # Variables
|
17
|
-
return
|
18
|
+
return true # Good! Found matching usage.
|
18
19
|
rescue NoMatch
|
19
20
|
next
|
20
21
|
ensure
|
21
22
|
@cache.clear
|
22
23
|
end
|
23
24
|
end
|
25
|
+
return false # Bad! Did not match any of the expected usage.
|
26
|
+
end
|
24
27
|
|
28
|
+
# Diagnose user's usage.
|
29
|
+
def diagnose
|
25
30
|
dict = {}
|
26
31
|
@specs.each do |k,v|
|
27
|
-
next if [
|
32
|
+
next if RESERVED[k]
|
28
33
|
v.flatten.map{|_|_.scan(/\w+/).first}.each{|_|dict[_]=true}
|
29
34
|
end
|
30
35
|
typos = @hash.keys.select{|k|k.is_a? String and not dict[k]}
|
@@ -34,22 +39,20 @@ module HelpParser
|
|
34
39
|
end
|
35
40
|
|
36
41
|
def types
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
raise UsageError, "--#{key}=#{string} !~ #{type}=#{regex.inspect}" unless string=~regex
|
49
|
-
end
|
50
|
-
else
|
51
|
-
raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
|
42
|
+
t2r,k2t = HelpParser.t2r(@specs),HelpParser.k2t(@specs)
|
43
|
+
@hash.each do |key,value|
|
44
|
+
next unless key.is_a?(String)
|
45
|
+
if type = k2t[key]
|
46
|
+
regex = t2r[type]
|
47
|
+
case value
|
48
|
+
when String
|
49
|
+
raise UsageError, "--#{key}=#{value} !~ #{type}=#{regex.inspect}" unless value=~regex
|
50
|
+
when Array
|
51
|
+
value.each do |string|
|
52
|
+
raise UsageError, "--#{key}=#{string} !~ #{type}=#{regex.inspect}" unless string=~regex
|
52
53
|
end
|
54
|
+
else
|
55
|
+
raise UsageError, "--#{key} !~ #{type}=#{regex.inspect}"
|
53
56
|
end
|
54
57
|
end
|
55
58
|
end
|
@@ -74,7 +77,7 @@ module HelpParser
|
|
74
77
|
elsif value = @hash[long]
|
75
78
|
@hash[short] = true
|
76
79
|
if value==true && !default.nil?
|
77
|
-
@hash.delete(long)
|
80
|
+
@hash.delete(long) # ArgvHash reset
|
78
81
|
@hash[long]=default
|
79
82
|
end
|
80
83
|
end
|
@@ -83,7 +86,7 @@ module HelpParser
|
|
83
86
|
long,default = first[2..(i-1)],second
|
84
87
|
value = @hash[long]
|
85
88
|
if value==true
|
86
|
-
@hash.delete(long)
|
89
|
+
@hash.delete(long) # ArgvHash reset
|
87
90
|
@hash[long] = default
|
88
91
|
end
|
89
92
|
end
|
@@ -1,15 +1,8 @@
|
|
1
1
|
module HelpParser
|
2
|
-
@@validate = false
|
3
|
-
def self.validate!
|
4
|
-
@@validate = true
|
5
|
-
end
|
6
|
-
def self.validate?
|
7
|
-
@@validate
|
8
|
-
end
|
9
|
-
|
10
2
|
USAGE = 'usage'
|
11
3
|
TYPES = 'types'
|
12
4
|
EXCLUSIVE = 'exclusive'
|
5
|
+
INCLUSIVE = 'inclusive'
|
13
6
|
|
14
7
|
# usage
|
15
8
|
FLAG = /^[-][-]?(?<k>\w+)$/
|
@@ -29,7 +22,7 @@ module HelpParser
|
|
29
22
|
TYPE_DEF = /^(?<t>[A-Z]+),?\s+\/(?<r>\S+)\/$/
|
30
23
|
|
31
24
|
# spec w+( w+)+
|
32
|
-
X_DEF = /^\w+( +\w+)+$/
|
25
|
+
X_DEF = /^\w+( +\w+)+$/ # for both exclusive and inclusive specs
|
33
26
|
|
34
27
|
CSV = /,?\s+/
|
35
28
|
|
@@ -42,17 +35,18 @@ module HelpParser
|
|
42
35
|
DUP_KEY = 'Duplicate key'
|
43
36
|
DUP_WORD = 'Duplicate word'
|
44
37
|
DUP_FLAG = 'Duplicate flag'
|
45
|
-
DUP_X = 'Duplicate exclusive spec'
|
38
|
+
DUP_X = 'Duplicate exclusive/inclusive spec'
|
46
39
|
UNSEEN_FLAG = 'Undefined flag'
|
47
40
|
INCONSISTENT = 'Inconsistent use of variable'
|
48
41
|
UNEXPECTED = 'Unexpected string in help text'
|
49
42
|
BAD_REGEX = 'Bad regex'
|
50
43
|
REDUNDANT = 'Redundant'
|
51
44
|
EXCLUSIVE_KEYS = 'Exclusive keys'
|
45
|
+
INCLUSIVE_KEYS = 'Inclusive keys'
|
52
46
|
UNBALANCED = 'Unbalanced brackets'
|
53
47
|
UNRECOGNIZED_TOKEN = 'Unrecognized usage token'
|
54
48
|
UNRECOGNIZED_TYPE = 'Unrecognized type spec'
|
55
|
-
UNRECOGNIZED_X = 'Unrecognized exclusive spec'
|
49
|
+
UNRECOGNIZED_X = 'Unrecognized exclusive/inclusive spec'
|
56
50
|
UNRECOGNIZED_OPTION = 'Unrecognized option spec'
|
57
51
|
UNRECOGNIZED = 'Unrecognized'
|
58
52
|
UNDEFINED_SECTION = 'Section not defined'
|
@@ -74,5 +68,6 @@ module HelpParser
|
|
74
68
|
# lambda utilities
|
75
69
|
MSG = lambda{|msg,*keys| "#{msg}: #{keys.join(' ')}"}
|
76
70
|
F2K = lambda{|f| f[1]=='-' ? f[2..((f.index('=')||0)-1)] : f[1]}
|
77
|
-
RESERVED = lambda{|k| [USAGE,TYPES,EXCLUSIVE].include?(k)} # reserved
|
71
|
+
RESERVED = lambda{|k| [USAGE,TYPES,EXCLUSIVE,INCLUSIVE].include?(k)} # reserved
|
72
|
+
REDTTY = lambda{|msg,out=$stderr| out.tty? ? out.puts("\033[0;31m#{msg}\033[0m"): out.puts(msg)}
|
78
73
|
end
|
@@ -10,9 +10,9 @@ module HelpParser
|
|
10
10
|
|
11
11
|
def exit
|
12
12
|
if @code > 0
|
13
|
-
|
13
|
+
REDTTY[self.message]
|
14
14
|
else
|
15
|
-
puts self.message
|
15
|
+
$stdout.puts self.message
|
16
16
|
end
|
17
17
|
Kernel.exit @code
|
18
18
|
end
|
@@ -36,10 +36,10 @@ module HelpParser
|
|
36
36
|
end
|
37
37
|
|
38
38
|
class NoMatch < HelpParserException
|
39
|
-
# used to
|
39
|
+
# used to short-circuit out
|
40
40
|
def _init; @code = EX_SOFTWARE; end
|
41
41
|
|
42
|
-
# Forces it's
|
42
|
+
# Forces it's own message
|
43
43
|
def initialize
|
44
44
|
super(NO_MATCH)
|
45
45
|
end
|
data/lib/help_parser/k2t2r.rb
CHANGED
@@ -5,8 +5,11 @@ module HelpParser
|
|
5
5
|
tokens.each do |token|
|
6
6
|
if match = VARIABLE.match(token) || LONG.match(token)
|
7
7
|
name, type = match['k'], match['t']
|
8
|
-
k2t[name]
|
9
|
-
|
8
|
+
if _=k2t[name]
|
9
|
+
raise HelpError, MSG[INCONSISTENT,name,type,_] unless type==_
|
10
|
+
else
|
11
|
+
k2t[name] = type
|
12
|
+
end
|
10
13
|
else
|
11
14
|
# Expected these to be caught earlier...
|
12
15
|
raise SoftwareError, MSG[UNEXPECTED,token]
|
data/lib/help_parser/options.rb
CHANGED
@@ -7,20 +7,24 @@ module HelpParser
|
|
7
7
|
raise VersionException, version
|
8
8
|
end
|
9
9
|
if help
|
10
|
-
|
11
|
-
if
|
12
|
-
|
13
|
-
# validates help
|
14
|
-
HelpParser.parseh(help, true)
|
15
|
-
rescue HelpError
|
16
|
-
$stderr.puts $!
|
17
|
-
end if _
|
10
|
+
h = ['h', 'help']
|
11
|
+
if h.any?{|_|@hash.key?_}
|
12
|
+
HelpParser.parseh(help, validate: true) if h.all?{|_|@hash.key?_}
|
18
13
|
raise HelpException, help
|
19
14
|
end
|
20
|
-
specs = HelpParser.parseh(help
|
15
|
+
specs = HelpParser.parseh(help)
|
21
16
|
Completion.new(@hash, specs)
|
22
17
|
if exclusive=specs[EXCLUSIVE]
|
23
|
-
exclusive.each
|
18
|
+
exclusive.each do |x|
|
19
|
+
count = @hash.keys.count{|k|x.include?(k)}
|
20
|
+
raise HelpParser::UsageError, MSG[EXCLUSIVE_KEYS,*x] if count > 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
if inclusive=specs[INCLUSIVE]
|
24
|
+
inclusive.each do |i|
|
25
|
+
count = @hash.keys.count{|k|i.include?(k)}
|
26
|
+
raise HelpParser::UsageError, MSG[INCLUSIVE_KEYS,*i] unless count==0 or count==i.length
|
27
|
+
end
|
24
28
|
end
|
25
29
|
end
|
26
30
|
$VERBOSE = true if @hash['verbose']==true
|
data/lib/help_parser/parseh.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module HelpParser
|
2
|
-
def self.parseh(help, validate
|
2
|
+
def self.parseh(help, validate: false)
|
3
3
|
specs,name = NoDupHash.new,''
|
4
4
|
help.each_line do |line|
|
5
5
|
line.chomp!
|
@@ -18,11 +18,11 @@ module HelpParser
|
|
18
18
|
HelpParser.validate_line_chars(spec.chars) if validate
|
19
19
|
tokens = HelpParser.parseu(spec.chars)
|
20
20
|
HelpParser.validate_usage_tokens(tokens) if validate
|
21
|
-
specs[
|
21
|
+
specs[USAGE].push tokens
|
22
22
|
when TYPES
|
23
23
|
raise HelpError, MSG[UNRECOGNIZED_TYPE,spec] if validate and not spec=~TYPE_DEF
|
24
|
-
specs[
|
25
|
-
when EXCLUSIVE
|
24
|
+
specs[TYPES].push spec.split(CSV)
|
25
|
+
when EXCLUSIVE,INCLUSIVE
|
26
26
|
raise HelpError, MSG[UNRECOGNIZED_X,spec] if validate and not spec=~X_DEF
|
27
27
|
specs[name].push spec.split(CSV)
|
28
28
|
else
|
@@ -30,14 +30,16 @@ module HelpParser
|
|
30
30
|
def self.validate_usage_specs(specs)
|
31
31
|
option_specs = specs.select{|a,b| !RESERVED[a]}
|
32
32
|
flags = option_specs.values.flatten.select{|f|f[0]=='-'}.map{|f| F2K[f]}
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
33
|
+
[EXCLUSIVE,INCLUSIVE].each do |k|
|
34
|
+
if a=specs[k]
|
35
|
+
seen = {}
|
36
|
+
a.each do |xs|
|
37
|
+
k = xs.sort.join(' ').to_sym
|
38
|
+
raise HelpError, MSG[DUP_X,k] if seen[k] or not xs.length==xs.uniq.length
|
39
|
+
seen[k] = true
|
40
|
+
xs.each do |x|
|
41
|
+
raise HelpError, MSG[UNSEEN_FLAG, x] unless flags.include?(x)
|
42
|
+
end
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
metadata
CHANGED
@@ -1,21 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: help_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 7.0.200907
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- carlosjhr64
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
I prefer easy.
|
14
|
+
Can't help YOU???
|
15
|
+
You're not the first to say that...
|
16
|
+
|
17
|
+
I will parse your help!
|
19
18
|
email: carlosjhr64@gmail.com
|
20
19
|
executables: []
|
21
20
|
extensions: []
|
@@ -54,11 +53,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
53
|
- !ruby/object:Gem::Version
|
55
54
|
version: '0'
|
56
55
|
requirements:
|
57
|
-
- 'ruby: ruby 2.
|
58
|
-
|
59
|
-
rubygems_version: 2.7.7
|
56
|
+
- 'ruby: ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]'
|
57
|
+
rubygems_version: 3.1.2
|
60
58
|
signing_key:
|
61
59
|
specification_version: 4
|
62
|
-
summary:
|
63
|
-
Hard? I prefer easy.
|
60
|
+
summary: Can't help YOU??? You're not the first to say that...
|
64
61
|
test_files: []
|