command_line_boss 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []