commander-tools 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.
- checksums.yaml +7 -0
- data/lib/command.rb +147 -0
- metadata +57 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '05063299fe8d358adfd25b631a9e2f4d97fc1167171b0179731051606513fecf'
|
|
4
|
+
data.tar.gz: aac4e4fb0961d048c61d26082aaa5866577c5c2b111f2197c6f9846bb69665d8
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6560b4bae0716524907bc7cd9c574408923ee2a4455e2d19bfe91e5601aa0bbfddc6bfbe2f868382e377a70ac403d510a138faff8609a81f4db899f0bd9f1d50
|
|
7
|
+
data.tar.gz: 74234827b5c2ea05d6df688636ea7508b0c549fed9006ec29dce364265abec94a15a0af5102191937ec8bfe8bd58c4c75a94130715009f53b4be5ba1c89e4a6c
|
data/lib/command.rb
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
require 'did_you_mean'
|
|
2
|
+
|
|
3
|
+
class Commander
|
|
4
|
+
class Command
|
|
5
|
+
attr_reader :name, :description, :options, :block
|
|
6
|
+
|
|
7
|
+
def initialize(name, description = '', &block)
|
|
8
|
+
@name = name.to_s
|
|
9
|
+
@description = description
|
|
10
|
+
@options = []
|
|
11
|
+
@block = block
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def add_option(switches, description = '')
|
|
15
|
+
@options << Option.new(switches, description)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Option
|
|
20
|
+
attr_reader :switches, :description, :key
|
|
21
|
+
|
|
22
|
+
def initialize(switches, description)
|
|
23
|
+
@switches = switches # e.g., ["-v", "--version"]
|
|
24
|
+
@description = description
|
|
25
|
+
@key = switches.find { |s| s.start_with?('--') }&.sub(/^--/, '')&.tr('-', '_') ||
|
|
26
|
+
switches.find { |s| s.start_with?('-') }&.sub(/^-/, '')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def matches?(arg)
|
|
30
|
+
@switches.include?(arg)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def initialize
|
|
35
|
+
@commands = {}
|
|
36
|
+
@global_options = []
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def add_command(name, description = '', &block)
|
|
40
|
+
cmd = Command.new(name, description, &block)
|
|
41
|
+
@commands[cmd.name] = cmd
|
|
42
|
+
cmd
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def add_option(switches, description = '')
|
|
46
|
+
@global_options << Option.new(switches, description)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def parse(args = ARGV)
|
|
50
|
+
global_parsed, remaining_args = parse_options(args, @global_options)
|
|
51
|
+
|
|
52
|
+
command_name = remaining_args.shift
|
|
53
|
+
|
|
54
|
+
if command_name.nil?
|
|
55
|
+
handle_no_command(global_parsed)
|
|
56
|
+
return
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if @commands.key?(command_name)
|
|
60
|
+
command = @commands[command_name]
|
|
61
|
+
cmd_parsed, command_args = parse_options(remaining_args, command.options)
|
|
62
|
+
|
|
63
|
+
combined_options = global_parsed.merge(cmd_parsed)
|
|
64
|
+
|
|
65
|
+
if command.block
|
|
66
|
+
command.block.call(combined_options, command_args)
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
raise_unknown_command(command_name)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def display_usage
|
|
74
|
+
puts "Available commands:"
|
|
75
|
+
@commands.each do |name, cmd|
|
|
76
|
+
puts " #{name} - #{cmd.description}"
|
|
77
|
+
cmd.options.each do |opt|
|
|
78
|
+
puts " #{opt.switches.join(', ')}: #{opt.description}"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
puts "\nGlobal options:"
|
|
82
|
+
@global_options.each do |opt|
|
|
83
|
+
puts " #{opt.switches.join(', ')}: #{opt.description}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def parse_options(args, available_options)
|
|
90
|
+
parsed = {}
|
|
91
|
+
remaining = []
|
|
92
|
+
i = 0
|
|
93
|
+
|
|
94
|
+
while i < args.length
|
|
95
|
+
arg = args[i]
|
|
96
|
+
if arg.start_with?('-')
|
|
97
|
+
option = available_options.find { |opt| opt.matches?(arg) }
|
|
98
|
+
if option
|
|
99
|
+
if args[i + 1] && !args[i + 1].start_with?('-')
|
|
100
|
+
parsed[option.key.to_sym] = args[i + 1]
|
|
101
|
+
i += 2
|
|
102
|
+
else
|
|
103
|
+
parsed[option.key.to_sym] = true
|
|
104
|
+
i += 1
|
|
105
|
+
end
|
|
106
|
+
else
|
|
107
|
+
raise_unknown_option(arg, available_options)
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
remaining << arg
|
|
111
|
+
i += 1
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
[parsed, remaining]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def raise_unknown_command(command_name)
|
|
119
|
+
corrector = DidYouMean::SpellChecker.new(dictionary: @commands.keys)
|
|
120
|
+
suggestions = corrector.correct(command_name)
|
|
121
|
+
|
|
122
|
+
msg = "error: unknown command '#{command_name}'"
|
|
123
|
+
msg += "\n\nDid you mean?\n\t#{suggestions.join("\n\t")}" unless suggestions.empty?
|
|
124
|
+
|
|
125
|
+
abort msg
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def raise_unknown_option(option_name, available_options)
|
|
129
|
+
all_switches = available_options.flat_map(&:switches)
|
|
130
|
+
corrector = DidYouMean::SpellChecker.new(dictionary: all_switches)
|
|
131
|
+
suggestions = corrector.correct(option_name)
|
|
132
|
+
|
|
133
|
+
msg = "error: unknown option '#{option_name}'"
|
|
134
|
+
msg += "\n\nDid you mean?\n\t#{suggestions.join("\n\t")}" unless suggestions.empty?
|
|
135
|
+
|
|
136
|
+
abort msg
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def handle_no_command(options)
|
|
140
|
+
if options[:version] || options[:v]
|
|
141
|
+
puts "Commander CLI v1.0.0"
|
|
142
|
+
else
|
|
143
|
+
puts "error: no command specified."
|
|
144
|
+
display_usage
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: commander-tools
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Your Name
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-09-16 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: did_you_mean
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: Commander-tools gem providing CLI command parsing with suggestions
|
|
27
|
+
email:
|
|
28
|
+
- dodi66412@gmail.com
|
|
29
|
+
executables: []
|
|
30
|
+
extensions: []
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
files:
|
|
33
|
+
- lib/command.rb
|
|
34
|
+
homepage: https://github.com/jjjm03299-wq/didactic-parakeet
|
|
35
|
+
licenses:
|
|
36
|
+
- MIT
|
|
37
|
+
metadata:
|
|
38
|
+
homepage_uri: https://github.com/jjjm03299-wq/didactic-parakeet
|
|
39
|
+
source_code_uri: https://github.com/jjjm03299-wq/didactic-parakeet
|
|
40
|
+
rdoc_options: []
|
|
41
|
+
require_paths:
|
|
42
|
+
- lib
|
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '2.5'
|
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
requirements: []
|
|
54
|
+
rubygems_version: 3.6.2
|
|
55
|
+
specification_version: 4
|
|
56
|
+
summary: A custom CLI commander tools gem
|
|
57
|
+
test_files: []
|