docscribe 1.4.1 → 1.4.2
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 +4 -4
- data/README.md +149 -0
- data/lib/docscribe/cli/config_builder.rb +125 -35
- data/lib/docscribe/cli/generate.rb +288 -117
- data/lib/docscribe/cli/init.rb +49 -13
- data/lib/docscribe/cli/options.rb +302 -127
- data/lib/docscribe/cli/run.rb +391 -135
- data/lib/docscribe/cli.rb +23 -5
- data/lib/docscribe/config/defaults.rb +11 -11
- data/lib/docscribe/config/emit.rb +1 -0
- data/lib/docscribe/config/filtering.rb +24 -11
- data/lib/docscribe/config/loader.rb +7 -4
- data/lib/docscribe/config/plugin.rb +1 -0
- data/lib/docscribe/config/rbs.rb +31 -22
- data/lib/docscribe/config/sorbet.rb +41 -15
- data/lib/docscribe/config/sorting.rb +1 -0
- data/lib/docscribe/config/template.rb +1 -0
- data/lib/docscribe/config/utils.rb +1 -0
- data/lib/docscribe/config.rb +1 -0
- data/lib/docscribe/infer/constants.rb +15 -0
- data/lib/docscribe/infer/literals.rb +43 -25
- data/lib/docscribe/infer/names.rb +24 -15
- data/lib/docscribe/infer/params.rb +52 -6
- data/lib/docscribe/infer/raises.rb +24 -14
- data/lib/docscribe/infer/returns.rb +365 -182
- data/lib/docscribe/infer.rb +10 -9
- data/lib/docscribe/inline_rewriter/collector.rb +766 -375
- data/lib/docscribe/inline_rewriter/doc_block.rb +217 -74
- data/lib/docscribe/inline_rewriter/doc_builder.rb +1488 -602
- data/lib/docscribe/inline_rewriter/source_helpers.rb +100 -52
- data/lib/docscribe/inline_rewriter/tag_sorter.rb +109 -48
- data/lib/docscribe/inline_rewriter.rb +1009 -595
- data/lib/docscribe/plugin/base/collector_plugin.rb +2 -3
- data/lib/docscribe/plugin/base/tag_plugin.rb +1 -1
- data/lib/docscribe/plugin/registry.rb +34 -7
- data/lib/docscribe/plugin.rb +48 -17
- data/lib/docscribe/types/rbs/collection_loader.rb +0 -1
- data/lib/docscribe/types/rbs/provider.rb +75 -26
- data/lib/docscribe/types/rbs/type_formatter.rb +127 -59
- data/lib/docscribe/types/sorbet/base_provider.rb +31 -12
- data/lib/docscribe/version.rb +1 -1
- metadata +2 -2
|
@@ -14,6 +14,23 @@ module Docscribe
|
|
|
14
14
|
module Generate
|
|
15
15
|
PLUGIN_TYPES = %w[tag collector].freeze
|
|
16
16
|
|
|
17
|
+
NEXT_STEPS_TEMPLATE = <<~TEXT
|
|
18
|
+
Next steps:
|
|
19
|
+
1. Open %<path>s and implement the plugin logic.
|
|
20
|
+
%<hint>s
|
|
21
|
+
|
|
22
|
+
2. Register the plugin in your docscribe_plugins.rb:
|
|
23
|
+
|
|
24
|
+
require_relative '%<require_path>s'
|
|
25
|
+
Docscribe::Plugin::Registry.register(%<base_name>s.new)
|
|
26
|
+
|
|
27
|
+
3. Add the file to docscribe.yml:
|
|
28
|
+
|
|
29
|
+
plugins:
|
|
30
|
+
require:
|
|
31
|
+
- ./docscribe_plugins
|
|
32
|
+
TEXT
|
|
33
|
+
|
|
17
34
|
class << self
|
|
18
35
|
# Run the `generate` subcommand.
|
|
19
36
|
#
|
|
@@ -21,76 +38,61 @@ module Docscribe
|
|
|
21
38
|
# @raise [OptionParser::InvalidOption]
|
|
22
39
|
# @return [Integer] exit code
|
|
23
40
|
def run(argv)
|
|
24
|
-
opts =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
end
|
|
41
|
+
opts, parser = parse_generate_options(argv)
|
|
42
|
+
return 0 if opts[:help]
|
|
43
|
+
|
|
44
|
+
plugin_type, class_name = extract_generate_args(argv)
|
|
45
|
+
result = validate_generate_args(plugin_type, class_name, parser)
|
|
46
|
+
return result if result
|
|
47
|
+
|
|
48
|
+
content = render(plugin_type, class_name)
|
|
49
|
+
dispatch_output(content, plugin_type, class_name, opts)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Parse options for the generate subcommand.
|
|
55
|
+
#
|
|
56
|
+
# @private
|
|
57
|
+
# @param [Array<String>] argv
|
|
58
|
+
# @raise [OptionParser::InvalidOption]
|
|
59
|
+
# @return [Array(Hash, OptionParser)]
|
|
60
|
+
def parse_generate_options(argv)
|
|
61
|
+
opts = { output: nil, stdout: false, help: false }
|
|
62
|
+
parser = build_option_parser(opts)
|
|
47
63
|
|
|
48
64
|
begin
|
|
49
65
|
parser.parse!(argv)
|
|
50
66
|
rescue OptionParser::InvalidOption => e
|
|
51
67
|
warn e.message
|
|
52
68
|
warn parser
|
|
53
|
-
return 1
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
plugin_type = argv.shift
|
|
57
|
-
class_name = argv.shift
|
|
58
|
-
|
|
59
|
-
unless plugin_type && class_name
|
|
60
|
-
warn 'Error: both <type> and <PluginName> are required.'
|
|
61
|
-
warn parser
|
|
62
|
-
return 1
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
unless PLUGIN_TYPES.include?(plugin_type)
|
|
66
|
-
warn "Error: unknown type #{plugin_type.inspect}. Must be one of: #{PLUGIN_TYPES.join(', ')}."
|
|
67
|
-
return 1
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
unless valid_constant?(class_name)
|
|
71
|
-
warn "Error: #{class_name.inspect} is not a valid Ruby constant name."
|
|
72
|
-
return 1
|
|
73
69
|
end
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if opts[:stdout]
|
|
78
|
-
puts content
|
|
79
|
-
return 0
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
write_plugin(content, plugin_type: plugin_type, class_name: class_name, output_dir: opts[:output] || '.')
|
|
71
|
+
[opts, parser]
|
|
83
72
|
end
|
|
84
73
|
|
|
85
|
-
|
|
74
|
+
# Extract plugin_type and class_name from remaining argv.
|
|
75
|
+
#
|
|
76
|
+
# @private
|
|
77
|
+
# @param [Array<String>] argv
|
|
78
|
+
# @return [Array(String, String)]
|
|
79
|
+
def extract_generate_args(argv)
|
|
80
|
+
[argv.shift, argv.shift]
|
|
81
|
+
end
|
|
86
82
|
|
|
87
|
-
#
|
|
83
|
+
# Validate generate arguments and return exit code on failure.
|
|
88
84
|
#
|
|
89
85
|
# @private
|
|
90
|
-
# @param [String]
|
|
91
|
-
# @
|
|
92
|
-
|
|
93
|
-
|
|
86
|
+
# @param [String, nil] plugin_type
|
|
87
|
+
# @param [String, nil] class_name
|
|
88
|
+
# @param [OptionParser] parser
|
|
89
|
+
# @return [Integer, nil] exit code or nil if valid
|
|
90
|
+
def validate_generate_args(plugin_type, class_name, parser)
|
|
91
|
+
return 1 unless args_provided?(plugin_type, class_name, parser)
|
|
92
|
+
return 1 unless known_type?(plugin_type)
|
|
93
|
+
return 1 unless valid_name?(class_name)
|
|
94
|
+
|
|
95
|
+
nil
|
|
94
96
|
end
|
|
95
97
|
|
|
96
98
|
# Render plugin boilerplate for the given type and class name.
|
|
@@ -106,66 +108,6 @@ module Docscribe
|
|
|
106
108
|
end
|
|
107
109
|
end
|
|
108
110
|
|
|
109
|
-
# Write the generated content to a file.
|
|
110
|
-
#
|
|
111
|
-
# @private
|
|
112
|
-
# @param [String] content
|
|
113
|
-
# @param [String] plugin_type
|
|
114
|
-
# @param [String] class_name
|
|
115
|
-
# @param [String] output_dir
|
|
116
|
-
# @return [Integer] exit code
|
|
117
|
-
def write_plugin(content, plugin_type:, class_name:, output_dir:)
|
|
118
|
-
filename = "#{underscore(class_name)}.rb"
|
|
119
|
-
path = File.join(output_dir, filename)
|
|
120
|
-
|
|
121
|
-
if File.exist?(path)
|
|
122
|
-
warn "Error: #{path} already exists. Remove it first or use --stdout."
|
|
123
|
-
return 1
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
require 'fileutils'
|
|
127
|
-
FileUtils.mkdir_p(output_dir)
|
|
128
|
-
File.write(path, content)
|
|
129
|
-
puts "Created: #{path}"
|
|
130
|
-
puts
|
|
131
|
-
puts next_steps(plugin_type, path)
|
|
132
|
-
0
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# Print registration instructions after file creation.
|
|
136
|
-
#
|
|
137
|
-
# @private
|
|
138
|
-
# @param [String] plugin_type
|
|
139
|
-
# @param [String] path
|
|
140
|
-
# @return [String]
|
|
141
|
-
def next_steps(plugin_type, path)
|
|
142
|
-
base_name = File.basename(path, '.rb').split('_').map(&:capitalize).join
|
|
143
|
-
|
|
144
|
-
implement_hint = case plugin_type
|
|
145
|
-
when 'tag'
|
|
146
|
-
'Implement the #call method to return Array<Docscribe::Plugin::Tag>.'
|
|
147
|
-
when 'collector'
|
|
148
|
-
'Implement the #collect method to return Array<{anchor_node:, doc:}>.'
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
<<~TEXT
|
|
152
|
-
Next steps:
|
|
153
|
-
1. Open #{path} and implement the plugin logic.
|
|
154
|
-
#{implement_hint}
|
|
155
|
-
|
|
156
|
-
2. Register the plugin in your docscribe_plugins.rb:
|
|
157
|
-
|
|
158
|
-
require_relative '#{path.delete_suffix('.rb')}'
|
|
159
|
-
Docscribe::Plugin::Registry.register(#{base_name}.new)
|
|
160
|
-
|
|
161
|
-
3. Add the file to docscribe.yml:
|
|
162
|
-
|
|
163
|
-
plugins:
|
|
164
|
-
require:
|
|
165
|
-
- ./docscribe_plugins
|
|
166
|
-
TEXT
|
|
167
|
-
end
|
|
168
|
-
|
|
169
111
|
# Template for a TagPlugin.
|
|
170
112
|
#
|
|
171
113
|
# @private
|
|
@@ -292,6 +234,161 @@ module Docscribe
|
|
|
292
234
|
RUBY
|
|
293
235
|
end
|
|
294
236
|
|
|
237
|
+
# Write generated plugin to a file or print to STDOUT based on options.
|
|
238
|
+
#
|
|
239
|
+
# @private
|
|
240
|
+
# @param [String] content generated plugin source code
|
|
241
|
+
# @param [String] plugin_type 'tag' or 'collector'
|
|
242
|
+
# @param [String] class_name CamelCase plugin class name
|
|
243
|
+
# @param [Hash] opts parsed options hash
|
|
244
|
+
# @return [Integer] exit code
|
|
245
|
+
def dispatch_output(content, plugin_type, class_name, opts)
|
|
246
|
+
if opts[:stdout]
|
|
247
|
+
puts content
|
|
248
|
+
return 0
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
write_plugin(content, plugin_type: plugin_type, class_name: class_name, output_dir: opts[:output] || '.')
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Write the generated content to a file.
|
|
255
|
+
#
|
|
256
|
+
# @private
|
|
257
|
+
# @param [String] content
|
|
258
|
+
# @param [String] plugin_type
|
|
259
|
+
# @param [String] class_name
|
|
260
|
+
# @param [String] output_dir
|
|
261
|
+
# @return [Integer] exit code
|
|
262
|
+
def write_plugin(content, plugin_type:, class_name:, output_dir:)
|
|
263
|
+
path = plugin_path(class_name, output_dir)
|
|
264
|
+
|
|
265
|
+
return 1 if file_exists?(path)
|
|
266
|
+
|
|
267
|
+
write_to_file(output_dir, path, content)
|
|
268
|
+
print_created(plugin_type, path)
|
|
269
|
+
0
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Build the OptionParser for the generate subcommand.
|
|
273
|
+
#
|
|
274
|
+
# @private
|
|
275
|
+
# @param [Hash] opts mutable parsed options hash
|
|
276
|
+
# @return [OptionParser]
|
|
277
|
+
def build_option_parser(opts)
|
|
278
|
+
OptionParser.new do |opt|
|
|
279
|
+
opt.banner = parser_banner
|
|
280
|
+
register_output_option(opt, opts)
|
|
281
|
+
register_stdout_option(opt, opts)
|
|
282
|
+
register_help_option(opt, opts)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Return the usage banner for the generate subcommand parser.
|
|
287
|
+
#
|
|
288
|
+
# @private
|
|
289
|
+
# @return [String]
|
|
290
|
+
def parser_banner
|
|
291
|
+
<<~TEXT
|
|
292
|
+
Usage: docscribe generate <type> <PluginName> [options]
|
|
293
|
+
|
|
294
|
+
Types:
|
|
295
|
+
tag Generate a TagPlugin skeleton
|
|
296
|
+
collector Generate a CollectorPlugin skeleton
|
|
297
|
+
|
|
298
|
+
Options:
|
|
299
|
+
TEXT
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Register the --output option on the OptionParser.
|
|
303
|
+
#
|
|
304
|
+
# @private
|
|
305
|
+
# @param [OptionParser] opt
|
|
306
|
+
# @param [Hash] opts mutable parsed options hash
|
|
307
|
+
# @return [void]
|
|
308
|
+
def register_output_option(opt, opts)
|
|
309
|
+
opt.on('--output DIR', 'Directory to write the plugin file (default: .)') { |v| opts[:output] = v }
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Register the --stdout option on the OptionParser.
|
|
313
|
+
#
|
|
314
|
+
# @private
|
|
315
|
+
# @param [OptionParser] opt
|
|
316
|
+
# @param [Hash] opts mutable parsed options hash
|
|
317
|
+
# @return [void]
|
|
318
|
+
def register_stdout_option(opt, opts)
|
|
319
|
+
opt.on('--stdout', 'Print the generated plugin to STDOUT instead of writing a file') { opts[:stdout] = true }
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Register the -h/--help option on the OptionParser.
|
|
323
|
+
#
|
|
324
|
+
# @private
|
|
325
|
+
# @param [OptionParser] opt
|
|
326
|
+
# @param [Hash] opts mutable parsed options hash
|
|
327
|
+
# @return [void]
|
|
328
|
+
def register_help_option(opt, opts)
|
|
329
|
+
opt.on('-h', '--help', 'Show this help') do
|
|
330
|
+
opts[:help] = true
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Validate that both plugin_type and class_name arguments were provided.
|
|
335
|
+
#
|
|
336
|
+
# @private
|
|
337
|
+
# @param [String, nil] plugin_type plugin type argument
|
|
338
|
+
# @param [String, nil] class_name plugin class name argument
|
|
339
|
+
# @param [OptionParser] parser
|
|
340
|
+
# @return [Boolean]
|
|
341
|
+
def args_provided?(plugin_type, class_name, parser)
|
|
342
|
+
return true if plugin_type && class_name
|
|
343
|
+
|
|
344
|
+
warn 'Error: both <type> and <PluginName> are required.'
|
|
345
|
+
warn parser
|
|
346
|
+
false
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Validate that the plugin type is one of the recognized types.
|
|
350
|
+
#
|
|
351
|
+
# @private
|
|
352
|
+
# @param [String] plugin_type plugin type to validate
|
|
353
|
+
# @return [Boolean]
|
|
354
|
+
def known_type?(plugin_type)
|
|
355
|
+
return true if PLUGIN_TYPES.include?(plugin_type)
|
|
356
|
+
|
|
357
|
+
warn "Error: unknown type #{plugin_type.inspect}. Must be one of: #{PLUGIN_TYPES.join(', ')}."
|
|
358
|
+
false
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Validate that the class name is a valid Ruby constant name.
|
|
362
|
+
#
|
|
363
|
+
# @private
|
|
364
|
+
# @param [String] class_name class name to validate
|
|
365
|
+
# @return [Boolean]
|
|
366
|
+
def valid_name?(class_name)
|
|
367
|
+
return true if valid_constant?(class_name)
|
|
368
|
+
|
|
369
|
+
warn "Error: #{class_name.inspect} is not a valid Ruby constant name."
|
|
370
|
+
false
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Check whether a string is a valid Ruby constant name.
|
|
374
|
+
#
|
|
375
|
+
# @private
|
|
376
|
+
# @param [String] str
|
|
377
|
+
# @return [Boolean]
|
|
378
|
+
def valid_constant?(str)
|
|
379
|
+
!!(str =~ /\A[A-Z][A-Za-z0-9]*(?:::[A-Z][A-Za-z0-9]*)*\z/)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# Build the file path for the generated plugin.
|
|
383
|
+
#
|
|
384
|
+
# @private
|
|
385
|
+
# @param [String] class_name CamelCase plugin class name
|
|
386
|
+
# @param [String] output_dir output directory
|
|
387
|
+
# @return [String] full file path
|
|
388
|
+
def plugin_path(class_name, output_dir)
|
|
389
|
+
File.join(output_dir || '.', "#{underscore(class_name || '')}.rb")
|
|
390
|
+
end
|
|
391
|
+
|
|
295
392
|
# Convert CamelCase to snake_case for file naming.
|
|
296
393
|
#
|
|
297
394
|
# @private
|
|
@@ -303,6 +400,80 @@ module Docscribe
|
|
|
303
400
|
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
304
401
|
.downcase
|
|
305
402
|
end
|
|
403
|
+
|
|
404
|
+
# Check whether the target plugin file already exists and warn if so.
|
|
405
|
+
#
|
|
406
|
+
# @private
|
|
407
|
+
# @param [String] path file path to check
|
|
408
|
+
# @return [Boolean]
|
|
409
|
+
def file_exists?(path)
|
|
410
|
+
return false unless File.exist?(path)
|
|
411
|
+
|
|
412
|
+
warn "Error: #{path} already exists. Remove it first or use --stdout."
|
|
413
|
+
true
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Create the output directory and write the plugin file.
|
|
417
|
+
#
|
|
418
|
+
# @private
|
|
419
|
+
# @param [String] output_dir output directory path
|
|
420
|
+
# @param [String] path full plugin file path
|
|
421
|
+
# @param [String] content file content to write
|
|
422
|
+
# @return [void]
|
|
423
|
+
def write_to_file(output_dir, path, content)
|
|
424
|
+
require 'fileutils'
|
|
425
|
+
FileUtils.mkdir_p(output_dir || '.')
|
|
426
|
+
File.write(path, content)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# Print the creation message and next steps after generating a plugin.
|
|
430
|
+
#
|
|
431
|
+
# @private
|
|
432
|
+
# @param [String] plugin_type 'tag' or 'collector'
|
|
433
|
+
# @param [String] path file path of the created plugin
|
|
434
|
+
# @return [void]
|
|
435
|
+
def print_created(plugin_type, path)
|
|
436
|
+
puts "Created: #{path}"
|
|
437
|
+
puts
|
|
438
|
+
puts next_steps(plugin_type, path)
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Print registration instructions after file creation.
|
|
442
|
+
#
|
|
443
|
+
# @private
|
|
444
|
+
# @param [String] plugin_type
|
|
445
|
+
# @param [String] path
|
|
446
|
+
# @return [String]
|
|
447
|
+
def next_steps(plugin_type, path)
|
|
448
|
+
format(NEXT_STEPS_TEMPLATE,
|
|
449
|
+
path: path,
|
|
450
|
+
hint: generate_implement_hint(plugin_type),
|
|
451
|
+
require_path: path.delete_suffix('.rb'),
|
|
452
|
+
base_name: plugin_base_name(path))
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# Derive a CamelCase base name from a snake_case file path.
|
|
456
|
+
#
|
|
457
|
+
# @private
|
|
458
|
+
# @param [String] path file path
|
|
459
|
+
# @return [String] CamelCase class name
|
|
460
|
+
def plugin_base_name(path)
|
|
461
|
+
File.basename(path, '.rb').split('_').map(&:capitalize).join
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# Generate an implementation hint string for the given plugin type.
|
|
465
|
+
#
|
|
466
|
+
# @private
|
|
467
|
+
# @param [String] plugin_type 'tag' or 'collector'
|
|
468
|
+
# @return [String] hint text
|
|
469
|
+
def generate_implement_hint(plugin_type)
|
|
470
|
+
case plugin_type
|
|
471
|
+
when 'tag'
|
|
472
|
+
'Implement the #call method to return Array<Docscribe::Plugin::Tag>.'
|
|
473
|
+
when 'collector'
|
|
474
|
+
'Implement the #collect method to return Array<{anchor_node:, doc:}>.'
|
|
475
|
+
end
|
|
476
|
+
end
|
|
306
477
|
end
|
|
307
478
|
end
|
|
308
479
|
end
|
data/lib/docscribe/cli/init.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'docscribe/config'
|
|
|
5
5
|
|
|
6
6
|
module Docscribe
|
|
7
7
|
module CLI
|
|
8
|
+
# Generate starter Docscribe configuration.
|
|
8
9
|
module Init
|
|
9
10
|
class << self
|
|
10
11
|
# Create or print a starter Docscribe configuration file.
|
|
@@ -18,30 +19,65 @@ module Docscribe
|
|
|
18
19
|
# @param [Array<String>] argv command-line arguments for `docscribe init`
|
|
19
20
|
# @return [Integer] process exit code
|
|
20
21
|
def run(argv)
|
|
21
|
-
opts =
|
|
22
|
-
|
|
23
|
-
force: false,
|
|
24
|
-
stdout: false
|
|
25
|
-
}
|
|
22
|
+
opts = parse_init_options(argv)
|
|
23
|
+
return 0 if opts[:help]
|
|
26
24
|
|
|
25
|
+
yaml = Docscribe::Config.default_yaml
|
|
26
|
+
|
|
27
|
+
if opts[:stdout]
|
|
28
|
+
puts yaml
|
|
29
|
+
return 0
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
write_init_config(opts, yaml)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Parse CLI options for `docscribe init`.
|
|
38
|
+
#
|
|
39
|
+
# @private
|
|
40
|
+
# @param [Array<String>] argv
|
|
41
|
+
# @return [Hash] parsed options
|
|
42
|
+
def parse_init_options(argv)
|
|
43
|
+
opts = default_init_options
|
|
44
|
+
build_init_parser(opts).parse!(argv)
|
|
45
|
+
opts
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Return the default options hash for the init command.
|
|
49
|
+
#
|
|
50
|
+
# @private
|
|
51
|
+
# @return [Hash]
|
|
52
|
+
def default_init_options
|
|
53
|
+
{ config: 'docscribe.yml', force: false, stdout: false, help: false }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Build and return an OptionParser for the init command.
|
|
57
|
+
#
|
|
58
|
+
# @private
|
|
59
|
+
# @param [Hash] opts options hash that the parser populates
|
|
60
|
+
# @return [OptionParser]
|
|
61
|
+
def build_init_parser(opts)
|
|
27
62
|
OptionParser.new do |o|
|
|
28
63
|
o.banner = 'Usage: docscribe init [options]'
|
|
29
64
|
o.on('--config PATH', 'Where to write the config (default: docscribe.yml)') { |v| opts[:config] = v }
|
|
30
65
|
o.on('-f', '--force', 'Overwrite if the file already exists') { opts[:force] = true }
|
|
31
66
|
o.on('--stdout', 'Print config template to STDOUT instead of writing a file') { opts[:stdout] = true }
|
|
32
67
|
o.on('-h', '--help', 'Show this help') do
|
|
68
|
+
opts[:help] = true
|
|
33
69
|
puts o
|
|
34
|
-
return 0
|
|
35
70
|
end
|
|
36
|
-
end.parse!(argv)
|
|
37
|
-
|
|
38
|
-
yaml = Docscribe::Config.default_yaml
|
|
39
|
-
|
|
40
|
-
if opts[:stdout]
|
|
41
|
-
puts yaml
|
|
42
|
-
return 0
|
|
43
71
|
end
|
|
72
|
+
end
|
|
44
73
|
|
|
74
|
+
# Write the config template to a file.
|
|
75
|
+
#
|
|
76
|
+
# @private
|
|
77
|
+
# @param [Hash] opts parsed options
|
|
78
|
+
# @param [String] yaml config template content
|
|
79
|
+
# @return [Integer] exit code
|
|
80
|
+
def write_init_config(opts, yaml)
|
|
45
81
|
path = opts[:config]
|
|
46
82
|
if File.exist?(path) && !opts[:force]
|
|
47
83
|
warn "Config already exists: #{path} (use --force to overwrite)"
|