command_kit 0.4.0 → 0.4.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72a3fdeb65d9eb4c4e996aa95ea7e8131e220c2cb24be8b17d56e1742a156d35
4
- data.tar.gz: d05b75366f58c219c9680d07571a171239f0ab8f77580cb1c2d47c6322d7e74c
3
+ metadata.gz: 3a35e836c936c91aa916f33e691260a591d2e363de6a0fd3d9259150b86add15
4
+ data.tar.gz: fe2e2cc87a8a0fe889cba23b5567b307bb088fdf3ee4eff7d681694a981b5143
5
5
  SHA512:
6
- metadata.gz: 1cc85835b40fded3170ee21619620dbb988dd3427fa8cb48456f09a7991b6eed72299a81ba19be543c260557504d5636237993f33360d463d72a87600ca14441
7
- data.tar.gz: 9d57ec57d540547d2fa664faf8e3bb1e0c0aa06929162d3dac849f858911aa5e7610e8294b2189927d8633de8e9784079df548a3e900fd73c57f76e72c87ad9d
6
+ metadata.gz: da2676ab635edc9898457b1c532af6ac4c478c6f3a0ab19dfc42f0abc4ddd73ef4f473fe7699f8bed3ac5abec3381a7a9ca9c50736c4cc3697612ef36f3d6fea
7
+ data.tar.gz: d30a04ab5274f489ef3e534950301669d47112ee56d43b59e51c5e816f7de21fba72197d43f3362331c40f3e75a70e0c316d53fe12bf8960daf79efec270a372
@@ -12,6 +12,8 @@ jobs:
12
12
  ruby:
13
13
  - '3.0'
14
14
  - '3.1'
15
+ - '3.2'
16
+ - '3.3'
15
17
  # TODO: uncomment when jruby supports ruby >= 2.7
16
18
  # - jruby
17
19
  - truffleruby
data/.rubocop.yml CHANGED
@@ -152,3 +152,6 @@ Bundler/OrderedGems: { Enabled: false }
152
152
  Layout/SpaceInsideArrayLiteralBrackets: { Enabled: false }
153
153
 
154
154
  Naming/HeredocDelimiterNaming: { Enabled: false }
155
+
156
+ # I prefer to use explicit parenthesis for compound logical statements
157
+ Style/RedundantParentheses: { Enabled: false }
data/ChangeLog.md CHANGED
@@ -1,3 +1,15 @@
1
+ ### 0.4.1 / 2024-01-03
2
+
3
+ * Added more examples of how to define sub-commands and sub-sub-commands.
4
+
5
+ #### CommandKit::Options::Parser
6
+
7
+ * Do not override the command's `usage` if it's already been set.
8
+
9
+ #### CommandKit::Printing::Tables
10
+
11
+ * Format the table output as UTF-8 to allow UTF-8 data in the formatted table.
12
+
1
13
  ### 0.4.0 / 2022-11-11
2
14
 
3
15
  * Added {CommandKit::BugReport}.
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2021-2022 Hal Brodigan
1
+ Copyright (c) 2021-2024 Hal Brodigan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -253,7 +253,7 @@ Twitter.
253
253
 
254
254
  ## Copyright
255
255
 
256
- Copyright (c) 2021-2022 Hal Brodigan
256
+ Copyright (c) 2021-2024 Hal Brodigan
257
257
 
258
258
  See {file:LICENSE.txt} for details.
259
259
 
