flagabowski 0.0.4
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 +38 -0
- data/.gitlab-ci.yml +36 -0
- data/Gemfile +8 -0
- data/README.md +182 -0
- data/Rakefile +16 -0
- data/flagabowski.gemspec +17 -0
- data/lib/flagabowski.rb +214 -0
- metadata +52 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: dd71400c2d0a9dc112f260cf1f444f6382a104bf5d494634365fb9ea7b416fa6
|
|
4
|
+
data.tar.gz: fe332aecfbf6c5d8ee138274f6c04575f957009704bd2572967522aa09bffc1c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a869f7020b49edd8d3fbeb3b568c62c6faf6629f98efee0fe54036b1a7420643fa847148c7416c9d2a78bc157f8ee0f667ef2fc2bcb80b1ec0248a478fadce5e
|
|
7
|
+
data.tar.gz: 76ecdff5dc1ced0e7d661822d05823d4213348d411767ff7d26e39a1e5f2fd26ca06df8c650ad2d96e9a10da869d6b3225b4aa818372333d7b29176133459b50
|
data/.gitignore
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
/.config
|
|
4
|
+
/coverage/
|
|
5
|
+
/InstalledFiles
|
|
6
|
+
/pkg/
|
|
7
|
+
/spec/reports/
|
|
8
|
+
/spec/examples.txt
|
|
9
|
+
/test/tmp/
|
|
10
|
+
/test/version_tmp/
|
|
11
|
+
/tmp/
|
|
12
|
+
|
|
13
|
+
.env
|
|
14
|
+
.byebug_history
|
|
15
|
+
|
|
16
|
+
.dat*
|
|
17
|
+
.repl_history
|
|
18
|
+
build/
|
|
19
|
+
*.bridgesupport
|
|
20
|
+
build-iPhoneOS/
|
|
21
|
+
build-iPhoneSimulator/
|
|
22
|
+
|
|
23
|
+
/.yardoc/
|
|
24
|
+
/_yardoc/
|
|
25
|
+
/doc/
|
|
26
|
+
/rdoc/
|
|
27
|
+
|
|
28
|
+
/.bundle/
|
|
29
|
+
/vendor/bundle
|
|
30
|
+
/lib/bundler/man/
|
|
31
|
+
|
|
32
|
+
Gemfile.lock
|
|
33
|
+
.ruby-version
|
|
34
|
+
.ruby-gemset
|
|
35
|
+
|
|
36
|
+
.rvmrc
|
|
37
|
+
|
|
38
|
+
.rubocop-https?--*
|
data/.gitlab-ci.yml
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
image: ruby:3.3
|
|
2
|
+
|
|
3
|
+
stages:
|
|
4
|
+
- test
|
|
5
|
+
- release
|
|
6
|
+
|
|
7
|
+
variables:
|
|
8
|
+
BUNDLE_PATH: vendor/bundle
|
|
9
|
+
|
|
10
|
+
cache:
|
|
11
|
+
paths:
|
|
12
|
+
- vendor/bundle
|
|
13
|
+
|
|
14
|
+
before_script:
|
|
15
|
+
- ruby -v
|
|
16
|
+
- bundle install --jobs $(nproc)
|
|
17
|
+
|
|
18
|
+
test:
|
|
19
|
+
stage: test
|
|
20
|
+
script:
|
|
21
|
+
- bundle exec rake spec
|
|
22
|
+
coverage: '/\d+\.\d+\%/'
|
|
23
|
+
|
|
24
|
+
release:
|
|
25
|
+
stage: release
|
|
26
|
+
only:
|
|
27
|
+
- tags
|
|
28
|
+
except:
|
|
29
|
+
- branches
|
|
30
|
+
script:
|
|
31
|
+
- gem build flagabowski.gemspec
|
|
32
|
+
- export GEM_HOST_API_KEY="${RUBYGEMS_API_KEY}"
|
|
33
|
+
- gem push flagabowski-*.gem
|
|
34
|
+
environment:
|
|
35
|
+
name: production
|
|
36
|
+
url: https://rubygems.org/gems/flagabowski
|
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Flagabowski
|
|
2
|
+
|
|
3
|
+
Bunny says you're good for it.
|
|
4
|
+
|
|
5
|
+
A wrapper around Ruby's OptionParser that adds validation, conflict groups, one-of constraints, and automatic short option conflict resolution.
|
|
6
|
+
|
|
7
|
+
Deeply inspired by [micro-optparse](https://github.com/florianpilz/micro-optparse) but with some additional features.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add this line to your application's Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'flagabowski'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or install it yourself:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
gem install flagabowski
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Basic Example
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
require 'flagabowski'
|
|
29
|
+
|
|
30
|
+
parser = Flagabowski::OptionParser.new do |p|
|
|
31
|
+
p.banner "Usage: myapp [options]"
|
|
32
|
+
|
|
33
|
+
p.option name: :get_a_toe,
|
|
34
|
+
desc: "Get me a toe"
|
|
35
|
+
|
|
36
|
+
p.option name: :by,
|
|
37
|
+
default: "3 o'clock",
|
|
38
|
+
desc: "You don't want to know how... trust me!"
|
|
39
|
+
|
|
40
|
+
p.option name: :dollars,
|
|
41
|
+
default: 1000000,
|
|
42
|
+
desc: "amount we thought we were getting"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
result = parser.process!(ARGV)
|
|
46
|
+
puts result['get_a_toe'] # true/false
|
|
47
|
+
puts result['by'] # "3 o'clock" or user-provided value
|
|
48
|
+
puts result['dollars'] # 1000000 or user-provided value
|
|
49
|
+
puts result[:pos_args] # Array of positional arguments
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Required Options
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
parser.option name: :id,
|
|
56
|
+
default: '',
|
|
57
|
+
required: true,
|
|
58
|
+
desc: "is this your only?"
|
|
59
|
+
|
|
60
|
+
# Raises OptionParser::MissingArgument if not provided
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Validation with Arrays
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
parser.option name: :the_money,
|
|
67
|
+
default: 'ringer',
|
|
68
|
+
valid: ['ringer', 'ringer for a ringer'],
|
|
69
|
+
desc: "to be thrown"
|
|
70
|
+
|
|
71
|
+
# Raises OptionParser::InvalidArgument if value not in array
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Validation with Regex
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
parser.option name: :email,
|
|
78
|
+
default: '',
|
|
79
|
+
valid: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i,
|
|
80
|
+
desc: "Email address"
|
|
81
|
+
|
|
82
|
+
# Raises OptionParser::InvalidArgument if value doesn't match regex
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Custom Long and Short Options
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
parser.option name: :verbose,
|
|
89
|
+
short: 'x',
|
|
90
|
+
long: 'debug',
|
|
91
|
+
desc: "Debug mode"
|
|
92
|
+
|
|
93
|
+
# Can be invoked with -x or --debug
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Automatic Short Option Conflict Resolution
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
parser.option name: :verbose, desc: "Verbose output"
|
|
100
|
+
parser.option name: :version, desc: "Show version"
|
|
101
|
+
|
|
102
|
+
# First option gets -v, second automatically gets -e
|
|
103
|
+
# Both can still use --verbose and --version
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Conflict Groups
|
|
107
|
+
|
|
108
|
+
Ensure only one option from a group is used:
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
parser.option name: :json, desc: "JSON output"
|
|
112
|
+
parser.option name: :xml, desc: "XML output"
|
|
113
|
+
parser.option name: :yaml, desc: "YAML output"
|
|
114
|
+
|
|
115
|
+
parser.add_conflicts_constraint(['json', 'xml', 'yaml'])
|
|
116
|
+
|
|
117
|
+
# Raises OptionParser::InvalidOption if more than one is provided
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### One-Of Groups
|
|
121
|
+
|
|
122
|
+
Require exactly one option from a group:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
parser.option name: :input, default: '', desc: "Input file"
|
|
126
|
+
parser.option name: :stdin, desc: "Read from stdin"
|
|
127
|
+
|
|
128
|
+
parser.add_one_of_constraint(['input', 'stdin'])
|
|
129
|
+
|
|
130
|
+
# Raises OptionParser::MissingArgument if neither is provided
|
|
131
|
+
# Raises OptionParser::InvalidOption if both are provided
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Help Text
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
# Get help text as a string
|
|
138
|
+
help_text = parser.usage
|
|
139
|
+
puts help_text
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Option Parameters
|
|
143
|
+
|
|
144
|
+
- `name:` (required) - Symbol or string, the option name
|
|
145
|
+
- `desc:` - Description shown in help text (default: "Lazy option authors have failed you.")
|
|
146
|
+
- `default:` - Default value (also determines type casting if `cast:` not specified)
|
|
147
|
+
- `required:` - Boolean, whether option is required (default: false)
|
|
148
|
+
- `valid:` - Array or Regex for validation
|
|
149
|
+
- `short:` - Custom short option letter
|
|
150
|
+
- `long:` - Custom long option name
|
|
151
|
+
- `cast:` - Explicit type to cast to (overrides type inferred from default)
|
|
152
|
+
|
|
153
|
+
## Type Casting
|
|
154
|
+
|
|
155
|
+
Types are inferred from the default value, or can be explicitly specified with `cast:`:
|
|
156
|
+
- `default: "text"` → String
|
|
157
|
+
- `default: 42` → Integer
|
|
158
|
+
- `default: nil` → Boolean flag (no argument)
|
|
159
|
+
- `cast: Integer` → Integer (regardless of default)
|
|
160
|
+
- `cast: Float` → Float (can override default's type)
|
|
161
|
+
|
|
162
|
+
Example with explicit casting:
|
|
163
|
+
```ruby
|
|
164
|
+
parser.option name: :percentage,
|
|
165
|
+
default: 0,
|
|
166
|
+
cast: Float,
|
|
167
|
+
desc: "Percentage as decimal"
|
|
168
|
+
|
|
169
|
+
# Accepts: --percentage 0.75
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Error Handling
|
|
173
|
+
|
|
174
|
+
Flagabowski raises standard OptionParser exceptions:
|
|
175
|
+
- `OptionParser::MissingArgument` - Required option not provided, or no option from one-of group
|
|
176
|
+
- `OptionParser::InvalidArgument` - Validation failed
|
|
177
|
+
- `OptionParser::InvalidOption` - Conflicting options used, or multiple from one-of group
|
|
178
|
+
- `OptionParser::ParseError` - General parsing errors (base class)
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
GPLv2
|
data/Rakefile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'rspec/core/rake_task'
|
|
2
|
+
|
|
3
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
4
|
+
|
|
5
|
+
task default: :spec
|
|
6
|
+
|
|
7
|
+
desc 'Tag the current commit with the version from gemspec and push'
|
|
8
|
+
task :tag do
|
|
9
|
+
spec = Gem::Specification.load('flagabowski.gemspec')
|
|
10
|
+
version = spec.version
|
|
11
|
+
tag = "v#{version}"
|
|
12
|
+
|
|
13
|
+
sh "git tag #{tag}"
|
|
14
|
+
sh "git push origin #{tag}"
|
|
15
|
+
puts "Tagged and pushed #{tag}"
|
|
16
|
+
end
|
data/flagabowski.gemspec
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = "flagabowski"
|
|
3
|
+
s.version = "0.0.4"
|
|
4
|
+
s.summary = "Bunny says you're good for it."
|
|
5
|
+
s.description = "A wrapper around Ruby's OptionParser with validation, conflict groups, and one-of constraints"
|
|
6
|
+
s.authors = ["Sam Rowe"]
|
|
7
|
+
s.email = "gemspam@samrowe.com"
|
|
8
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
9
|
+
s.homepage = "https://gitlab.com/srowe/flagabowski"
|
|
10
|
+
s.license = "GPL-2.0-or-later"
|
|
11
|
+
s.required_ruby_version = ">= 2.7.0"
|
|
12
|
+
|
|
13
|
+
s.metadata = {
|
|
14
|
+
"source_code_uri" => "https://gitlab.com/srowe/flagabowski",
|
|
15
|
+
"bug_tracker_uri" => "https://gitlab.com/srowe/flagabowski/-/issues"
|
|
16
|
+
}
|
|
17
|
+
end
|
data/lib/flagabowski.rb
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
require 'shellwords'
|
|
2
|
+
require 'optparse'
|
|
3
|
+
|
|
4
|
+
module Flagabowski
|
|
5
|
+
class Option
|
|
6
|
+
def initialize(opts)
|
|
7
|
+
@name = opts[:name]
|
|
8
|
+
@default = opts[:default] || nil
|
|
9
|
+
@desc = opts[:desc] || "Lazy option authors have failed you."
|
|
10
|
+
@long = opts[:long]
|
|
11
|
+
@opts = opts #for #cast
|
|
12
|
+
@required = opts[:required] || false
|
|
13
|
+
@satisfied = false
|
|
14
|
+
@short = opts[:short]
|
|
15
|
+
@valid = opts[:valid]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def cast
|
|
19
|
+
@cast ||= @opts[:cast] || @default.class
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def default
|
|
23
|
+
@default
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def desc
|
|
27
|
+
@desc
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def long
|
|
31
|
+
@long || name
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def long_arg
|
|
35
|
+
%Q{--#{long}#{cast_arg}}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def name
|
|
39
|
+
@name.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def satisfied!
|
|
43
|
+
@satisfied = true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def satisfied?
|
|
47
|
+
@satisfied
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def unsatisfied?
|
|
51
|
+
@required ? ! @satisfied : false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def short
|
|
55
|
+
@short ||= namechars.first
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def short_arg
|
|
59
|
+
%Q{-#{short}#{cast_arg}}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def short_try_again
|
|
63
|
+
# perfect is the enemy of good
|
|
64
|
+
@short = namechars[namechars.index(short)+1]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def validate!(thing)
|
|
68
|
+
return true if @valid.nil?
|
|
69
|
+
|
|
70
|
+
case @valid
|
|
71
|
+
when Array
|
|
72
|
+
@valid.include?(thing)
|
|
73
|
+
when Regexp
|
|
74
|
+
@valid.match?(thing)
|
|
75
|
+
else
|
|
76
|
+
false
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def cast_arg
|
|
83
|
+
return '' if cast.name == 'NilClass' #really again, ruby?
|
|
84
|
+
%Q{=#{really_ruby}}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def namechars
|
|
88
|
+
name.chars
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def really_ruby
|
|
92
|
+
@cast.to_s.sub(/C/,'_C').upcase
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class OptionParser
|
|
97
|
+
def initialize
|
|
98
|
+
@banner = ''
|
|
99
|
+
@conflicts = [] #array of arrays of conflicting switches: [['morning','evening'],['breakfast','lunch','dinner']]
|
|
100
|
+
@one_of = [] #array of arrays where any one of the names satsifies the hoax
|
|
101
|
+
@options = []
|
|
102
|
+
@used_short = {}
|
|
103
|
+
yield self if block_given?
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def add_one_of_constraint(arr)
|
|
107
|
+
@one_of << arr
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def add_conflicts_constraint(arr)
|
|
111
|
+
@conflicts << arr
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def banner(banstr)
|
|
115
|
+
@banner = banstr
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def option(opts)
|
|
119
|
+
@options << Option.new(opts)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def process!(args)
|
|
123
|
+
args = args.shellsplit if args.is_a?(String)
|
|
124
|
+
result = {}
|
|
125
|
+
|
|
126
|
+
@options.each do |opt|
|
|
127
|
+
opt.instance_variable_set(:@satisfied, false)
|
|
128
|
+
result[opt.name] = opt.default
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
option_parser = build_option_parser do |opt, value|
|
|
132
|
+
unless opt.validate!(value)
|
|
133
|
+
raise ::OptionParser::InvalidArgument, "Invalid value '#{value}' for option #{opt.long_arg}"
|
|
134
|
+
end
|
|
135
|
+
opt.satisfied!
|
|
136
|
+
result[opt.name] = value
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
option_parser.parse!(args)
|
|
140
|
+
|
|
141
|
+
validate_required
|
|
142
|
+
validate_conflicts
|
|
143
|
+
validate_one_of
|
|
144
|
+
|
|
145
|
+
result[:pos_args] = args
|
|
146
|
+
result
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def usage
|
|
150
|
+
build_option_parser.to_s
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
private
|
|
154
|
+
|
|
155
|
+
def build_option_parser(&on_option)
|
|
156
|
+
fix_shorts
|
|
157
|
+
|
|
158
|
+
::OptionParser.new do |optparser|
|
|
159
|
+
optparser.banner = @banner if @banner
|
|
160
|
+
|
|
161
|
+
optparser.on('-h', '--help', 'Show this help message') { } # No-op to prevent exit
|
|
162
|
+
|
|
163
|
+
@options.each do |opt|
|
|
164
|
+
if opt.cast == NilClass
|
|
165
|
+
optparser.on(opt.short_arg, opt.long_arg, opt.desc) do |value|
|
|
166
|
+
on_option.call(opt, value) if on_option
|
|
167
|
+
end
|
|
168
|
+
else
|
|
169
|
+
optparser.on(opt.short_arg, opt.long_arg, opt.cast, opt.desc) do |value|
|
|
170
|
+
on_option.call(opt, value) if on_option
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def validate_required
|
|
178
|
+
unsatisfied = @options.select(&:unsatisfied?)
|
|
179
|
+
if unsatisfied.any?
|
|
180
|
+
missing_names = unsatisfied.map { |opt| opt.long_arg }.join(', ')
|
|
181
|
+
raise ::OptionParser::MissingArgument, "Missing required options: #{missing_names}"
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def validate_conflicts
|
|
186
|
+
@conflicts.each do |conflict_group|
|
|
187
|
+
if (satisfied = conflict_group.select { |name| @options.find { |opt| opt.name == name && opt.satisfied? } }).length > 1
|
|
188
|
+
raise ::OptionParser::InvalidOption, "Conflicting options used: #{satisfied.join(', ')}"
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def validate_one_of
|
|
194
|
+
@one_of.each do |one_of_group|
|
|
195
|
+
satisfied_count = one_of_group.count { |name| @options.find { |opt| opt.name == name && opt.satisfied? } }
|
|
196
|
+
if satisfied_count == 0
|
|
197
|
+
raise ::OptionParser::MissingArgument, "One of the following options is required: #{one_of_group.join(', ')}"
|
|
198
|
+
elsif satisfied_count > 1
|
|
199
|
+
raise ::OptionParser::InvalidOption, "Only one of the following options can be used: #{one_of_group.join(', ')}"
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def fix_shorts
|
|
205
|
+
@options.each do |opt|
|
|
206
|
+
while @used_short.keys.include?(opt.short)
|
|
207
|
+
break if @used_short[opt.short] == opt
|
|
208
|
+
opt.short_try_again
|
|
209
|
+
end
|
|
210
|
+
@used_short[opt.short] = opt
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: flagabowski
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.4
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Sam Rowe
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-12-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: A wrapper around Ruby's OptionParser with validation, conflict groups,
|
|
14
|
+
and one-of constraints
|
|
15
|
+
email: gemspam@samrowe.com
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- ".gitignore"
|
|
21
|
+
- ".gitlab-ci.yml"
|
|
22
|
+
- Gemfile
|
|
23
|
+
- README.md
|
|
24
|
+
- Rakefile
|
|
25
|
+
- flagabowski.gemspec
|
|
26
|
+
- lib/flagabowski.rb
|
|
27
|
+
homepage: https://gitlab.com/srowe/flagabowski
|
|
28
|
+
licenses:
|
|
29
|
+
- GPL-2.0-or-later
|
|
30
|
+
metadata:
|
|
31
|
+
source_code_uri: https://gitlab.com/srowe/flagabowski
|
|
32
|
+
bug_tracker_uri: https://gitlab.com/srowe/flagabowski/-/issues
|
|
33
|
+
post_install_message:
|
|
34
|
+
rdoc_options: []
|
|
35
|
+
require_paths:
|
|
36
|
+
- lib
|
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: 2.7.0
|
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
requirements: []
|
|
48
|
+
rubygems_version: 3.5.22
|
|
49
|
+
signing_key:
|
|
50
|
+
specification_version: 4
|
|
51
|
+
summary: Bunny says you're good for it.
|
|
52
|
+
test_files: []
|