option_parser_usage 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 48ea8ccb44efc9c7e89385293a14a0c0daf823bad7cb24f15a80f60c91f49424
4
+ data.tar.gz: 96f7dc93de75511696b285fbd8ab372b7ff3f2f6640ba4e4a7b3d427ec96bd6a
5
+ SHA512:
6
+ metadata.gz: '09ea5f0b3dc5c936bd2cb04e3384c798fa7b6076447240211531b9fe8568c6fdd07872e8145737db96ba8fa11efe3554d6197f845489a4cb1d66ccb375556f24'
7
+ data.tar.gz: a39a8d02b64f2b780a6ae484d968f2bb273fed824adbb08ae5f197881b3fc636c99b363398a5cb5d978e0550ab4ad8e62cc8757739aa46da6091b4f507f30945
data/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
4
+
5
+ In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # OptionParserUsage
2
+
3
+ Tooling for creating [usage][] specs from `OptionParser` definitions.
4
+
5
+ ## Installation
6
+
7
+ Add `gem "option_parser_usage"` to your gemfile and then `bundle install`.
8
+
9
+ ## Usage
10
+
11
+ Example:
12
+
13
+ ```ruby
14
+ require 'optparse'
15
+ require 'option_parser_usage'
16
+
17
+ myparser = OptionParser.new do |p|
18
+ p.program_name = "myprog"
19
+ p.version = "1.2.3"
20
+ p.banner = "my test program"
21
+
22
+ p.on("--foo", "enables foo")
23
+ p.on("--bar BAR", "requires argument BAR")
24
+ p.on("--baz [BAZ]", "optional argument BAZ")
25
+ end
26
+
27
+ puts OptionParserUsage.usage_for_parser(myparser)
28
+
29
+ # Alternatively
30
+ OptionParserUsage.monkeypatch!
31
+ puts myparser.to_usage_spec
32
+ ```
33
+
34
+ This will print (twice)
35
+
36
+ ```
37
+ bin myprog
38
+ version "1.2.3"
39
+ about "my test program"
40
+ flag --foo {
41
+ help "enables foo"
42
+ }
43
+ flag --bar {
44
+ arg <BAR> required=#true
45
+ help "requires argument BAR"
46
+ }
47
+ flag --baz {
48
+ arg "[BAZ]" required=#false
49
+ help "optional argument BAZ"
50
+ }
51
+ ```
52
+
53
+ ### Monkeypatching
54
+
55
+ ```
56
+ OptionParserUsage.monkeypatch!
57
+ # undo with OptionParserUsage.unmonkeypatch!
58
+ ```
59
+
60
+ This monkeypatch adds `#to_usage_spec` to `OptionParser` instances.
61
+
62
+ ```
63
+ OptionParserUsage.document_officious_help!
64
+ # undo with OptionParserUsage.undocument_officious_help!
65
+ ```
66
+
67
+ This monkeypatch changes the default `-h/--help` switch internal to the `OptionParser` to be documented so it can be exposed in docs
68
+
69
+ Both monkeypatches alter global state with all the requisite warnings about doing that.
70
+
71
+ ### Limitations
72
+
73
+ `OptionParser` supports a style of flag that cannot be properly rendered in usage as far as I can tell. `--foo=[FOO]` will enable either `--foo` or `--foo=val` but not `--foo val` (`val` will be left as a positional argument). Generating a usage spec will raise an error if one of these is encountered.
74
+
75
+ Positional arguments and generally anything parsed outside of the `OptionParser` will not be represented in the generated config.
76
+
77
+ ## Development
78
+
79
+ `bundle install` for the dev dependencies
80
+
81
+ `bundle exec rspec` to run the tests
82
+
83
+ `bundle exec standardrb --fix` to lint and autocorrect
84
+
85
+ ## Contributing
86
+
87
+ Bug reports and pull requests are welcome on GitHub at https://github.com/packrat386/option_parser_usage.
88
+
89
+ [usage]: https://usage.jdx.dev/
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kdl"
4
+
5
+ module OptionParserUsage
6
+ VERSION = "0.1.0"
7
+
8
+ NotSupportedError = Class.new(StandardError)
9
+
10
+ def self.monkeypatch!
11
+ OptionParser.class_exec do
12
+ def to_usage_spec
13
+ OptionParserUsage.usage_for_parser(self)
14
+ end
15
+ end
16
+ end
17
+
18
+ def self.unmonkeypatch!
19
+ OptionParser.remove_method(:to_usage_spec)
20
+ rescue NameError => e
21
+ raise if e.name != :to_usage_spec
22
+ end
23
+
24
+ def self.document_officious_help!
25
+ return unless @original_help.nil?
26
+
27
+ @original_help = OptionParser::Officious["help"]
28
+
29
+ OptionParser::Officious["help"] = proc do |parser|
30
+ switch = @original_help.call(parser)
31
+ switch.instance_variable_set(:@desc, ["shows option summary"])
32
+ switch.instance_variable_set(:@short, ["-h"])
33
+ switch.instance_variable_set(:@long, ["--help"])
34
+
35
+ switch
36
+ end
37
+ end
38
+
39
+ def self.undocument_officious_help!
40
+ return if @original_help.nil?
41
+
42
+ OptionParser::Officious["help"] = @original_help
43
+
44
+ @original_help = nil
45
+ end
46
+
47
+ def self.usage_for_parser(parser)
48
+ ::KDL.build do |spec|
49
+ spec.node "bin", parser.program_name
50
+ spec.node "version", parser.version if parser.version
51
+ spec.node "about", parser.banner
52
+
53
+ parser.instance_variable_get(:@stack).map { [_1.long.values, _1.short.values] }.flatten.uniq.select { switch_has_doc?(_1) }.each do |s|
54
+ spec.node "flag", primary_flag(s) do
55
+ flag_aliases(s).each { spec.node "alias", _1 }
56
+
57
+ if s.arg
58
+ spec.node "arg", flag_arg(s), required: flag_arg_required?(s)
59
+ end
60
+
61
+ if s.desc.length > 0
62
+ spec.node "help", s.desc.first
63
+ end
64
+
65
+ if s.desc.length > 1
66
+ spec.node "help_long", s.desc.join("\n")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ # private
74
+
75
+ def self.primary_flag(switch)
76
+ if switch.long
77
+ switch.long.first
78
+ else
79
+ switch.short.first
80
+ end
81
+ end
82
+
83
+ def self.flag_aliases(switch)
84
+ [switch.long, switch.short].flatten.compact.reject { _1 == primary_flag(switch) }
85
+ end
86
+
87
+ def self.switch_has_doc?(switch)
88
+ !(switch.long.nil? && switch.short.nil?)
89
+ end
90
+
91
+ def self.flag_arg(switch)
92
+ case switch
93
+ when OptionParser::Switch::RequiredArgument
94
+ switch.arg.gsub(/^\s*([[:word:]]+)\s*$/, '<\1>')
95
+ when OptionParser::Switch::PlacedArgument
96
+ switch.arg.gsub(/^\s*=?\[([[:word:]]+)\]\s*$/, '[\1]')
97
+ else
98
+ raise NotSupportedError.new("switch type not supported: #{switch.class}")
99
+ end
100
+ end
101
+
102
+ def self.flag_arg_required?(switch)
103
+ case switch
104
+ when OptionParser::Switch::RequiredArgument
105
+ true
106
+ else
107
+ false
108
+ end
109
+ end
110
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: option_parser_usage
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Aidan Coyle
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: kdl
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: optparse
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ description: 'see: https://usage.jdx.dev/'
41
+ email:
42
+ - packrat386@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - LICENSE.txt
48
+ - README.md
49
+ - lib/option_parser_usage.rb
50
+ homepage: https://github.com/packrat386/option_parser_usage
51
+ licenses: []
52
+ metadata:
53
+ homepage_uri: https://github.com/packrat386/option_parser_usage
54
+ source_code_uri: https://github.com/packrat386/option_parser_usage
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.2.0
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubygems_version: 4.0.3
70
+ specification_version: 4
71
+ summary: tooling to create usage specs from OptionParser
72
+ test_files: []