@@ -0,0 +1,47 @@
1
+ require 'command_kit/command'
2
+
3
+ module Foo
4
+ class CLI
5
+ class Config < CommandKit::Command
6
+ #
7
+ # The `config get` sub-command.
8
+ #
9
+ class Get < CommandKit::Command
10
+
11
+ usage '[options] NAME'
12
+
13
+ argument :name, required: false,
14
+ desc: 'Configuration variable name'
15
+
16
+ description 'Gets a configuration variable'
17
+
18
+ CONFIG = {
19
+ 'name' => 'John Smith',
20
+ 'email' => 'john.smith@example.com'
21
+ }
22
+
23
+ #
24
+ # Runs the `config get` sub-command.
25
+ #
26
+ # @param [String, nil] name
27
+ # The optional name argument.
28
+ #
29
+ def run(name=nil)
30
+ if name
31
+ unless CONFIG.has_key?(name)
32
+ print_error "unknown config variable: #{name}"
33
+ exit(1)
34
+ end
35
+
36
+ puts CONFIG.fetch(name)
37
+ else
38
+ CONFIG.each do |name,value|
39
+ puts "#{name}:\t#{value}"
40
+ end
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ require 'command_kit/command'
2
+
3
+ module Foo
4
+ class CLI
5
+ class Config < CommandKit::Command
6
+ #
7
+ # The `config set` sub-command.
8
+ #
9
+ class Set < CommandKit::Command
10
+
11
+ usage '[options] NAME'
12
+
13
+ argument :name, required: true,
14
+ desc: 'Configuration variable name to set'
15
+
16
+ argument :value, required: true,
17
+ desc: 'Configuration variable value to set'
18
+
19
+ description 'Sets a configuration variable'
20
+
21
+ CONFIG = {
22
+ 'name' => 'John Smith',
23
+ 'email' => 'john.smith@example.com'
24
+ }
25
+
26
+ #
27
+ # Runs the `config get` sub-command.
28
+ #
29
+ # @param [String] name
30
+ # The name argument.
31
+ #
32
+ def run(name,value)
33
+ unless CONFIG.has_key?(name)
34
+ print_error "unknown config variable: #{name}"
35
+ exit(1)
36
+ end
37
+
38
+ puts "Configuration variable #{name} was #{CONFIG.fetch(name)}, but is now #{value}"
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,23 @@
1
+ require 'command_kit/command'
2
+ require 'command_kit/commands'
3
+
4
+ require_relative 'config/get'
5
+ require_relative 'config/set'
6
+
7
+ module Foo
8
+ class CLI
9
+ #
10
+ # The `config` sub-command.
11
+ #
12
+ class Config < CommandKit::Command
13
+
14
+ include CommandKit::Commands
15
+
16
+ command Get
17
+ command Set
18
+
19
+ description 'Get or set the configuration'
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,35 @@
1
+ require 'command_kit/command'
2
+
3
+ module Foo
4
+ class CLI
5
+ #
6
+ # The `list` sub-command.
7
+ #
8
+ class List < CommandKit::Command
9
+
10
+ usage '[options] [NAME]'
11
+
12
+ argument :name, required: false,
13
+ desc: 'Optional name to list'
14
+
15
+ description 'Lists the contents'
16
+
17
+ ITEMS = %w[foo bar baz]
18
+
19
+ #
20
+ # Runs the `list` sub-command.
21
+ #
22
+ # @param [String, nil] name
23
+ # The optional name argument.
24
+ #
25
+ def run(name=nil)
26
+ if name
27
+ puts ITEMS.grep(name)
28
+ else
29
+ puts ITEMS
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ require 'command_kit/command'
2
+
3
+ module Foo
4
+ class CLI
5
+ #
6
+ # The `update` sub-command.
7
+ #
8
+ class Update < CommandKit::Command
9
+
10
+ usage '[options] [NAME]'
11
+
12
+ option :quiet, short: '-q',
13
+ desc: 'Suppresses logging messages'
14
+
15
+ argument :name, required: false,
16
+ desc: 'Optional name to update'
17
+
18
+ description 'Updates an item or all items'
19
+
20
+ ITEMS = %w[foo bar baz]
21
+
22
+ #
23
+ # Runs the `update` sub-command.
24
+ #
25
+ # @param [String, nil] name
26
+ # The optional name argument.
27
+ #
28
+ def run(name=nil)
29
+ if name
30
+ unless ITEMS.include?(name)
31
+ print_error "unknown item: #{name}"
32
+ exit(1)
33
+ end
34
+
35
+ puts "Updating #{name} ..." unless options[:quiet]
36
+ sleep 1
37
+ puts "Item #{name} updated." unless options[:quiet]
38
+ else
39
+ puts "Updating ..." unless options[:quiet]
40
+ sleep 2
41
+ puts "All items updated." unless options[:quiet]
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../../../lib',__FILE__))
4
+
5
+ require 'command_kit/commands'
6
+
7
+ require_relative 'cli/config'
8
+ require_relative 'cli/list'
9
+ require_relative 'cli/update'
10
+
11
+ module Foo
12
+ #
13
+ # The main CLI command.
14
+ #
15
+ class CLI
16
+
17
+ include CommandKit::Commands
18
+
19
+ class << self
20
+ # The global configuration file setting.
21
+ #
22
+ # @return [String, nil]
23
+ attr_accessor :config_file
24
+ end
25
+
26
+ command_name 'foo'
27
+
28
+ # Commands must be explicitly registered, unless
29
+ # CommandKit::Commands::AutoLoad.new(...) is included.
30
+ command Config
31
+ command List
32
+ command Update
33
+
34
+ # Commands may have aliases
35
+ command_aliases['ls'] = 'list'
36
+ command_aliases['up'] = 'update'
37
+
38
+ # Global options may be defined which are parsed before the sub-command's
39
+ # options are parsed and the sub-command is executed.
40
+ option :config_file, short: '-C',
41
+ value: {
42
+ type: String,
43
+ usage: 'FILE'
44
+ },
45
+ desc: 'Global option to set the config file' do |file|
46
+ CLI.config_file = file
47
+ end
48
+
49
+ end
50
+ end
51
+
52
+ if $0 == __FILE__
53
+ # Normally you would invoke Foo::CLI.start from a bin/ script.
54
+ Foo::CLI.start
55
+ end
@@ -20,7 +20,7 @@ module CommandKit
20
20
  module ModuleMethods
