command_kit 0.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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