options_by_example 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +48 -0
- data/lib/options_by_example/version.rb +31 -0
- data/lib/options_by_example.rb +136 -0
- metadata +48 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2d803c63947acbb818c4fbf749cdb18084e6b0750678f9e600369ce543b78536
|
4
|
+
data.tar.gz: 21d6c8a4866627102f82d12a2805eb60850e6a24eb04d0ec064ea5af55dd87a2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a6e16dbd993f098415710aacd89e859ff9ac9403256a648a51fd4ab7754d91aa473e8c5b53f055ab0378d4acf1977e6b30f8826d1a626f6bcdeef4e0eb15e233
|
7
|
+
data.tar.gz: b9dcfa2249f316554b416f788109486caecefdc68c8d48890cc3e0033a3270509bea96fbe994931413bae7160c035e8968f63f855e59f522d67149d20e7fc15b
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Options by Example
|
2
|
+
|
3
|
+
No-code options parser that automatically detects command-line options from the usage text of your application. This intuitive parser identifies optional and required argument names as well as option names without requiring any additional code, making it easy to manage user input for your command-line applications.
|
4
|
+
|
5
|
+
Features
|
6
|
+
|
7
|
+
- Automatically detects optional and required argument names from usage text
|
8
|
+
- Automatically detects option names and associated arguments (if any) from usage text
|
9
|
+
- Parses those arguments and options from the command line (ARGV)
|
10
|
+
- Raises errors for unknown options or missing required arguments
|
11
|
+
|
12
|
+
|
13
|
+
Example
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
require 'options_by_example'
|
17
|
+
|
18
|
+
Options = OptionsByExample.read(DATA).parse(ARGV)
|
19
|
+
|
20
|
+
puts Options.include_secure?
|
21
|
+
puts Options.include_verbose?
|
22
|
+
puts Options.include_retries?
|
23
|
+
puts Options.include_timeout?
|
24
|
+
puts Options.argument_retries
|
25
|
+
puts Options.argument_timeout
|
26
|
+
puts Options.argument_mode
|
27
|
+
puts Options.argument_host
|
28
|
+
puts Options.argument_port
|
29
|
+
|
30
|
+
|
31
|
+
__END__
|
32
|
+
Establishes a network connection to a designated host and port, enabling
|
33
|
+
users to assess network connectivity and diagnose potential problems.
|
34
|
+
|
35
|
+
Usage: connect [options] [mode] host port
|
36
|
+
|
37
|
+
Options:
|
38
|
+
-s, --secure Establish a secure connection (SSL/TSL)
|
39
|
+
-v, --verbose Enable verbose output for detailed information
|
40
|
+
-r, --retries NUM Specify the number of connection retries (default 3)
|
41
|
+
-t, --timeout NUM Set the connection timeout in seconds (default 10)
|
42
|
+
|
43
|
+
Arguments:
|
44
|
+
[mode] Optional connection mode (active or passive)
|
45
|
+
host The target host to connect to (e.g., example.com)
|
46
|
+
port The target port to connect to (e.g., 80)
|
47
|
+
```
|
48
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class OptionsByExample
|
4
|
+
VERSION = '1.1.0'
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
__END__
|
9
|
+
|
10
|
+
# Major version bump when breaking changes or new features
|
11
|
+
# Minor version bump when backward-compatible changes or enhancements
|
12
|
+
# Patch version bump when backward-compatible bug fixes, security updates etc
|
13
|
+
|
14
|
+
1.0.1
|
15
|
+
|
16
|
+
- Update readme file with features and an example
|
17
|
+
- Update the gemspec to include readme and ruby files only
|
18
|
+
|
19
|
+
1.0.0
|
20
|
+
|
21
|
+
- Extract optional and required argument names from a usage text
|
22
|
+
- Extract option names and associated argument names (if any) from a usage text
|
23
|
+
- Include help option by default
|
24
|
+
- Parse options and arguments from command-line arguments aka ARGV
|
25
|
+
- Exit gracefully or raise exceptions, depending on the exit_on_error parameter
|
26
|
+
- Implement dynamic methods for checking options and getting arguments
|
27
|
+
- Ensure correct git tag and all changes committed when building gem
|
28
|
+
|
29
|
+
0.0.0
|
30
|
+
|
31
|
+
- Prehistory starts here...
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "options_by_example/version"
|
4
|
+
|
5
|
+
|
6
|
+
class OptionsByExample
|
7
|
+
|
8
|
+
attr_reader :argument_names_optional
|
9
|
+
attr_reader :argument_names_required
|
10
|
+
attr_reader :option_names
|
11
|
+
|
12
|
+
attr_reader :arguments
|
13
|
+
attr_reader :options
|
14
|
+
|
15
|
+
|
16
|
+
def self.read(data)
|
17
|
+
return new data.read
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(text)
|
21
|
+
@usage = text.gsub('$0', File.basename($0)).gsub(/\n+\Z/, "\n\n")
|
22
|
+
|
23
|
+
# ---- 1) Parse argument names ------------------------------------
|
24
|
+
#
|
25
|
+
# Parse the usage string and extract both optional argument names
|
26
|
+
# and required argument names, for example:
|
27
|
+
#
|
28
|
+
# Usage: connect [options] [mode] host port
|
29
|
+
|
30
|
+
text =~ /Usage: (\w+|\$0) \[options\](( \[\w+\])*)(( \w+)*)/
|
31
|
+
raise RuntimeError, "Expected usage string, got none" unless $1
|
32
|
+
@argument_names_optional = $2.to_s.split.map { |match| match.tr('[]', '').downcase }
|
33
|
+
@argument_names_required = $4.to_s.split.map(&:downcase)
|
34
|
+
|
35
|
+
# ---- 2) Parse option names --------------------------------------
|
36
|
+
#
|
37
|
+
# Parse the usage message and extract option names, their short and
|
38
|
+
# long forms, and the associated argument name (if any), eg:
|
39
|
+
#
|
40
|
+
# Options:
|
41
|
+
# -s, --secure Use secure connection
|
42
|
+
# -v, --verbose Enable verbose output
|
43
|
+
# -r, --retries NUM Number of connection retries (default 3)
|
44
|
+
# -t, --timeout NUM Connection timeout in seconds (default 10)
|
45
|
+
|
46
|
+
@option_names = {}
|
47
|
+
text.scan(/((--?\w+)(, --?\w+)*) ?(\w+)?/) do
|
48
|
+
opts = $1.split(", ")
|
49
|
+
opts.each { |each| @option_names[each] = [opts.last.tr('-', ''), $4&.downcase] }
|
50
|
+
end
|
51
|
+
|
52
|
+
# ---- 3) Include help option by default --------------------------
|
53
|
+
|
54
|
+
@option_names.update("-h" => :help, "--help" => :help)
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse(argv, exit_on_error: true)
|
58
|
+
array = argv.dup
|
59
|
+
@arguments = {}
|
60
|
+
@options = {}
|
61
|
+
|
62
|
+
# --- 1) Parse options --------------------------------------------
|
63
|
+
|
64
|
+
most_recent_option = nil
|
65
|
+
until array.empty? do
|
66
|
+
break unless array.first.start_with?(?-)
|
67
|
+
most_recent_option = option = array.shift
|
68
|
+
option_name, argument_name = @option_names[option]
|
69
|
+
raise "Got unknown option #{option}" if option_name.nil?
|
70
|
+
raise if option_name == :help # Show usage without error message
|
71
|
+
@options[option_name] = true
|
72
|
+
|
73
|
+
# Consume argument, if expected by most recent option
|
74
|
+
if argument_name
|
75
|
+
argument = array.shift
|
76
|
+
raise "Expected argument for option #{option}" unless /^[^-]/ === argument
|
77
|
+
@arguments[option_name] = argument
|
78
|
+
most_recent_option = nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# --- 2) Parse optional arguments ---------------------------------
|
83
|
+
|
84
|
+
# Check any start with --, ie excess options
|
85
|
+
# Check min_length - max_length here
|
86
|
+
|
87
|
+
stash = array.pop(@argument_names_required.length)
|
88
|
+
@argument_names_optional.each do |argument_name|
|
89
|
+
break if array.empty?
|
90
|
+
argument = array.shift
|
91
|
+
raise "Expected more arguments, got option #{option}" unless /^[^-]/ === argument
|
92
|
+
@arguments[argument_name] = argument
|
93
|
+
end
|
94
|
+
|
95
|
+
# --- 3) Parse required arguments ---------------------------------
|
96
|
+
|
97
|
+
@argument_names_required.each do |argument_name|
|
98
|
+
raise "Expected required argument #{argument_name.upcase}, got none" if stash.empty?
|
99
|
+
argument = stash.shift
|
100
|
+
raise "Expected more arguments, got option #{option}" unless /^[^-]/ === argument
|
101
|
+
@arguments[argument_name] = argument
|
102
|
+
end
|
103
|
+
|
104
|
+
# --- 4) Expect to be done ----------------------------------------
|
105
|
+
|
106
|
+
if not array.empty?
|
107
|
+
# Custom error message if most recent option did not require argument
|
108
|
+
raise "Got unexpected argument for option #{most_recent_option}" if most_recent_option
|
109
|
+
raise "Expected #{min_length}#{"-#{max_length}" if max_length > min_length} arguments, got more"
|
110
|
+
end
|
111
|
+
|
112
|
+
return self
|
113
|
+
|
114
|
+
rescue RuntimeError => err
|
115
|
+
if exit_on_error
|
116
|
+
puts "ERROR: #{err.message}\n\n" unless err.message.empty?
|
117
|
+
puts @usage
|
118
|
+
exit
|
119
|
+
else
|
120
|
+
raise # Reraise the same exception
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def method_missing(sym, *args, &block)
|
125
|
+
case sym
|
126
|
+
when /^argument_(\w+)$/
|
127
|
+
val = @arguments[$1]
|
128
|
+
block && val ? block.call(val) : val
|
129
|
+
when /^include_(\w+)\?$/
|
130
|
+
@options[$1]
|
131
|
+
else
|
132
|
+
super
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: options_by_example
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adrian Kuhn
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-04-28 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
- akuhn@iam.unibe.ch
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- README.md
|
21
|
+
- lib/options_by_example.rb
|
22
|
+
- lib/options_by_example/version.rb
|
23
|
+
homepage: https://github.com/akuhn/options_by_example
|
24
|
+
licenses:
|
25
|
+
- MIT
|
26
|
+
metadata:
|
27
|
+
homepage_uri: https://github.com/akuhn/options_by_example
|
28
|
+
source_code_uri: https://github.com/akuhn/options_by_example
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.6.0
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubygems_version: 3.3.7
|
45
|
+
signing_key:
|
46
|
+
specification_version: 4
|
47
|
+
summary: No-code options parser that extracts arguments directly from usage text.
|
48
|
+
test_files: []
|