21
21
  #
22
22
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
23
- # {Env::Home} is being included into a class or a module..
23
+ # {Env::Home} is being included into a class or a module.
24
24
  #
25
25
  # @param [Class, Module] context
26
26
  # The class or module which is including {Home}.
@@ -103,7 +103,7 @@ module CommandKit
103
103
  new_string << word
104
104
  elsif scanner.scan(/[_-]+/)
105
105
  # skip
106
- elsif scanner.scan(/\//)
106
+ elsif scanner.scan(%r{/})
107
107
  new_string << '::'
108
108
  else
109
109
  raise(ArgumentError,"cannot convert string to CamelCase: #{scanner.string.inspect}")
@@ -50,7 +50,7 @@ module CommandKit
50
50
 
51
51
  if context.class == Module
52
52
  context.extend ModuleMethods
53
- else
53
+ elsif context.usage.nil?
54
54
  context.usage '[options]'
55
55
  end
56
56
  end
@@ -36,7 +36,7 @@ module CommandKit
36
36
  module ModuleMethods
37
37
  #
38
38
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
39
- # {OS} is being included into a class or a module..
39
+ # {OS} is being included into a class or a module.
40
40
  #
41
41
  # @param [Class, Module] context
42
42
  # The class or module which is including {OS}.
@@ -32,7 +32,7 @@ module CommandKit
32
32
  module ModuleMethods
33
33
  #
34
34
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether
35
- # {OS} is being included into a class or a module..
35
+ # {OS} is being included into a class or a module.
36
36
  #
37
37
  # @param [Class, Module] context
38
38
  # The class or module which is including {OS}.
@@ -87,7 +87,7 @@ module CommandKit
87
87
  column_border: ,
88
88
  joined_border: ,
89
89
  right_border: )
90
- line = String.new
90
+ line = String.new(encoding: Encoding::UTF_8)
91
91
  line << left_border
92
92
 
93
93
  @table.max_columns.times do |column_index|
@@ -191,7 +191,7 @@ module CommandKit
191
191
  # The formatted row line.
192
192
  #
193
193
  def format_row_line(row,line_index, justify: @style.justify)
194
- line = String.new
194
+ line = String.new(encoding: Encoding::UTF_8)
195
195
  line << @style.border.left_border if @style.border
196
196
 
197
197
  @table.max_columns.times do |column_index|
@@ -1,4 +1,4 @@
1
1
  module CommandKit
2
2
  # command_kit version
3
- VERSION = "0.4.0"
3
+ VERSION = "0.4.1"
4
4
  end
@@ -30,7 +30,7 @@ module CommandKit
30
30
  module ModuleMethods
31
31
  #
