options_parser 1.0.1

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: 407b2f4ed706b0be1a0e8b4ce7397d82c0152137eda948d03cf191c03d803f11
4
+ data.tar.gz: bf06e068a8d04793a9d181b49ef6e0826c9ff7b8a6d099429078f7c9b4cc99e2
5
+ SHA512:
6
+ metadata.gz: 53b83b4c5633c789554566cfd810a54aade3f7d9ee501caec333cdbd93f1cb70be2e90a57dd5e5e057d9f248de3abd99cefe9009ad3bfba79b8772126de7ca34
7
+ data.tar.gz: 9cd7b5ec11c783b2f9b035df6449f2ab910c88ac78e2239448bd37180ac9512de66617245fe1ec81eb360bd0097406d14cbd0ce4b0e7efd5b61a1830c13137c1
data/.git-com.yaml ADDED
@@ -0,0 +1,44 @@
1
+ change-type:
2
+ after-string: '] '
3
+ before-string: '['
4
+ destination: title
5
+ instructions: What kind of change?
6
+ modifiable: true
7
+ options:
8
+ - fix
9
+ - add
10
+ - refactor
11
+ - clean
12
+ - meta
13
+ type: select
14
+ commit-title:
15
+ destination: title
16
+ placeholder: Commit Title
17
+ type: text
18
+ commit-description:
19
+ after-string: ""
20
+ allow-empty: true
21
+ destination: body
22
+ instructions: Please describe your changes.
23
+ type: multiline-text
24
+ code-sections:
25
+ after-string: ""
26
+ allow-empty: true
27
+ before-string: "\nCode Sections: "
28
+ destination: body
29
+ instructions: Which section(s) of the codebase?
30
+ modifiable: true
31
+ options:
32
+ - parser
33
+ - option
34
+ - tests
35
+ - docs
36
+ record-as: joined-string
37
+ type: multi-select
38
+ ticket-number:
39
+ allow-empty: true
40
+ before-string: "\n\nTicket: "
41
+ data-type: integer
42
+ destination: body
43
+ instructions: Associated Ticket Number (if any)
44
+ type: text
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Modify this
4
+ # LIST='list\|of\|words\|splitted\|by\|slash\|and\|pipe'
5
+ # NOTE: list is case SENSITIVE (intentionally)
6
+ LIST="debug\|debugger\|binding.pry\|byebug\|alert(\|console.log("
7
+
8
+
9
+ for FILE in $(git diff --cached --name-only --diff-filter=ACM) ; do
10
+ # Check if the file contains one of the words in LIST
11
+ if grep "$LIST" "$FILE"; then
12
+ echo "$FILE contains \"uncommittable\" words"
13
+ exit 1
14
+ fi
15
+ done
16
+ exit
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ files = `git diff --cached --name-only --diff-filter=ACM`.split("\n")
5
+ tested_files = Set.new
6
+ git_root = `git rev-parse --show-toplevel`.strip
7
+ # tracking tested files because there may be changes to
8
+ # foo.rb and foo_spec.rb which would result in
9
+ # foo_spec.rb being invoked twice.
10
+ def get_spec_path(file)
11
+ new_path = file
12
+ unless is_spec?(file)
13
+ new_path = file.sub(/\.rb$/, "_spec.rb")
14
+ if new_path.include? "app/"
15
+ new_path = new_path.sub("app/", "spec/")
16
+ end
17
+ if new_path.include? "lib/"
18
+ new_path = new_path.sub("lib/", "spec/")
19
+ end
20
+ end
21
+ new_path
22
+ end
23
+
24
+ def get_spec_subpath(file)
25
+ file.sub(/.*?spec/, "spec")
26
+ end
27
+
28
+ def is_spec?(file)
29
+ file.end_with?("_spec.rb")
30
+ end
31
+
32
+ def is_ruby?(file)
33
+ file.end_with?(".rb")
34
+ end
35
+
36
+ def get_inner_root(file)
37
+ file.sub(/spec\/.*/, "")
38
+ end
39
+
40
+ files.each do |file|
41
+ filename = file
42
+ next unless is_ruby? file
43
+
44
+ filename = get_spec_path(filename)
45
+ next unless is_spec?(filename)
46
+
47
+ full_spec_path = "#{git_root}/#{filename}"
48
+ begin
49
+ Dir.chdir(get_inner_root(full_spec_path)) do
50
+ spec_subpath = get_spec_subpath(full_spec_path)
51
+ next unless File.exist?(spec_subpath) && tested_files.exclude?(spec_subpath)
52
+
53
+ valid_code = system("ruby -c #{spec_subpath}")
54
+ exit 2 unless valid_code
55
+
56
+ tested_files.add full_spec_path
57
+
58
+ response = system("bundle exec rspec #{spec_subpath}")
59
+ unless response
60
+ puts "spec failed: #{spec_subpath}"
61
+ exit 1
62
+ end
63
+ end
64
+ rescue
65
+ nil
66
+ end
67
+ end
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2026-01-03
4
+
5
+ - Initial release
data/LICENSE.md ADDED
@@ -0,0 +1,117 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+
3
+ Version 2, June 1991
4
+
5
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
6
+ <[https://fsf.org/](https://fsf.org/)\>
7
+ Everyone is permitted to copy and distribute verbatim copies
8
+ of this license document, but changing it is not allowed.
9
+
10
+ #### Preamble
11
+
12
+ The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
13
+
14
+ When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
15
+
16
+ To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
17
+
18
+ For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
19
+
20
+ We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
21
+
22
+ Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
23
+
24
+ Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
25
+
26
+ The precise terms and conditions for copying, distribution and modification follow.
27
+
28
+ #### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
29
+
30
+ **0.** This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
31
+
32
+ Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
33
+
34
+ **1.** You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
35
+
36
+ You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
37
+
38
+ **2.** You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
39
+
40
+ **a)** You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
41
+
42
+ **b)** You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
43
+
44
+ **c)** If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
45
+
46
+ These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
47
+
48
+ Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
49
+
50
+ In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
51
+
52
+ **3.** You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
53
+
54
+ **a)** Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
55
+
56
+ **b)** Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
57
+
58
+ **c)** Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
59
+
60
+ The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
61
+
62
+ If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
63
+
64
+ **4.** You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
65
+
66
+ **5.** You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
67
+
68
+ **6.** Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
69
+
70
+ **7.** If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
71
+
72
+ If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
73
+
74
+ It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
75
+
76
+ This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
77
+
78
+ **8.** If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
79
+
80
+ **9.** The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
81
+
82
+ Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
83
+
84
+ **10.** If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
85
+
86
+ **NO WARRANTY**
87
+
88
+ **11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
89
+
90
+ **12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
91
+
92
+ #### END OF TERMS AND CONDITIONS
93
+
94
+ #### How to Apply These Terms to Your New Programs
95
+
96
+ If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
97
+
98
+ To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
99
+
100
+ ------
101
+ better-parseopt is a better option parsing library for ruby
102
+
103
+ Copyright (C) 2025 Kay Rhodes, and Felipe Contreras
104
+
105
+ This program is free software; you can redistribute it and/or
106
+ modify it under the terms of the GNU General Public License
107
+ as published by the Free Software Foundation; either version 2
108
+ of the License, or (at your option) any later version.
109
+
110
+ This program is distributed in the hope that it will be useful,
111
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
112
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
113
+ GNU General Public License for more details.
114
+
115
+ You should have received a copy of the GNU General Public License
116
+ along with this program; if not, see
117
+ <[https://www.gnu.org/licenses/](https://www.gnu.org/licenses/)\>.
data/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # Options Parser
2
+
3
+ A simple, clean command-line option parser for Ruby.
4
+
5
+ ## Overview
6
+
7
+ Options Parser provides a straightforward way to define and parse command-line arguments in Ruby applications. It supports short and long flags, typed values, required options, and automatic help generation with colorized output.
8
+
9
+ ## Installation
10
+
11
+ Add to your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'options_parser'
15
+ ```
16
+
17
+ Or install directly:
18
+
19
+ ```bash
20
+ gem install options_parser
21
+ ```
22
+
23
+ **Requires Ruby 3.2+**
24
+
25
+ ## Usage
26
+
27
+ ```ruby
28
+ require 'options_parser'
29
+
30
+ options = {}
31
+
32
+ parser = OptionsParser::Parser.new(command: "myapp", description: "Does something useful") do |p|
33
+ p.on(short: "-f", long: "--file", value_type: :string, help: "Input file path") do |value|
34
+ options[:file] = value
35
+ end
36
+
37
+ p.on(short: "-n", long: "--count", value_type: :integer, help: "Number of iterations") do |value|
38
+ options[:count] = value
39
+ end
40
+
41
+ p.on(short: "-r", long: "--rate", value_type: :decimal, help: "Processing rate") do |value|
42
+ options[:rate] = value
43
+ end
44
+
45
+ p.on(short: "-v", long: "--verbose", help: "Enable verbose output") do |value|
46
+ options[:verbose] = value # value is `true` for flags without value_type
47
+ end
48
+
49
+ p.on(short: "-o", long: "--output", value_type: :string, required: true, help: "Output file (required)") do |value|
50
+ options[:output] = value
51
+ end
52
+ end
53
+
54
+ parser.parse(ARGV)
55
+
56
+ # Access trailing arguments (anything after --)
57
+ trailing = parser.trailing_values
58
+ ```
59
+ Options have the following attributes:
60
+
61
+ - `short` - the optional short flag
62
+ - `long` - the optional long flag
63
+ - `value_type` - tells the system that the argument takes a value,
64
+ and what kind of value it is. Must be specified if you want to guarantee
65
+ a value is passed.
66
+ - `required` - `true` / `false` defaults to false.
67
+ - `help` - additional details that will appear in the help docs
68
+
69
+ Either `short` or `long` _must_ be specified. It doesn't matter which, and specifying both is fine.
70
+
71
+
72
+ ### Command Line Examples
73
+
74
+ ```bash
75
+ # Short flags
76
+ myapp -f input.txt -n 10 -v -o output.txt
77
+
78
+ # Long flags
79
+ myapp --file=input.txt --count=10 --verbose --output=output.txt
80
+
81
+ # Mixed
82
+ myapp -f input.txt --count 10 -v -o output.txt
83
+
84
+ # With trailing arguments
85
+ myapp -f input.txt -o output.txt -- extra1 extra2 extra3
86
+ ```
87
+
88
+ ## Features
89
+
90
+ ### Value Types
91
+
92
+ - `:string` - Any string value
93
+ - `:integer` - Whole numbers only (validated)
94
+ - `:decimal` - Floating point numbers (validated, must include decimal point)
95
+
96
+ Options without a `value_type` are boolean flags that pass `true` to their block.
97
+
98
+ ### Required Options
99
+
100
+ Mark an option as required:
101
+
102
+ ```ruby
103
+ p.on(short: "-o", long: "--output", value_type: :string, required: true) do |value|
104
+ # ...
105
+ end
106
+ ```
107
+
108
+ The parser will display an error and usage information if required options are missing.
109
+
110
+ ### Trailing Values
111
+
112
+ Arguments after `--` are collected in `parser.trailing_values`:
113
+
114
+ ```bash
115
+ myapp -f file.txt -- arg1 arg2 arg3
116
+ ```
117
+
118
+ ```ruby
119
+ parser.trailing_values # => ["arg1", "arg2", "arg3"]
120
+ ```
121
+
122
+ ### Automatic Help
123
+
124
+ `-h` and `--help` are automatically handled, displaying usage information and exiting.
125
+
126
+ ### Direct Value Access
127
+
128
+ Option values can be accessed directly from the option object:
129
+
130
+ ```ruby
131
+ file_opt = parser.on(short: "-f", value_type: :string)
132
+ parser.parse(ARGV)
133
+ file_opt.value # => the parsed value
134
+ ```
135
+
136
+ ## Error Handling
137
+
138
+ Invalid options or values display colorized error messages followed by usage information:
139
+
140
+ - Missing required options
141
+ - Invalid option flags
142
+ - Type validation failures (e.g., non-integer passed to `:integer` type)
143
+
144
+ ## License
145
+
146
+ GPL-2.0-only
147
+
148
+ ## Authors
149
+
150
+ - masukomi
151
+ - Felipe Contreras
152
+
153
+ Filipe wrote [ruby-parseopt](https://github.com/felipec/ruby-parseopt). masukomi took that as a starting point and modified it until its core functionality was radically different. Now, it's its own thing.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/lefthook.yml ADDED
@@ -0,0 +1,20 @@
1
+ skip_output:
2
+ - meta
3
+ - skips
4
+ pre-commit:
5
+ parallel: true
6
+ commands:
7
+ rubocop:
8
+ exclude: "application.rb|routes.rb"
9
+ run: bundle exec rubocop -A --force-exclusion {staged_files}
10
+ stage_fixed: true
11
+ # run: bundle exec standardrb --fix {staged_files} && git add {staged_files}
12
+ tags: linting
13
+ scripts:
14
+ "bad_words":
15
+ exclude: "Gemfile|Gemfile.lock"
16
+ runner: bash
17
+ tags: bad_words
18
+ "rb_tester":
19
+ runner: ruby
20
+ tags: testing
@@ -0,0 +1,94 @@
1
+ require 'paint'
2
+
3
+ module OptionsParser
4
+ class Option
5
+ attr_reader :short, :long, :help, :required, :value
6
+
7
+ VALUE_TYPES = [:string, :decimal, :integer]
8
+
9
+ def initialize(short: nil, long: nil, value_type: nil, help: nil, required: false, &block)
10
+ @value_type = value_type;
11
+ @block = block
12
+ @short = short
13
+ @long = long
14
+ @help = help
15
+ @required = false if required.nil?
16
+ @value = nil
17
+ raise "Option must contain a short or long flag" if @short.nil? && @long.nil?
18
+ end
19
+
20
+ def matches_arg?(arg)
21
+ return [@short, @long].include?(arg)
22
+ end
23
+
24
+ def takes_value?
25
+ return ! @value_type.nil?
26
+ end
27
+
28
+ # @returns true if it doesn't take a value, or if its value has been found
29
+ def satisfied?
30
+ return ! @value.nil?
31
+ end
32
+
33
+ def missing_val_text()
34
+ usage_elements = []
35
+ usage_elements.push(short) if @short
36
+ usage_elements.push(" / ") if @short && @long
37
+ usage_elements.push(long) if @long
38
+ usage_elements.push("takes a #{@value_type}")
39
+ return usage_elements.join(" ")
40
+ end
41
+
42
+ def usage_text()
43
+ usage_elements = []
44
+ usage_elements.push("[") unless @required
45
+ usage_elements.push(short) if @short
46
+ usage_elements.push(", ") if @short && @long
47
+ usage_elements.push(long) if @long
48
+ if takes_value?
49
+ usage_elements.push("=#{@value_type.upcase}")
50
+ end
51
+ usage_elements.push("]") unless @required
52
+ usage_elements.join(" ")
53
+ end
54
+
55
+ def get_minimal_args
56
+ args = []
57
+ args.push(@short) unless @short.nil?
58
+ args.push("/") if !@short.nil? && !@long.nil?
59
+ args.push(@long) unless @long.nil?
60
+ args.join("")
61
+ end
62
+ def convert_value(value, type)
63
+ return value if type == :string
64
+ if type == :integer
65
+ raise InvalidValueException.new("#{get_minimal_args} value must be an integer") \
66
+ unless value.match?(/^\d+$/)
67
+ return value.to_i
68
+ end
69
+ if type == :decimal
70
+ raise InvalidValueException.new("#{get_minimal_args} value must be a decimal") \
71
+ unless value.match?(/^\d+\.\d+$/)
72
+ return value.to_f
73
+ end
74
+ raise "unsupported value type: #{type}"
75
+ end
76
+
77
+ # passes the converted value to the block.
78
+ # If no value is supplied by the user the block
79
+ # will be passed true
80
+ def call(value = nil)
81
+ if takes_value? && ! value.nil?
82
+ value = convert_value(value, @value_type)
83
+ elsif takes_value?
84
+ raise InvalidValueException.new("I was passed a nil value. Shouldn't happen.")
85
+ else
86
+ value = true
87
+ end
88
+ # setting value in case they want to reference it directly
89
+ # and to make satisfied? return true
90
+ @value = value
91
+ @block&.call(value)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,142 @@
1
+ require 'paint'
2
+ module OptionsParser
3
+ class Parser
4
+
5
+ attr_reader :command, :description, :options, :trailing_values
6
+ # Creates a new instance.
7
+ #
8
+ # Yields itself if called with a block.
9
+ #
10
+ # @param [String] usage usage banner.
11
+ #
12
+ # @yieldparam [ParseOpt] self Option parser object
13
+ def initialize(command:, description: nil)
14
+ @command = command
15
+ @description = description
16
+ @options = []
17
+ @trailing_values = []
18
+ yield self if block_given?
19
+ end
20
+
21
+ # Creates an option.
22
+ #
23
+ # @param [String] :short the short flag
24
+ #
25
+ def on(short: nil, long: nil, value_type: nil, help: nil, required: false, &block)
26
+ opt = OptionsParser::Option.new(short: short,
27
+ long: long,
28
+ value_type: value_type,
29
+ help: help,
30
+ required: required,
31
+ &block)
32
+ @options.push opt
33
+ opt
34
+ end
35
+
36
+ def find_opt_for_arg(arg)
37
+ @options.find{ |opt| opt.matches_arg?(arg) }
38
+ end
39
+
40
+ def usage_and_exit()
41
+ usage
42
+ exit 0
43
+ end
44
+
45
+ def test_satisfied_or_exit(option)
46
+ raise "No option passed" if option.nil?
47
+ if ! option.satisfied?
48
+ Paint[option.missing_val_text, :red]
49
+ usage_and_exit()
50
+ end
51
+ true
52
+ end
53
+
54
+ # Parses all the command line arguments
55
+ def parse(args = ARGV)
56
+ # list. key and value may be in separate elements
57
+ # e.g. ["-f", "from value"]
58
+ # or may be in same element separated by a =
59
+ # e.g. ["--from=from_value"]
60
+ separated_args = args.map{ |x| x.include?("=") ? x.split("=") : x }.flatten
61
+
62
+
63
+
64
+ if separated_args.include?('-h') or separated_args.include?('--help')
65
+ usage_and_exit()
66
+ end
67
+
68
+ begin
69
+ current_option = nil
70
+ separator_found = false
71
+ separated_args.each do | arg |
72
+ if separator_found
73
+ @trailing_values.push(arg)
74
+ next
75
+ end
76
+ hyphen_arg = arg.match?(/^-{1,2}\w+$/)
77
+ separator = hyphen_arg ? false : (arg == "--")
78
+
79
+ # our first argument flag?
80
+ if current_option.nil? && hyphen_arg
81
+ current_option = find_opt_for_arg(arg)
82
+ raise InvalidOptionException.new("#{arg} is not a supported option") unless current_option
83
+ next
84
+ # an argument flag when we've already encountered one
85
+ elsif current_option && hyphen_arg && test_satisfied_or_exit(current_option)
86
+ # execute the option's block
87
+ current_option = find_opt_for_arg(arg)
88
+ raise InvalidOptionException.new("#{arg} is not a supported option") unless current_option
89
+
90
+ elsif current_option && hyphen_arg
91
+ current_option.call()
92
+ next
93
+ # a separator ( no are options after this )
94
+ elsif separator && (current_option.nil? || test_satisfied_or_exit(current_option))
95
+ separator_found = true
96
+ next
97
+ end
98
+
99
+ # still here?
100
+ # not an option flag, or separator,
101
+ if !separator_found
102
+ # must be a value that we'll give to the current_option
103
+ current_option&.call(arg)
104
+ else
105
+ @trailing_values.push(arg)
106
+ end
107
+ end
108
+ # if there's still an option
109
+ # make sure it's satisfied and call its block
110
+ unless current_option.nil?
111
+ unless test_satisfied_or_exit(current_option)
112
+ current_option.call()
113
+ end
114
+ end
115
+ # end parsing args
116
+ rescue InvalidValueException, InvalidOptionException => e
117
+ puts Paint[e.message, :red]
118
+ usage()
119
+ exit(1)
120
+ end
121
+
122
+ end
123
+
124
+ # Generates the usage output (similar to `--help`)
125
+ def usage()
126
+ top_line_usage =[]
127
+ usage_body = []
128
+ @options.each do |opt|
129
+ usage_text = opt.usage_text
130
+ top_line_usage.push(usage_text)
131
+ usage_body.push(usage_text)
132
+ if opt.help
133
+ usage_body.push("\t" + help.split(/\n/).join("\n\t\t"))
134
+ end
135
+ end
136
+ puts "Usage: #{top_line_usage.join(" ")}"
137
+ puts @description unless @description.nil?
138
+ puts usage_body.join("\n")
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OptionsParser
4
+ VERSION = "1.0.1"
5
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "options_parser/version"
4
+ require_relative "options_parser/option"
5
+ require_relative "options_parser/parser"
6
+
7
+ module OptionsParser
8
+ class InvalidValueException < StandardError; end
9
+ class InvalidOptionException < StandardError; end
10
+
11
+ end
@@ -0,0 +1,4 @@
1
+ module OptionsParser
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: options_parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - masukomi
8
+ - Felipe Contreras
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: paint
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.3.0
27
+ description: Simple, but full featured option parsing for most project's needs
28
+ email:
29
+ - masukomi@masukomi.org
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".git-com.yaml"
35
+ - ".lefthook/pre-commit/bad_words"
36
+ - ".lefthook/pre-commit/rb_tester"
37
+ - CHANGELOG.md
38
+ - LICENSE.md
39
+ - README.md
40
+ - Rakefile
41
+ - lefthook.yml
42
+ - lib/options_parser.rb
43
+ - lib/options_parser/option.rb
44
+ - lib/options_parser/parser.rb
45
+ - lib/options_parser/version.rb
46
+ - sig/options_parser.rbs
47
+ homepage: https://github.com/masukomi/better_parseopt
48
+ licenses:
49
+ - GPL-2.0-only
50
+ metadata:
51
+ homepage_uri: https://github.com/masukomi/better_parseopt
52
+ source_code_uri: https://github.com/masukomi/better_parseopt
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 3.2.0
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 3.7.1
68
+ specification_version: 4
69
+ summary: A pretty decent option parser.
70
+ test_files: []