command_line_boss 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.
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CommandLineBoss
4
+ # Gem version
5
+ VERSION = '0.1.0'
6
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'optparse'
5
+
6
+ require 'command_line_boss/help_option'
7
+ require 'command_line_boss/logger_options'
8
+ require 'command_line_boss/version'
9
+
10
+ # Command line interface parser based on OptionsParser
11
+ #
12
+ # @api public
13
+ #
14
+ class CommandLineBoss
15
+ # Create a new command line parser
16
+ #
17
+ # @example
18
+ # parser = CommandLineParser.new
19
+ #
20
+ # @param program_name [String] the name of the program to report in the usage line
21
+ #
22
+ # The program name is used in the usage line when the --help option is given.
23
+ # This is given as an optional argument so that it can be overridden in tests.
24
+ #
25
+ def initialize(program_name: $PROGRAM_NAME)
26
+ @program_name = program_name
27
+ @parser = OptionParser.new.tap { |p| p.set_program_name(program_name) }
28
+ @error_messages = []
29
+ set_defaults if private_methods.include?(:set_defaults)
30
+ define_options
31
+ end
32
+
33
+ # Parse the command line arguments and return self
34
+ #
35
+ # The caller will have to check the {#failed?} to see if there were any errors.
36
+ #
37
+ # If there were errors, {#error_messages} will contain the error messages.
38
+ #
39
+ # @example
40
+ # parser = CommandLineParser.new.call(ARGV)
41
+ #
42
+ # @param args [Array<String>] the command line arguments
43
+ #
44
+ # @return [CommandLineParser] returns self
45
+ #
46
+ def parse(args)
47
+ @args = args.dup
48
+ parse_options
49
+ parse_arguments
50
+ validate if @error_messages.empty?
51
+ self
52
+ end
53
+
54
+ # Parse the command line arguments and return self
55
+ #
56
+ # If there were any errors parsing the command line arguments, this method
57
+ # will output the error messages to stderr and exit the program with a non-zero
58
+ # status code.
59
+ #
60
+ # @example
61
+ # parser = CommandLineParser.new.parse!(ARGV)
62
+ #
63
+ # @param args [Array<String>] the command line arguments
64
+ #
65
+ # @raise [SystemExit] if the command line arguments are invalid
66
+ #
67
+ # @return [CommandLineParser] returns self
68
+ #
69
+ def parse!(args)
70
+ parse(args)
71
+
72
+ if failed?
73
+ warn error_messages.join("\n")
74
+ exit 1
75
+ end
76
+
77
+ self
78
+ end
79
+
80
+ # The name of the program to report in the usage line
81
+ #
82
+ # The program name is used in the usage line when the --help option is given.
83
+ # This is given as an optional argument so that it can be overridden in tests.
84
+ #
85
+ # @example
86
+ # options.program_name #=> 'sync_paranoids_milestones'
87
+ #
88
+ # @return [String]
89
+ #
90
+ attr_reader :program_name
91
+
92
+ # The error messages generated by the validation methods
93
+ #
94
+ # @example
95
+ # options.error_messages #=> ["ERROR: The sheet 'Sheet1' was given more than once"]
96
+ #
97
+ # @return [Array<String>]
98
+ #
99
+ def error_messages = @error_messages.dup.freeze
100
+
101
+ # true if the parser encountered no errors
102
+ #
103
+ #
104
+ # @example
105
+ # options.succeeded? #=> true
106
+ #
107
+ # @return [Boolean]
108
+ #
109
+ def succeeded? = @error_messages.empty?
110
+
111
+ # true if the parser encountered errors
112
+ #
113
+ # @example
114
+ # options.failed? #=> false
115
+ #
116
+ # @return [Boolean]
117
+ #
118
+ def failed? = !succeeded?
119
+
120
+ private
121
+
122
+ # This is how you should add an error message to the error_messages array
123
+ #
124
+ # @example
125
+ # add_error_message("The sheet 'Sheet1' was given more than once")
126
+ # error_messages #=> ["ERROR: The sheet 'Sheet1' was given more than once"]
127
+ #
128
+ # @param message [String] the error message to add
129
+ #
130
+ # @return [void]
131
+ #
132
+ # @api private
133
+ #
134
+ def add_error_message(message)
135
+ @error_messages << "ERROR: #{message}"
136
+ end
137
+
138
+ # Ensure that any extra args are valid
139
+ #
140
+ # This method is expected to add a message to {add_error} if there are
141
+ # any unexpected arguments.
142
+ #
143
+ # For this command line, there should be no extra arguments.
144
+ #
145
+ # @return [void]
146
+ #
147
+ # @api private
148
+ #
149
+ def validate_remaining_args
150
+ return if args.empty?
151
+
152
+ add_error_message("Unexpected arguments: #{args.join(' ')}")
153
+ end
154
+
155
+ # The parse to use to parse the command line arguments
156
+ #
157
+ # @return [OptionParser]
158
+ #
159
+ # @api private
160
+ #
161
+ attr_reader :parser
162
+
163
+ # The command line args passed to the parser
164
+ #
165
+ # The parser will remove options and their arguments from this array
166
+ # as it parses them.
167
+ #
168
+ # After all options are parsed, this array will contain only the
169
+ # remaining arguments.
170
+ #
171
+ # @return [Array<String>]
172
+ #
173
+ # @api private
174
+ #
175
+ attr_reader :args
176
+
177
+ # Parse the command line arguments using the OptionParser
178
+ #
179
+ # The OptionParser will remove options and their arguments from the
180
+ # `args` array as it parses them. After all options are parsed, `args`
181
+ # will contain only the remaining arguments.
182
+ #
183
+ # @raise [SystemExit] if the command line arguments are invalid
184
+ #
185
+ # @return [void]
186
+ #
187
+ # @api private
188
+ #
189
+ def parse_options
190
+ parser.parse!(args)
191
+ rescue OptionParser::ParseError => e
192
+ add_error_message(e.message)
193
+ end
194
+
195
+ # Parse the arguments that remain after parsing options
196
+ #
197
+ # This method is expected to be overridden by subclasses to parse the
198
+ # remaining arguments. {args} will contain the remaining arguments. This
199
+ # method should remove any arguments it processes from {args}.
200
+ #
201
+ # @return [void]
202
+ #
203
+ # @api private
204
+ #
205
+ def parse_arguments; end
206
+
207
+ VALIDATION_METHOD_REGEXP = /^validate_(.+)$/
208
+
209
+ # Validate the command line options and remaining arguments
210
+ #
211
+ # Calls all validation methods defined by this class. Validation
212
+ # methods are private methods that start with `validate_`.
213
+ #
214
+ # If validation fails, a validation method is expected to output an error
215
+ # messages using `warn` and exit the program with a non-zero status code.
216
+ #
217
+ # @return [void]
218
+ #
219
+ # @api private
220
+ #
221
+ def validate
222
+ private_methods.select { |m| m.to_s.match?(VALIDATION_METHOD_REGEXP) }.each { |m| send(m) }
223
+ end
224
+
225
+ # Set the default values for the command line options
226
+ #
227
+ # @return [void]
228
+ #
229
+ # @api private
230
+ #
231
+ # def set_defaults; end
232
+
233
+ DEFINITION_METHOD_REGEXP = /^define_(.+)_option$/
234
+
235
+ # Define the command line options
236
+ #
237
+ # @return [void]
238
+ #
239
+ # @api private
240
+ #
241
+ def define_options
242
+ parser.separator 'Options:'
243
+ private_methods.select { |m| m.to_s.match?(DEFINITION_METHOD_REGEXP) }.each { |m| send(m) }
244
+ parser.separator ''
245
+ end
246
+ end
metadata ADDED
@@ -0,0 +1,266 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: command_line_boss
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - James Couball
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-07-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler-audit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: create_github_release
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: csv
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: fuubar
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '13.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '13.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.13'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.13'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.64'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.64'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.22'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.22'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov-lcov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.8'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.8'
139
+ - !ruby/object:Gem::Dependency
140
+ name: turnip
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '4.4'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '4.4'
153
+ - !ruby/object:Gem::Dependency
154
+ name: redcarpet
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '3.6'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '3.6'
167
+ - !ruby/object:Gem::Dependency
168
+ name: yard
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '0.9'
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: 0.9.28
177
+ type: :development
178
+ prerelease: false
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - "~>"
182
+ - !ruby/object:Gem::Version
183
+ version: '0.9'
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ version: 0.9.28
187
+ - !ruby/object:Gem::Dependency
188
+ name: yardstick
189
+ requirement: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '0.9'
194
+ type: :development
195
+ prerelease: false
196
+ version_requirements: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - "~>"
199
+ - !ruby/object:Gem::Version
200
+ version: '0.9'
201
+ description: |
202
+ Command Line Boss is a convenience layer over OptionsParser. It provides a simple
203
+ way to define command line options and arguments, and then parse them into an options
204
+ object.
205
+ email:
206
+ - jcouball@yahoo.com
207
+ executables: []
208
+ extensions: []
209
+ extra_rdoc_files: []
210
+ files:
211
+ - ".rspec"
212
+ - ".rubocop.yml"
213
+ - ".yardopts"
214
+ - CHANGELOG.md
215
+ - CODE_OF_CONDUCT.md
216
+ - LICENSE.txt
217
+ - README.md
218
+ - Rakefile
219
+ - examples/create_spreadsheet/.gitignore
220
+ - examples/create_spreadsheet/.rspec
221
+ - examples/create_spreadsheet/.rubocop.yml
222
+ - examples/create_spreadsheet/Gemfile
223
+ - examples/create_spreadsheet/README.md
224
+ - examples/create_spreadsheet/Rakefile
225
+ - examples/create_spreadsheet/create_spreadsheet.gemspec
226
+ - examples/create_spreadsheet/exe/create-spreadsheet
227
+ - examples/create_spreadsheet/lib/create_spreadsheet.rb
228
+ - examples/create_spreadsheet/lib/create_spreadsheet/command_line.rb
229
+ - examples/create_spreadsheet/spec/create_spreadsheet/command_line.feature
230
+ - examples/create_spreadsheet/spec/create_spreadsheet/command_line_steps.rb
231
+ - examples/create_spreadsheet/spec/spec_helper.rb
232
+ - examples/readme_example/create-spreadsheet
233
+ - lib/command_line_boss.rb
234
+ - lib/command_line_boss/help_option.rb
235
+ - lib/command_line_boss/logger_options.rb
236
+ - lib/command_line_boss/version.rb
237
+ homepage: https://github.com/main-branch/command_line_boss
238
+ licenses:
239
+ - MIT
240
+ metadata:
241
+ allowed_push_host: https://rubygems.org
242
+ homepage_uri: https://github.com/main-branch/command_line_boss
243
+ source_code_uri: https://github.com/main-branch/command_line_boss
244
+ changelog_uri: https://rubydoc.info/gems/command_line_boss/0.1.0/file/CHANGELOG.md
245
+ documentation_uri: https://rubydoc.info/gems/command_line_boss/0.1.0
246
+ rubygems_mfa_required: 'true'
247
+ post_install_message:
248
+ rdoc_options: []
249
+ require_paths:
250
+ - lib
251
+ required_ruby_version: !ruby/object:Gem::Requirement
252
+ requirements:
253
+ - - ">="
254
+ - !ruby/object:Gem::Version
255
+ version: 3.1.0
256
+ required_rubygems_version: !ruby/object:Gem::Requirement
257
+ requirements:
258
+ - - ">="
259
+ - !ruby/object:Gem::Version
260
+ version: '0'
261
+ requirements: []
262
+ rubygems_version: 3.5.11
263
+ signing_key:
264
+ specification_version: 4
265
+ summary: Command Line Boss is a convenience layer over OptionsParser
266
+ test_files: []