32
32
  # Extends {ClassMethods} or {ModuleMethods}, depending on whether {XDG} is
33
- # being included into a class or a module..
33
+ # being included into a class or a module.
34
34
  #
35
35
  # @param [Class, Module] context
36
36
  # The class or module which is including {XDG}.
@@ -155,6 +155,32 @@ describe CommandKit::Commands do
155
155
 
156
156
  let(:command_class) { TestCommands::TestCommands }
157
157
 
158
+ describe ".included" do
159
+ subject { command_class }
160
+
161
+ it "must set .usage to '[options] [COMMAND [ARGS...]]'" do
162
+ expect(subject.usage).to eq('[options] [COMMAND [ARGS...]]')
163
+ end
164
+
165
+ it "must add a 'command' argument" do
166
+ expect(subject.arguments[:command]).to_not be_nil
167
+ expect(subject.arguments[:command].required?).to be(false)
168
+ expect(subject.arguments[:command].desc).to eq('The command name to run')
169
+ end
170
+
171
+ it "must add a 'args' argument" do
172
+ expect(subject.arguments[:args]).to_not be_nil
173
+ expect(subject.arguments[:args].required?).to be(false)
174
+ expect(subject.arguments[:args].repeats?).to be(true)
175
+ expect(subject.arguments[:args].desc).to eq('Additional arguments for the command')
176
+ end
177
+
178
+ it "must add a 'help' command" do
179
+ expect(subject.commands['help']).to_not be(nil)
180
+ expect(subject.commands['help'].command).to be(described_class::Help)
181
+ end
182
+ end
183
+
158
184
  describe ".commands" do
159
185
  subject { command_class }
160
186
 
@@ -19,6 +19,25 @@ describe CommandKit::Options::Parser do
19
19
  it { expect(subject).to include(CommandKit::Main) }
20
20
  it { expect(subject).to include(CommandKit::Usage) }
21
21
  it { expect(subject.usage).to eq('[options]') }
22
+
23
+ context "when the command class already defines a usage string" do
24
+ module TestOptionParser
25
+ class TestCommandWithUsage
26
+ include CommandKit::Usage
27
+
28
+ usage '[options] ARGS...'
29
+
30
+ include CommandKit::Options::Parser
31
+ end
32
+ end
33
+
34
+ let(:command_class) { TestOptionParser::TestCommandWithUsage }
35
+ subject { command_class }
36
+
37
+ it "must not override the usage" do
38
+ expect(subject.usage).to eq('[options] ARGS...')
39
+ end
40
+ end
22
41
  end
23
42
 
24
43
  subject { command_class.new }
@@ -57,6 +57,16 @@ describe CommandKit::Printing::Tables::TableFormatter do
57
57
  )
58
58
  end
59
59
 
60
+ it "must encode the Strings as UTF-8" do
61
+ yielded_lines = []
62
+
63
+ subject.format do |line|
64
+ yielded_lines << line
65
+ end
66
+
67
+ expect(yielded_lines.map(&:encoding)).to all(be(Encoding::UTF_8))
68
+ end
69
+
60
70
  context "but when the table contains multi-line cells" do
61
71
  let(:rows) { multiline_rows }
62
72
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-11 00:00:00.000000000 Z
11
+ date: 2024-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -50,6 +50,12 @@ files:
50
50
  - examples/command.rb
51
51
  - examples/pager.rb
52
52
  - examples/printing/tables.rb
53
+ - examples/subcommands/cli.rb
54
+ - examples/subcommands/cli/config.rb
55
+ - examples/subcommands/cli/config/get.rb
56
+ - examples/subcommands/cli/config/set.rb
57
+ - examples/subcommands/cli/list.rb
58
+ - examples/subcommands/cli/update.rb
53
59
  - gemspec.yml
54
60
  - lib/command_kit.rb
55
61
  - lib/command_kit/arguments.rb
@@ -198,7 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
204
  - !ruby/object:Gem::Version
199
205
  version: '0'
200
206
  requirements: []
201
- rubygems_version: 3.3.7
207
+ rubygems_version: 3.4.10
202
208
  signing_key:
203
209
  specification_version: 4
204
210
  summary: An all-in-one modular Ruby CLI toolkit