command_kit 0.1.0.pre1

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.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +29 -0
  4. data/.gitignore +7 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +29 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +283 -0
  11. data/Rakefile +23 -0
  12. data/command_kit.gemspec +60 -0
  13. data/gemspec.yml +14 -0
  14. data/lib/command_kit.rb +1 -0
  15. data/lib/command_kit/arguments.rb +161 -0
  16. data/lib/command_kit/arguments/argument.rb +111 -0
  17. data/lib/command_kit/arguments/argument_value.rb +81 -0
  18. data/lib/command_kit/arguments/usage.rb +6 -0
  19. data/lib/command_kit/colors.rb +355 -0
  20. data/lib/command_kit/command.rb +42 -0
  21. data/lib/command_kit/command_name.rb +95 -0
  22. data/lib/command_kit/commands.rb +299 -0
  23. data/lib/command_kit/commands/auto_load.rb +153 -0
  24. data/lib/command_kit/commands/auto_load/subcommand.rb +90 -0
  25. data/lib/command_kit/commands/auto_require.rb +138 -0
  26. data/lib/command_kit/commands/command.rb +12 -0
  27. data/lib/command_kit/commands/help.rb +43 -0
  28. data/lib/command_kit/commands/parent_command.rb +21 -0
  29. data/lib/command_kit/commands/subcommand.rb +51 -0
  30. data/lib/command_kit/console.rb +141 -0
  31. data/lib/command_kit/description.rb +89 -0
  32. data/lib/command_kit/env.rb +43 -0
  33. data/lib/command_kit/env/home.rb +71 -0
  34. data/lib/command_kit/env/path.rb +71 -0
  35. data/lib/command_kit/examples.rb +99 -0
  36. data/lib/command_kit/exception_handler.rb +55 -0
  37. data/lib/command_kit/help.rb +62 -0
  38. data/lib/command_kit/help/man.rb +125 -0
  39. data/lib/command_kit/inflector.rb +84 -0
  40. data/lib/command_kit/main.rb +103 -0
  41. data/lib/command_kit/options.rb +179 -0
  42. data/lib/command_kit/options/option.rb +171 -0
  43. data/lib/command_kit/options/option_value.rb +90 -0
  44. data/lib/command_kit/options/parser.rb +227 -0
  45. data/lib/command_kit/options/quiet.rb +53 -0
  46. data/lib/command_kit/options/usage.rb +6 -0
  47. data/lib/command_kit/options/verbose.rb +55 -0
  48. data/lib/command_kit/options/version.rb +62 -0
  49. data/lib/command_kit/os.rb +47 -0
  50. data/lib/command_kit/pager.rb +115 -0
  51. data/lib/command_kit/printing.rb +32 -0
  52. data/lib/command_kit/printing/indent.rb +78 -0
  53. data/lib/command_kit/program_name.rb +57 -0
  54. data/lib/command_kit/stdio.rb +138 -0
  55. data/lib/command_kit/usage.rb +102 -0
  56. data/lib/command_kit/version.rb +4 -0
  57. data/lib/command_kit/xdg.rb +138 -0
  58. data/spec/arguments/argument_spec.rb +169 -0
  59. data/spec/arguments/argument_value_spec.rb +126 -0
  60. data/spec/arguments_spec.rb +213 -0
  61. data/spec/colors_spec.rb +470 -0
  62. data/spec/command_kit_spec.rb +8 -0
  63. data/spec/command_name_spec.rb +130 -0
  64. data/spec/command_spec.rb +49 -0
  65. data/spec/commands/auto_load/subcommand_spec.rb +82 -0
  66. data/spec/commands/auto_load_spec.rb +128 -0
  67. data/spec/commands/auto_require_spec.rb +142 -0
  68. data/spec/commands/fixtures/test_auto_load/cli/commands/test1.rb +10 -0
  69. data/spec/commands/fixtures/test_auto_load/cli/commands/test2.rb +10 -0
  70. data/spec/commands/fixtures/test_auto_require/lib/test_auto_require/cli/commands/test1.rb +10 -0
  71. data/spec/commands/help_spec.rb +66 -0
  72. data/spec/commands/parent_command_spec.rb +40 -0
  73. data/spec/commands/subcommand_spec.rb +99 -0
  74. data/spec/commands_spec.rb +767 -0
  75. data/spec/console_spec.rb +201 -0
  76. data/spec/description_spec.rb +203 -0
  77. data/spec/env/home_spec.rb +46 -0
  78. data/spec/env/path_spec.rb +78 -0
  79. data/spec/env_spec.rb +123 -0
  80. data/spec/examples_spec.rb +235 -0
  81. data/spec/exception_handler_spec.rb +103 -0
  82. data/spec/help_spec.rb +119 -0
  83. data/spec/inflector_spec.rb +104 -0
  84. data/spec/main_spec.rb +179 -0
  85. data/spec/options/option_spec.rb +258 -0
  86. data/spec/options/option_value_spec.rb +67 -0
  87. data/spec/options/parser_spec.rb +265 -0
  88. data/spec/options_spec.rb +137 -0
  89. data/spec/os_spec.rb +46 -0
  90. data/spec/pager_spec.rb +154 -0
  91. data/spec/printing/indent_spec.rb +130 -0
  92. data/spec/printing_spec.rb +76 -0
  93. data/spec/program_name_spec.rb +62 -0
  94. data/spec/spec_helper.rb +6 -0
  95. data/spec/stdio_spec.rb +264 -0
  96. data/spec/usage_spec.rb +237 -0
  97. data/spec/xdg_spec.rb +191 -0
  98. metadata +156 -0
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'command_kit'
3
+
4
+ describe CommandKit do
5
+ it "should have a VERSION constant" do
6
+ expect(subject.const_get('VERSION')).to_not be_empty
7
+ end
8
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/command_name'
3
+
4
+ describe CommandName do
5
+ module TestCommandName
6
+ class ImplicitCmd
7
+ include CommandKit::CommandName
8
+ end
9
+ end
10
+
11
+ let(:command_class) { TestCommandName::ImplicitCmd }
12
+
13
+ describe ".command_name" do
14
+ subject { command_class }
15
+
16
+ context "when no command_name has been set" do
17
+ it "should underscore the class'es name" do
18
+ expect(subject.command_name).to eq('implicit_cmd')
19
+ end
20
+ end
21
+
22
+ context "when a command_name is explicitly set" do
23
+ module TestCommandName
24
+ class ExplicitCmd
25
+ include CommandKit::CommandName
26
+ command_name 'explicit'
27
+ end
28
+ end
29
+
30
+ subject { TestCommandName::ExplicitCmd }
31
+
32
+ it "must return the explicitly set command_name" do
33
+ expect(subject.command_name).to eq('explicit')
34
+ end
35
+ end
36
+
37
+ context "when the command class inherites from another class" do
38
+ module TestCommandName
39
+ class BaseCmd
40
+ include CommandKit::CommandName
41
+ end
42
+
43
+ class InheritedCmd < BaseCmd
44
+ end
45
+ end
46
+
47
+ subject { TestCommandName::InheritedCmd }
48
+
49
+ it "should underscore the class'es name" do
50
+ expect(subject.command_name).to eq('inherited_cmd')
51
+ end
52
+
53
+ context "when the superclass defines an explicit command_name" do
54
+ module TestCommandName
55
+ class ExplicitBaseCmd
56
+ include CommandKit::CommandName
57
+ command_name :explicit
58
+ end
59
+
60
+ class ImplicitInheritedCmd < ExplicitBaseCmd
61
+ end
62
+ end
63
+
64
+ let(:super_subject) { TestCommandName::ExplicitBaseCmd }
65
+ subject { TestCommandName::ImplicitInheritedCmd }
66
+
67
+ it "must return the subclass'es command_name, not the superclass'es" do
68
+ expect(subject.command_name).to eq('implicit_inherited_cmd')
69
+ end
70
+
71
+ it "must not change the superclass'es command_name" do
72
+ expect(super_subject.command_name).to eq('explicit')
73
+ end
74
+ end
75
+
76
+ context "when the subclass defines an explicit command_name" do
77
+ module TestCommandName
78
+ class ImplicitBaseCmd
79
+ include CommandKit::CommandName
80
+ end
81
+
82
+ class ExplicitInheritedCmd < ImplicitBaseCmd
83
+ command_name :explicit
84
+ end
85
+ end
86
+
87
+ let(:super_subject) { TestCommandName::ImplicitBaseCmd }
88
+ subject { TestCommandName::ExplicitInheritedCmd }
89
+
90
+ it "must return the subclass'es command_name, not the superclass'es" do
91
+ expect(subject.command_name).to eq('explicit')
92
+ end
93
+
94
+ it "must not change the superclass'es command_name" do
95
+ expect(super_subject.command_name).to eq('implicit_base_cmd')
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ describe "#initialize" do
102
+ context "when given no keyword arguments" do
103
+ subject { command_class.new }
104
+
105
+ it "must set #command_name to .command_name" do
106
+ expect(subject.command_name).to eq(command_class.command_name)
107
+ end
108
+ end
109
+
110
+ context "when given the command_name: keyword argument" do
111
+ let(:command_name) { 'foo' }
112
+
113
+ subject { command_class.new(command_name: command_name) }
114
+
115
+ it "must set #command_name to the command_name: keyword argument" do
116
+ expect(subject.command_name).to eq(command_name)
117
+ end
118
+ end
119
+ end
120
+
121
+ describe "#command_name" do
122
+ let(:command_name) { 'foo' }
123
+
124
+ subject { command_class.new(command_name: command_name) }
125
+
126
+ it "must return the initialized command_name: value" do
127
+ expect(subject.command_name).to eq(command_name)
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/command'
3
+
4
+ describe Command do
5
+ it "must include CommandKit::Main" do
6
+ expect(described_class).to include(CommandKit::Main)
7
+ end
8
+
9
+ it "must include CommandKit::Env" do
10
+ expect(described_class).to include(CommandKit::Env)
11
+ end
12
+
13
+ it "must include CommandKit::Stdio" do
14
+ expect(described_class).to include(CommandKit::Stdio)
15
+ end
16
+
17
+ it "must include CommandKit::Printing" do
18
+ expect(described_class).to include(CommandKit::Printing)
19
+ end
20
+
21
+ it "must include CommandKit::Help" do
22
+ expect(described_class).to include(CommandKit::Help)
23
+ end
24
+
25
+ it "must include CommandKit::Usage" do
26
+ expect(described_class).to include(CommandKit::Usage)
27
+ end
28
+
29
+ it "must include CommandKit::Arguments" do
30
+ expect(described_class).to include(CommandKit::Arguments)
31
+ end
32
+
33
+ it "must include CommandKit::Options" do
34
+ expect(described_class).to include(CommandKit::Options)
35
+ end
36
+
37
+ it "must include CommandKit::Examples" do
38
+ expect(described_class).to include(CommandKit::Examples)
39
+ end
40
+
41
+ it "must include CommandKit::Description" do
42
+ expect(described_class).to include(CommandKit::Description)
43
+ end
44
+
45
+ it "must include CommandKit::ExceptionHandler" do
46
+ expect(described_class).to include(CommandKit::ExceptionHandler)
47
+ end
48
+
49
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/commands/auto_load/subcommand'
3
+
4
+ describe Commands::AutoLoad::Subcommand do
5
+ let(:fixtures_dir) { File.expand_path('../../fixtures',__FILE__) }
6
+
7
+ let(:file) { 'test1.rb' }
8
+ let(:path) { File.join(fixtures_dir,'test_auto_load/cli/commands',file) }
9
+
10
+ let(:class_name) { 'Test1' }
11
+ let(:constant) { "TestAutoLoad::CLI::Commands::#{class_name}" }
12
+
13
+ subject { described_class.new(constant,path) }
14
+
15
+ describe "#initialize" do
16
+ it "must set #constant" do
17
+ expect(subject.constant).to eq(constant)
18
+ end
19
+
20
+ it "must set #path" do
21
+ expect(subject.path).to eq(path)
22
+ end
23
+
24
+ context "when an explicit summary: option is given" do
25
+ let(:summary) { 'This is a test' }
26
+
27
+ subject { described_class.new(constant,path, summary: summary) }
28
+
29
+ it "must set #summary" do
30
+ expect(subject.summary).to eq(summary)
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "#require!" do
36
+ it "must require #path" do
37
+ expect($LOADED_FEATURES).to_not include(path)
38
+
39
+ subject.require!
40
+
41
+ expect($LOADED_FEATURES).to include(path)
42
+ end
43
+
44
+ context "when #path does not exist" do
45
+ let(:path) { '/does/not/exist' }
46
+
47
+ it do
48
+ expect { subject.require! }.to raise_error(LoadError)
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "#const_get" do
54
+ before { subject.require! }
55
+
56
+ it "must resolve #constant" do
57
+ expect(subject.const_get).to eq(TestAutoLoad::CLI::Commands::Test1)
58
+ end
59
+
60
+ context "when the constant shadows a global constant" do
61
+ let(:constant) { 'TestAutoLoad::CLI::Commands::Object' }
62
+
63
+ it do
64
+ expect { subject.const_get }.to raise_error(NameError)
65
+ end
66
+ end
67
+
68
+ context "when #constant cannot be found" do
69
+ let(:constant) { 'Does::Not::Exist' }
70
+
71
+ it do
72
+ expect { subject.const_get }.to raise_error(NameError)
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "#command" do
78
+ it "return the command class" do
79
+ expect(subject.command).to eq(TestAutoLoad::CLI::Commands::Test1)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/commands/auto_load'
3
+
4
+ describe Commands::AutoLoad do
5
+ let(:fixtures_dir) { File.expand_path('../fixtures',__FILE__) }
6
+
7
+ let(:dir) { File.join(fixtures_dir,'test_auto_load','cli','commands') }
8
+ let(:namespace) { 'TestAutoLoad::CLI::Commands' }
9
+
10
+ subject { described_class.new(dir: dir, namespace: namespace) }
11
+
12
+ describe "#initialize" do
13
+ it "must set #dir" do
14
+ expect(subject.dir).to eq(dir)
15
+ end
16
+
17
+ it "must set #namespace" do
18
+ expect(subject.namespace).to eq(namespace)
19
+ end
20
+
21
+ it "must populate #commands based on the files within #dir" do
22
+ expect(subject.commands['test1']).to be_kind_of(described_class::Subcommand)
23
+ expect(subject.commands['test1'].constant).to eq("#{namespace}::Test1")
24
+ expect(subject.commands['test1'].path).to eq(File.join(dir,'test1.rb'))
25
+
26
+ expect(subject.commands['test2']).to be_kind_of(described_class::Subcommand)
27
+ expect(subject.commands['test2'].constant).to eq("#{namespace}::Test2")
28
+ expect(subject.commands['test2'].path).to eq(File.join(dir,'test2.rb'))
29
+ end
30
+
31
+ context "when given a block" do
32
+ subject do
33
+ described_class.new(dir: dir, namespace: namespace) do |autoload|
34
+ autoload.command 'test-1', 'Test1', 'test1.rb'
35
+ autoload.command 'test-2', 'Test2', 'test2.rb'
36
+ end
37
+ end
38
+
39
+ it "must not populate #commands based on the files within #dir" do
40
+ expect(subject.commands['test1']).to be(nil)
41
+ expect(subject.commands['test2']).to be(nil)
42
+ end
43
+
44
+ it "must allow explicitly initializing #commands via #command" do
45
+ expect(subject.commands['test-1']).to be_kind_of(described_class::Subcommand)
46
+ expect(subject.commands['test-1'].constant).to eq("#{namespace}::Test1")
47
+ expect(subject.commands['test-1'].path).to eq(File.join(dir,'test1.rb'))
48
+
49
+ expect(subject.commands['test-2']).to be_kind_of(described_class::Subcommand)
50
+ expect(subject.commands['test-2'].constant).to eq("#{namespace}::Test2")
51
+ expect(subject.commands['test-2'].path).to eq(File.join(dir,'test2.rb'))
52
+ end
53
+ end
54
+ end
55
+
56
+ module TestAutoLoad
57
+ class CLI
58
+ include CommandKit::Commands
59
+ include CommandKit::Commands::AutoLoad.new(
60
+ dir: File.expand_path('../fixtures/test_auto_load/cli/commands',__FILE__),
61
+ namespace: "#{self}::Commands"
62
+ )
63
+ end
64
+ end
65
+
66
+ let(:command_class) { TestAutoLoad::CLI }
67
+ let(:autoload_module) { command_class.included_modules.first }
68
+
69
+ describe "#included" do
70
+ subject { command_class }
71
+
72
+ it "must also include CommandKit::Commands" do
73
+ expect(subject).to include(CommandKit::Commands)
74
+ end
75
+
76
+ it "must merge the module's #commands into the class'es #commands" do
77
+ expect(command_class.commands).to include(autoload_module.commands)
78
+ end
79
+ end
80
+
81
+ describe "#join" do
82
+ let(:path) { 'foo/bar' }
83
+
84
+ it "must join the path with #dir" do
85
+ expect(subject.join(path)).to eq(File.join(dir,path))
86
+ end
87
+ end
88
+
89
+ describe "#files" do
90
+ it "must list the files within the auto-load directory" do
91
+ expect(subject.files).to eq(Dir["#{dir}/*.rb"])
92
+ end
93
+ end
94
+
95
+ describe "#command" do
96
+ let(:command_name) { 'test' }
97
+ let(:constant) { 'Test' }
98
+ let(:file) { 'test.rb' }
99
+
100
+ before do
101
+ subject.command command_name, constant, file
102
+ end
103
+
104
+ it "must add a Subcommand to #commands with the given command name" do
105
+ expect(subject.commands[command_name]).to be_kind_of(described_class::Subcommand)
106
+ end
107
+
108
+ it "must join the given file: value with #dir" do
109
+ expect(subject.commands[command_name].path).to eq(File.join(dir,file))
110
+ end
111
+
112
+ it "must combine the given constant: value with #namespace" do
113
+ expect(subject.commands[command_name].constant).to eq("#{namespace}::#{constant}")
114
+ end
115
+
116
+ context "when given a summary: value" do
117
+ let(:summary) { 'Test is a test' }
118
+
119
+ before do
120
+ subject.command command_name, constant, file, summary: summary
121
+ end
122
+
123
+ it "must set the subcommand's #summary" do
124
+ expect(subject.commands[command_name].summary).to eq(summary)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+ require 'command_kit/commands/auto_require'
3
+
4
+ describe Commands::AutoRequire do
5
+ let(:fixtures_dir) { File.expand_path('../fixtures',__FILE__) }
6
+ let(:lib_dir) { File.join(fixtures_dir,'test_auto_require','lib') }
7
+ let(:dir) { File.join('test_auto_require','cli','commands') }
8
+ let(:namespace) { 'TestAutoRequire::CLI::Commands' }
9
+
10
+ before { $LOAD_PATH.unshift(lib_dir) }
11
+ after { $LOAD_PATH.delete(lib_dir) }
12
+
13
+ describe "#initialize" do
14
+ subject { described_class.new(dir: dir, namespace: namespace) }
15
+
16
+ it "must set #dir" do
17
+ expect(subject.dir).to eq(dir)
18
+ end
19
+
20
+ it "must set #namespace" do
21
+ expect(subject.namespace).to eq(namespace)
22
+ end
23
+ end
24
+
25
+ module TestAutoRequire
26
+ class CLI
27
+ include CommandKit::Commands
28
+ include CommandKit::Commands::AutoRequire.new(
29
+ dir: 'test_auto_require/cli/commands',
30
+ namespace: "#{self}::Commands"
31
+ )
32
+ end
33
+ end
34
+
35
+ let(:command_class) { TestAutoRequire::CLI }
36
+ let(:auto_require_module) { command_class.included_modules.first }
37
+
38
+ subject { described_class.new(dir: dir, namespace: namespace) }
39
+
40
+ describe "#join" do
41
+ let(:path) { 'foo/bar' }
42
+
43
+ it "must join the path with #dir" do
44
+ expect(subject.join(path)).to eq(File.join(dir,path))
45
+ end
46
+ end
47
+
48
+ let(:file_name) { 'test1' }
49
+
50
+ describe "#require" do
51
+ let(:path) { File.join(lib_dir,dir,"#{file_name}.rb") }
52
+
53
+ it "must require #path" do
54
+ expect($LOADED_FEATURES).to_not include(path)
55
+
56
+ subject.require(file_name)
57
+
58
+ expect($LOADED_FEATURES).to include(path)
59
+ end
60
+
61
+ context "when #path does not exist" do
62
+ let(:file_name) { 'does_not_exist' }
63
+
64
+ it do
65
+ expect { subject.require(file_name) }.to raise_error(LoadError)
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "#const_get" do
71
+ before { subject.require(file_name) }
72
+
73
+ let(:constant) { 'Test1' }
74
+
75
+ it "must resolve the given constant within the namespace" do
76
+ expect(subject.const_get(constant)).to eq(TestAutoRequire::CLI::Commands::Test1)
77
+ end
78
+
79
+ context "when the constant shadows a global constant" do
80
+ let(:constant) { 'Object' }
81
+
82
+ it do
83
+ expect { subject.const_get(constant) }.to raise_error(NameError)
84
+ end
85
+ end
86
+
87
+ context "when #constant cannot be found" do
88
+ let(:constant) { 'DoesNotExist' }
89
+
90
+ it do
91
+ expect { subject.const_get(constant) }.to raise_error(NameError)
92
+ end
93
+ end
94
+ end
95
+
96
+ describe "#command" do
97
+ let(:command_name) { 'test1' }
98
+
99
+ it "must require the command's file and return the command class" do
100
+ expect(subject.command(command_name)).to eq(TestAutoRequire::CLI::Commands::Test1)
101
+ end
102
+
103
+ context "when command's file cannot be found" do
104
+ let(:command_name) { 'does_not_exist' }
105
+
106
+ it do
107
+ expect(subject.command(command_name)).to be(nil)
108
+ end
109
+ end
110
+ end
111
+
112
+ describe "#included" do
113
+ subject { command_class }
114
+
115
+ it "must also include CommandKit::Commands" do
116
+ expect(subject).to include(CommandKit::Commands)
117
+ end
118
+
119
+ describe ".commands" do
120
+ it "must set the class'es .commands.default_proc" do
121
+ expect(command_class.commands.default_proc).to_not be(nil)
122
+ expect(command_class.commands.default_proc).to be_kind_of(Proc)
123
+ end
124
+
125
+ let(:command_name) { 'test1' }
126
+
127
+ it "the .commands.default_proc must auto-require commands and return a Subcommand" do
128
+ expect(command_class.commands[command_name]).to_not be(nil)
129
+ expect(command_class.commands[command_name]).to be_kind_of(Commands::Subcommand)
130
+ expect(command_class.commands[command_name].command).to eq(TestAutoRequire::CLI::Commands::Test1)
131
+ end
132
+
133
+ context "when the given command_name cannot be auto-required" do
134
+ let(:command_name) { 'does_not_exist' }
135
+
136
+ it do
137
+ expect(command_class.commands[command_name]).to be(nil)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end