command_line_boss 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +34 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +34 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +340 -0
- data/Rakefile +90 -0
- data/examples/create_spreadsheet/.gitignore +21 -0
- data/examples/create_spreadsheet/.rspec +3 -0
- data/examples/create_spreadsheet/.rubocop.yml +34 -0
- data/examples/create_spreadsheet/Gemfile +16 -0
- data/examples/create_spreadsheet/README.md +314 -0
- data/examples/create_spreadsheet/Rakefile +50 -0
- data/examples/create_spreadsheet/create_spreadsheet.gemspec +39 -0
- data/examples/create_spreadsheet/exe/create-spreadsheet +28 -0
- data/examples/create_spreadsheet/lib/create_spreadsheet/command_line.rb +221 -0
- data/examples/create_spreadsheet/lib/create_spreadsheet.rb +8 -0
- data/examples/create_spreadsheet/spec/create_spreadsheet/command_line.feature +239 -0
- data/examples/create_spreadsheet/spec/create_spreadsheet/command_line_steps.rb +93 -0
- data/examples/create_spreadsheet/spec/spec_helper.rb +130 -0
- data/examples/readme_example/create-spreadsheet +64 -0
- data/lib/command_line_boss/help_option.rb +82 -0
- data/lib/command_line_boss/logger_options.rb +103 -0
- data/lib/command_line_boss/version.rb +6 -0
- data/lib/command_line_boss.rb +246 -0
- metadata +266 -0
@@ -0,0 +1,239 @@
|
|
1
|
+
Feature: CreateSpreadsheet::Cli#parse
|
2
|
+
|
3
|
+
Parse the create-spreadsheet command line
|
4
|
+
|
5
|
+
Example: Nothing is given on the command line
|
6
|
+
Given the command line ""
|
7
|
+
When the command line is parsed
|
8
|
+
Then the parser should have succeeded
|
9
|
+
And the title attribute should be nil
|
10
|
+
And the sheets attribute should be empty
|
11
|
+
And the permissions attribute should be empty
|
12
|
+
And the folder_id attribute should be nil
|
13
|
+
|
14
|
+
Example: A spreadsheet title is given
|
15
|
+
Given the command line "MySpreadsheet"
|
16
|
+
When the command line is parsed
|
17
|
+
Then the parser should have succeeded
|
18
|
+
And the title attribute should be "MySpreadsheet"
|
19
|
+
|
20
|
+
Example: A sheet is defined with a title
|
21
|
+
Given the command line "--sheet=Summary"
|
22
|
+
When the command line is parsed
|
23
|
+
Then the parser should have succeeded
|
24
|
+
And the sheets attribute should contain the following Sheets:
|
25
|
+
"""
|
26
|
+
[
|
27
|
+
{
|
28
|
+
title: "Summary",
|
29
|
+
data: nil
|
30
|
+
}
|
31
|
+
]
|
32
|
+
"""
|
33
|
+
|
34
|
+
Example: A sheet is defined with a title and data
|
35
|
+
Given the command line "--sheet=Summary --data=data1.csv"
|
36
|
+
And a file "data1.csv" containing:
|
37
|
+
"""
|
38
|
+
1,2,3
|
39
|
+
4,5,6
|
40
|
+
"""
|
41
|
+
When the command line is parsed
|
42
|
+
Then the parser should have succeeded
|
43
|
+
And the sheets attribute should contain the following Sheets:
|
44
|
+
"""
|
45
|
+
[
|
46
|
+
{
|
47
|
+
"title": "Summary",
|
48
|
+
"data": [["1", "2", "3"], ["4", "5", "6"]]
|
49
|
+
}
|
50
|
+
]
|
51
|
+
"""
|
52
|
+
|
53
|
+
Example: Multiple sheets are defined both with data
|
54
|
+
Given the command line "--sheet=Summary --data=summary.csv --sheet=Detail --data=detail.csv"
|
55
|
+
And a file "summary.csv" containing:
|
56
|
+
"""
|
57
|
+
6
|
58
|
+
"""
|
59
|
+
And a file "detail.csv" containing:
|
60
|
+
"""
|
61
|
+
1
|
62
|
+
2
|
63
|
+
3
|
64
|
+
"""
|
65
|
+
When the command line is parsed
|
66
|
+
Then the parser should have succeeded
|
67
|
+
And the sheets attribute should contain the following Sheets:
|
68
|
+
"""
|
69
|
+
[
|
70
|
+
{ "title": "Summary", "data": [["6"]] },
|
71
|
+
{ "title": "Detail", "data": [["1"], ["2"], ["3"]] }
|
72
|
+
]
|
73
|
+
"""
|
74
|
+
|
75
|
+
Example: Multiple sheets are defined only one with data
|
76
|
+
Given the command line "--sheet=Summary --sheet=Detail --data=detail.csv"
|
77
|
+
And a file "detail.csv" containing:
|
78
|
+
"""
|
79
|
+
Name,Age
|
80
|
+
John,25
|
81
|
+
Jane,23
|
82
|
+
"""
|
83
|
+
When the command line is parsed
|
84
|
+
Then the parser should have succeeded
|
85
|
+
And the sheets attribute should contain the following Sheets:
|
86
|
+
"""
|
87
|
+
[
|
88
|
+
{ title: "Summary", data: nil },
|
89
|
+
{ title: "Detail", data: [["Name", "Age"], ["John", "25"], ["Jane", "23"]] }
|
90
|
+
]
|
91
|
+
"""
|
92
|
+
|
93
|
+
Example: A user permission is given
|
94
|
+
Given the command line "--permission=user:bob@example.com:reader"
|
95
|
+
When the command line is parsed
|
96
|
+
Then the parser should have succeeded
|
97
|
+
And the permissions attribute should contain the following Permissions:
|
98
|
+
"""
|
99
|
+
[
|
100
|
+
{
|
101
|
+
"permission_spec": "user:bob@example.com:reader",
|
102
|
+
"type": "user", "subject": "bob@example.com", "role": "reader"
|
103
|
+
}
|
104
|
+
]
|
105
|
+
"""
|
106
|
+
|
107
|
+
Example: A group permission is given
|
108
|
+
Given the command line "--permission=group:admins@example.com:writer"
|
109
|
+
|
110
|
+
When the command line is parsed
|
111
|
+
Then the parser should have succeeded
|
112
|
+
And the permissions attribute should contain the following Permissions:
|
113
|
+
"""
|
114
|
+
[
|
115
|
+
{
|
116
|
+
"permission_spec": "group:admins@example.com:writer",
|
117
|
+
"type": "group", "subject": "admins@example.com", "role": "writer"
|
118
|
+
}
|
119
|
+
]
|
120
|
+
"""
|
121
|
+
|
122
|
+
Example: A domain permission is given
|
123
|
+
Given the command line "--permission=domain:domain_name:reader"
|
124
|
+
When the command line is parsed
|
125
|
+
Then the parser should have succeeded
|
126
|
+
And the permissions attribute should contain the following Permissions:
|
127
|
+
"""
|
128
|
+
[
|
129
|
+
{
|
130
|
+
"permission_spec": "domain:domain_name:reader",
|
131
|
+
"type": "domain", "subject": "domain_name", "role": "reader"
|
132
|
+
}
|
133
|
+
]
|
134
|
+
"""
|
135
|
+
|
136
|
+
Example: An anyone permission is given
|
137
|
+
Given the command line "--permission=anyone:reader"
|
138
|
+
When the command line is parsed
|
139
|
+
Then the parser should have succeeded
|
140
|
+
And the permissions attribute should contain the following Permissions:
|
141
|
+
"""
|
142
|
+
[
|
143
|
+
{
|
144
|
+
permission_spec: "anyone:reader", type: "anyone", subject: nil, role: "reader"
|
145
|
+
}
|
146
|
+
]
|
147
|
+
"""
|
148
|
+
|
149
|
+
Example: Multiple permissions are given
|
150
|
+
Given the command line "--permission=user:bob@example.com:writer --permission=anyone:reader"
|
151
|
+
When the command line is parsed
|
152
|
+
Then the parser should have succeeded
|
153
|
+
And the permissions attribute should contain the following Permissions:
|
154
|
+
"""
|
155
|
+
[
|
156
|
+
{
|
157
|
+
permission_spec: "user:bob@example.com:writer", type: "user", subject: "bob@example.com", role: "writer"
|
158
|
+
},
|
159
|
+
{
|
160
|
+
permission_spec: "anyone:reader", type: "anyone", subject: nil, role: "reader"
|
161
|
+
}
|
162
|
+
]
|
163
|
+
"""
|
164
|
+
|
165
|
+
Example: A folder is given
|
166
|
+
Given the command line "--folder=0ALLuhm2AwwlJUk9PVA"
|
167
|
+
When the command line is parsed
|
168
|
+
Then the parser should have succeeded
|
169
|
+
And the folder_id attribute should be "0ALLuhm2AwwlJUk9PVA"
|
170
|
+
|
171
|
+
# Failure cases
|
172
|
+
|
173
|
+
Example: A sheet given without a name
|
174
|
+
Given the command line "--sheet"
|
175
|
+
When the command line is parsed
|
176
|
+
Then the parser should have failed with the error "ERROR: missing argument: --sheet"
|
177
|
+
|
178
|
+
Example: A permission is given without a permission spec
|
179
|
+
Given the command line "--permission"
|
180
|
+
When the command line is parsed
|
181
|
+
Then the parser should have failed with the error "ERROR: missing argument: --permission"
|
182
|
+
|
183
|
+
Example: A invalid permission is given
|
184
|
+
Given the command line "--permission=anyone-writer"
|
185
|
+
When the command line is parsed
|
186
|
+
Then the parser should have failed with the error "ERROR: Invalid permission: anyone-writer"
|
187
|
+
|
188
|
+
Example: The permission spec has an invalid type
|
189
|
+
Given the command line "--permission=invalid:test@example.com:reader"
|
190
|
+
When the command line is parsed
|
191
|
+
Then the parser should have failed with the error "ERROR: Invalid permission type: invalid"
|
192
|
+
|
193
|
+
Example: A permission spec has an invalid role
|
194
|
+
Given the command line "--permission=user:test@example.com:invalid"
|
195
|
+
When the command line is parsed
|
196
|
+
Then the parser should have failed with the error "ERROR: Invalid permission role: invalid"
|
197
|
+
|
198
|
+
Example: A subject is given for an anyone permission
|
199
|
+
Given the command line "--permission=anyone:test@example.com:reader"
|
200
|
+
When the command line is parsed
|
201
|
+
Then the parser should have failed with the error "ERROR: An anyone permission must not have a subject"
|
202
|
+
|
203
|
+
Example: A subject is not given for a user permission
|
204
|
+
Given the command line "--permission=user:writer"
|
205
|
+
When the command line is parsed
|
206
|
+
Then the parser should have failed with the error "ERROR: A user permission must have a subject"
|
207
|
+
|
208
|
+
Example: Data is given without a path
|
209
|
+
Given the command line "--sheet=Summary --data"
|
210
|
+
When the command line is parsed
|
211
|
+
Then the parser should have failed with the error "ERROR: missing argument: --data"
|
212
|
+
|
213
|
+
Example: Data is given with an non-existant path
|
214
|
+
Given the command line "--sheet=Summary --data=nonexistent.csv"
|
215
|
+
And the file "nonexistent.csv" does not exist
|
216
|
+
When the command line is parsed
|
217
|
+
Then the parser should have failed with the error "ERROR: Data file not found: nonexistent.csv"
|
218
|
+
|
219
|
+
Example: The folder option is given twice
|
220
|
+
Given the command line "--folder=0ALLuhm2AwwlJUk9PVA --folder=0ALLuhm2AwwlJUk9PVA"
|
221
|
+
When the command line is parsed
|
222
|
+
|
223
|
+
Example: A same sheet name is given twice
|
224
|
+
Given the command line "--sheet=Summary --sheet=Summary"
|
225
|
+
When the command line is parsed
|
226
|
+
Then the parser should have failed with the error "ERROR: The sheet Summary was given more than once"
|
227
|
+
|
228
|
+
Example: Data is given twice for the same sheet
|
229
|
+
Given the command line "--sheet=Summary --data=data1.csv --data=data2.csv"
|
230
|
+
And a file "data1.csv" containing:
|
231
|
+
"""
|
232
|
+
1
|
233
|
+
"""
|
234
|
+
And a file "data2.csv" containing:
|
235
|
+
"""
|
236
|
+
2
|
237
|
+
"""
|
238
|
+
When the command line is parsed
|
239
|
+
Then the parser should have failed with the error "ERROR: Only one data file is allowed per sheet"
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
step 'the command line :command_line' do |command_line|
|
4
|
+
@args = command_line.split
|
5
|
+
end
|
6
|
+
|
7
|
+
# step 'the command line is parsed' do
|
8
|
+
# @options = CreateSpreadsheetCli.new.parse(@args)
|
9
|
+
# end
|
10
|
+
|
11
|
+
step 'the command line is parsed' do
|
12
|
+
stdout = StringIO.new
|
13
|
+
stderr = StringIO.new
|
14
|
+
original_stdout = $stdout
|
15
|
+
original_stderr = $stderr
|
16
|
+
|
17
|
+
begin
|
18
|
+
$stdout = stdout
|
19
|
+
$stderr = stderr
|
20
|
+
@options = CreateSpreadsheet::CommandLine.new.parse(@args)
|
21
|
+
rescue StandardError => e
|
22
|
+
@error = e
|
23
|
+
@options = e.parser
|
24
|
+
ensure
|
25
|
+
$stdout = original_stdout
|
26
|
+
$stderr = original_stderr
|
27
|
+
end
|
28
|
+
|
29
|
+
@stdout = stdout.string
|
30
|
+
@stderr = stderr.string
|
31
|
+
end
|
32
|
+
|
33
|
+
step 'the parser should have succeeded' do
|
34
|
+
expect(@options.error_messages).to eq([])
|
35
|
+
expect(@error).to be_nil
|
36
|
+
expect(@stdout).to be_empty
|
37
|
+
expect(@stderr).to be_empty
|
38
|
+
end
|
39
|
+
|
40
|
+
step 'the parser should have failed' do
|
41
|
+
expect(@options.error_messages).not_to be_empty
|
42
|
+
expect(@error).to be_nil
|
43
|
+
expect(@stdout).to be_empty
|
44
|
+
expect(@stderr).to be_empty
|
45
|
+
end
|
46
|
+
|
47
|
+
step 'the parser should have failed with the error :message' do |message|
|
48
|
+
expect(@options.error_messages).not_to be_empty
|
49
|
+
expect(@error).to be_nil
|
50
|
+
expect(@stdout).to be_empty
|
51
|
+
expect(@stderr).to be_empty
|
52
|
+
expect(@options.error_messages).to include(message)
|
53
|
+
end
|
54
|
+
|
55
|
+
step 'the :attribute attribute should be nil' do |attribute|
|
56
|
+
expect(@options.send(attribute.to_sym)).to be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
step 'the :attribute attribute should be empty' do |attribute|
|
60
|
+
expect(@options.send(attribute.to_sym)).to be_empty
|
61
|
+
end
|
62
|
+
|
63
|
+
step 'the :attribute attribute should be ":value"' do |attribute, value|
|
64
|
+
expect(@options.send(attribute.to_sym)).to eq(value)
|
65
|
+
end
|
66
|
+
|
67
|
+
step 'the sheets attribute should contain the following Sheets:' do |hash_as_string|
|
68
|
+
# rubocop:disable Security/Eval
|
69
|
+
expected_sheets = eval(hash_as_string).map { |s| CreateSpreadsheet::Sheet.new(s) }
|
70
|
+
# rubocop:enable Security/Eval
|
71
|
+
expect(@options.sheets).to match_array(expected_sheets)
|
72
|
+
end
|
73
|
+
|
74
|
+
RSpec.configure do |config|
|
75
|
+
config.before(type: :feature) do
|
76
|
+
allow(File).to receive(:read).and_call_original
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
step 'a file :path containing:' do |path, content|
|
81
|
+
allow(File).to receive(:read).with(path).and_return(content)
|
82
|
+
end
|
83
|
+
|
84
|
+
step 'the file :path does not exist' do |path|
|
85
|
+
allow(File).to receive(:read).with(path).and_raise(Errno::ENOENT, "No such file or directory @ rb_sysopen - #{path}")
|
86
|
+
end
|
87
|
+
|
88
|
+
step 'the permissions attribute should contain the following Permissions:' do |hash_as_string|
|
89
|
+
# rubocop:disable Security/Eval
|
90
|
+
expected_permissions = eval(hash_as_string).map { |p| CreateSpreadsheet::Permission.new(p) }
|
91
|
+
# rubocop:enable Security/Eval
|
92
|
+
expect(@options.permissions).to match_array(expected_permissions)
|
93
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Turnip setup
|
4
|
+
#
|
5
|
+
require 'turnip/rspec'
|
6
|
+
Dir[File.join(__dir__, '**/*_steps.rb')].each { |f| require f }
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
# Enable flags like --only-failures and --next-failure
|
10
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
11
|
+
|
12
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
13
|
+
config.disable_monkey_patching!
|
14
|
+
|
15
|
+
config.expect_with :rspec do |c|
|
16
|
+
c.syntax = :expect
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Setup simplecov
|
21
|
+
|
22
|
+
require 'simplecov'
|
23
|
+
require 'simplecov-lcov'
|
24
|
+
require 'json'
|
25
|
+
|
26
|
+
SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, SimpleCov::Formatter::LcovFormatter]
|
27
|
+
|
28
|
+
# Return `true` if the environment variable is set to a truthy value
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# env_true?('COV_SHOW_UNCOVERED')
|
32
|
+
#
|
33
|
+
# @param name [String] the name of the environment variable
|
34
|
+
# @return [Boolean]
|
35
|
+
#
|
36
|
+
def env_true?(name)
|
37
|
+
value = ENV.fetch(name, '').downcase
|
38
|
+
%w[yes on true 1].include?(value)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return `true` if the environment variable is NOT set to a truthy value
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# env_false?('COV_NO_FAIL')
|
45
|
+
#
|
46
|
+
# @param name [String] the name of the environment variable
|
47
|
+
# @return [Boolean]
|
48
|
+
#
|
49
|
+
def env_false?(name)
|
50
|
+
!env_true?(name)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return `true` if the the test run should fail if the coverage is below the threshold
|
54
|
+
#
|
55
|
+
# @return [Boolean]
|
56
|
+
#
|
57
|
+
def fail_on_low_coverage?
|
58
|
+
!(RSpec.configuration.dry_run? || env_true?('COV_NO_FAIL'))
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return `true` if the the test run should show the lines not covered by tests
|
62
|
+
#
|
63
|
+
# @return [Boolean]
|
64
|
+
#
|
65
|
+
def show_lines_not_covered?
|
66
|
+
env_true?('COV_SHOW_UNCOVERED')
|
67
|
+
end
|
68
|
+
|
69
|
+
# Report if the test coverage was below the configured threshold
|
70
|
+
#
|
71
|
+
# The threshold is configured by setting the `test_coverage_threshold` variable
|
72
|
+
# in this file.
|
73
|
+
#
|
74
|
+
# Example:
|
75
|
+
#
|
76
|
+
# ```Ruby
|
77
|
+
# test_coverage_threshold = 100
|
78
|
+
# ```
|
79
|
+
#
|
80
|
+
# Coverage below the threshold will cause the rspec run to fail unless the
|
81
|
+
# `COV_NO_FAIL` environment variable is set to TRUE.
|
82
|
+
#
|
83
|
+
# ```Shell
|
84
|
+
# COV_NO_FAIL=TRUE rspec
|
85
|
+
# ```
|
86
|
+
#
|
87
|
+
# Example of running the tests in an infinite loop writing failures to `fail.txt`:
|
88
|
+
#
|
89
|
+
# ```Shell
|
90
|
+
# while true; do COV_NO_FAIL=TRUE rspec >> fail.txt; done
|
91
|
+
# ````
|
92
|
+
#
|
93
|
+
# The lines missing coverage will be displayed if the `COV_SHOW_UNCOVERED`
|
94
|
+
# environment variable is set to TRUE.
|
95
|
+
#
|
96
|
+
# ```Shell
|
97
|
+
# COV_SHOW_UNCOVERED=TRUE rspec
|
98
|
+
# ```
|
99
|
+
#
|
100
|
+
test_coverage_threshold = 100
|
101
|
+
|
102
|
+
SimpleCov.at_exit do
|
103
|
+
SimpleCov.result.format!
|
104
|
+
# rubocop:disable Style/StderrPuts
|
105
|
+
if SimpleCov.result.covered_percent < test_coverage_threshold
|
106
|
+
$stderr.puts
|
107
|
+
$stderr.print 'FAIL: ' if fail_on_low_coverage?
|
108
|
+
$stderr.puts "RSpec Test coverage fell below #{test_coverage_threshold}%"
|
109
|
+
|
110
|
+
if show_lines_not_covered?
|
111
|
+
$stderr.puts "\nThe following lines were not covered by tests:\n"
|
112
|
+
SimpleCov.result.files.each do |source_file| # SimpleCov::SourceFile
|
113
|
+
source_file.missed_lines.each do |line| # SimpleCov::SourceFile::Line
|
114
|
+
$stderr.puts " .#{source_file.project_filename}:#{line.number}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
$stderr.puts
|
120
|
+
|
121
|
+
exit 1 if fail_on_low_coverage?
|
122
|
+
end
|
123
|
+
# rubocop:enable Style/StderrPuts
|
124
|
+
end
|
125
|
+
|
126
|
+
SimpleCov.start do
|
127
|
+
enable_coverage :branch
|
128
|
+
end
|
129
|
+
|
130
|
+
require 'create_spreadsheet'
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'command_line_boss'
|
5
|
+
|
6
|
+
# Define a command line interface for creating a spreadsheet
|
7
|
+
class CreateSpreadsheetCli < CommandLineBoss
|
8
|
+
attr_reader :spreadsheet_name, :sheet_names
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def set_defaults
|
13
|
+
@spreadsheet_name = nil
|
14
|
+
@sheet_names = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_sheet_option
|
18
|
+
parser.on('--sheet=SHEET_NAME', 'Name of a sheet to create') do |name|
|
19
|
+
add_error_message('Sheet names must be unique!') if sheet_names.include?(name)
|
20
|
+
sheet_names << name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_spreadsheet_name_given
|
25
|
+
add_error_message('A spreadsheet name is required') if spreadsheet_name.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_at_least_one_sheet_name_given
|
29
|
+
add_error_message('At least one sheet name is required') if sheet_names.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_arguments
|
33
|
+
@spreadsheet_name = args.shift
|
34
|
+
end
|
35
|
+
|
36
|
+
include CommandLineBoss::HelpOption
|
37
|
+
|
38
|
+
def banner = <<~BANNER
|
39
|
+
Create a spreadsheetasdf
|
40
|
+
|
41
|
+
Usage:
|
42
|
+
create_spreadsheet SPREADSHEET_NAME --sheet=SHEET_NAME [--sheet=SHEET_NAME ...]
|
43
|
+
|
44
|
+
BANNER
|
45
|
+
end
|
46
|
+
|
47
|
+
# Parse the command line arguments
|
48
|
+
|
49
|
+
options = CreateSpreadsheetCli.new.parse(ARGV)
|
50
|
+
|
51
|
+
# Report errors
|
52
|
+
|
53
|
+
if options.failed?
|
54
|
+
warn options.error_messages.join("\n")
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|
58
|
+
# Do something with the result
|
59
|
+
|
60
|
+
require 'pp'
|
61
|
+
|
62
|
+
puts \
|
63
|
+
"Creating spreadsheet #{options.spreadsheet_name.pretty_inspect.chomp} " \
|
64
|
+
"with sheets #{options.sheet_names.map(&:pretty_inspect).map(&:chomp).join(', ')}"
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CommandLineBoss
|
4
|
+
# Add the --help option
|
5
|
+
module HelpOption
|
6
|
+
private
|
7
|
+
|
8
|
+
# Define the command line options
|
9
|
+
#
|
10
|
+
# @return [void]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
def define_options
|
15
|
+
add_banner
|
16
|
+
add_header
|
17
|
+
super
|
18
|
+
add_footer
|
19
|
+
end
|
20
|
+
|
21
|
+
# Define the --help option
|
22
|
+
#
|
23
|
+
# @return [void]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
#
|
27
|
+
def define_help_option
|
28
|
+
parser.on('-h', '--help', 'Show this message') do
|
29
|
+
puts parser.help
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Adds the banner to the parser output
|
35
|
+
# @return [void]
|
36
|
+
# @api private
|
37
|
+
#
|
38
|
+
def add_banner
|
39
|
+
return unless banner
|
40
|
+
|
41
|
+
parser.banner = banner
|
42
|
+
end
|
43
|
+
|
44
|
+
# Derived classes should override this method to provide a banner
|
45
|
+
# @return [String, nil]
|
46
|
+
# @api private
|
47
|
+
#
|
48
|
+
def banner = nil
|
49
|
+
|
50
|
+
# Adds the header to the parser output
|
51
|
+
# @return [void]
|
52
|
+
# @api private
|
53
|
+
#
|
54
|
+
def add_header
|
55
|
+
return unless header
|
56
|
+
|
57
|
+
parser.separator header
|
58
|
+
end
|
59
|
+
|
60
|
+
# Derived classes should override this method to provide a header
|
61
|
+
# @return [String, nil]
|
62
|
+
# @api private
|
63
|
+
#
|
64
|
+
def header = nil
|
65
|
+
|
66
|
+
# Adds the footer to the parser output
|
67
|
+
# @return [void]
|
68
|
+
# @api private
|
69
|
+
#
|
70
|
+
def add_footer
|
71
|
+
return unless footer
|
72
|
+
|
73
|
+
parser.separator footer
|
74
|
+
end
|
75
|
+
|
76
|
+
# Derived classes should override this method to provide a footer
|
77
|
+
# @return [String, nil]
|
78
|
+
# @api private
|
79
|
+
#
|
80
|
+
def footer = nil
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CommandLineBoss
|
4
|
+
# Add --debug and --verbose options and a logger method
|
5
|
+
module LoggerOptions
|
6
|
+
# true if the --debug option was given
|
7
|
+
#
|
8
|
+
# @return [Boolean]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
attr_reader :debug
|
13
|
+
|
14
|
+
# true if the --verbose option was given
|
15
|
+
#
|
16
|
+
# @return [Boolean]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
#
|
20
|
+
attr_reader :verbose
|
21
|
+
|
22
|
+
# The logger to use to report progress
|
23
|
+
#
|
24
|
+
# Messages are logged at info and debug levels. The logger returned is one of
|
25
|
+
# the following:
|
26
|
+
#
|
27
|
+
# * A logger that logs to the console at the :info level if verbose mode is enabled
|
28
|
+
# * A logger that logs to the console at the :debug level if debug mode is enabled
|
29
|
+
# * Otherwise a null logger that does not log anything
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# options.logger #=> #<Logger:0x00007f9e3b8b3e08>
|
33
|
+
#
|
34
|
+
# @return [Logger]
|
35
|
+
#
|
36
|
+
def logger
|
37
|
+
@logger ||=
|
38
|
+
if verbose
|
39
|
+
verbose_logger
|
40
|
+
elsif debug
|
41
|
+
debug_logger
|
42
|
+
else
|
43
|
+
Logger.new(nil, level: Logger::UNKNOWN + 1)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Define the --verbose option
|
50
|
+
#
|
51
|
+
# @return [void]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
def define_verbose_option
|
56
|
+
parser.on('-v', '--verbose', 'Enable verbose mode (default is off)') do |verbose|
|
57
|
+
@verbose = verbose
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Define the --debug option
|
62
|
+
#
|
63
|
+
# @return [void]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
#
|
67
|
+
def define_debug_option
|
68
|
+
parser.on('-D', '--debug', 'Enable debug mode default is off') do |debug|
|
69
|
+
@debug = debug
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Ensure that the --debug and --verbose options are not both given
|
74
|
+
#
|
75
|
+
# @return [void]
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
#
|
79
|
+
def validate_debug_verbose_option
|
80
|
+
add_error_message('Can not give both --debug and --verbose') if debug && verbose
|
81
|
+
end
|
82
|
+
|
83
|
+
# A Logger that logs to the console at the :debug level with a simple formatter
|
84
|
+
#
|
85
|
+
# @return [Logger]
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
#
|
89
|
+
def debug_logger
|
90
|
+
Logger.new($stdout, level: 'debug', formatter: ->(_severity, _datetime, _progname, msg) { "#{msg}\n" })
|
91
|
+
end
|
92
|
+
|
93
|
+
# A Logger that logs to the console at the :info level with a simple formatter
|
94
|
+
#
|
95
|
+
# @return [Logger]
|
96
|
+
#
|
97
|
+
# @api private
|
98
|
+
#
|
99
|
+
def verbose_logger
|
100
|
+
Logger.new($stdout, level: 'info', formatter: ->(_severity, _datetime, _progname, msg) { "#{msg}\n" })
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|