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 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: []