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,89 @@
1
+ require 'command_kit/help'
2
+
3
+ module CommandKit
4
+ #
5
+ # Allows adding a description to a command's class.
6
+ #
7
+ # ## Examples
8
+ #
9
+ # include CommandKit::Description
10
+ #
11
+ # description "Does things and stuff"
12
+ #
13
+ module Description
14
+ include Help
15
+
16
+ module ModuleMethods
17
+ #
18
+ # Extends {ClassMethods} or {ModuleMethods}, depending on whether
19
+ # {Description} is being included into a class or a module.
20
+ #
21
+ # @param [Class, Module] context
22
+ # The class or module which is including {Description}.
23
+ #
24
+ def included(context)
25
+ super(context)
26
+
27
+ if context.class == Module
28
+ context.extend ModuleMethods
29
+ else
30
+ context.extend ClassMethods
31
+ end
32
+ end
33
+ end
34
+
35
+ extend ModuleMethods
36
+
37
+ #
38
+ # Defines class-level methods.
39
+ #
40
+ module ClassMethods
41
+ #
42
+ # Gets or sets the description string.
43
+ #
44
+ # @param [String, nil] new_description
45
+ # If a String is given, the class'es description will be set.
46
+ #
47
+ # @return [String, nil]
48
+ # The class'es or superclass'es description.
49
+ #
50
+ # @example
51
+ # description "Does things and stuff"
52
+ #
53
+ def description(new_description=nil)
54
+ if new_description
55
+ @description = new_description
56
+ else
57
+ @description || (superclass.description if superclass.kind_of?(ClassMethods))
58
+ end
59
+ end
60
+ end
61
+
62
+ #
63
+ # @see ClassMethods#description
64
+ #
65
+ def description
66
+ self.class.description
67
+ end
68
+
69
+ #
70
+ # Prints the {ClassMethods#description description}, if set.
71
+ #
72
+ def help_description
73
+ if (description = self.description)
74
+ puts
75
+ puts description
76
+ end
77
+ end
78
+
79
+ #
80
+ # Calls the superclass'es `#help` method, if it's defined, then calls
81
+ # {#help_description}.
82
+ #
83
+ def help
84
+ super if defined?(super)
85
+
86
+ help_description
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,43 @@
1
+ module CommandKit
2
+ #
3
+ # Provides access to environment variables.
4
+ #
5
+ # ## Examples
6
+ #
7
+ # class MyCmd
8
+ # include CommandKit::Env
9
+ #
10
+ # def main
11
+ # home = env['HOME']
12
+ # # ...
13
+ # end
14
+ # end
15
+ #
16
+ # ## Testing
17
+ #
18
+ # Can be initialized with a custom `env` hash for testing purposes.
19
+ #
20
+ # MyCmd.new(env: {...})
21
+ #
22
+ module Env
23
+ # The environment variables hash.
24
+ #
25
+ # @return [Hash{String => String}]
26
+ attr_reader :env
27
+
28
+ #
29
+ # Initializes {#env}.
30
+ #
31
+ # @param [Hash{String => String}] env
32
+ # The given environment for the command. Defaults to the global `ENV`.
33
+ #
34
+ # @param [Hash{Symbol => Object}] kwargs
35
+ # Additional keyword arguments.
36
+ #
37
+ def initialize(env: ENV, **kwargs)
38
+ @env = env
39
+
40
+ super(**kwargs)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'command_kit/env'
4
+
5
+ module CommandKit
6
+ module Env
7
+ #
8
+ # Provides access to the `HOME` environment variable.
9
+ #
10
+ # ## Environment Variables
11
+ #
12
+ # * `HOME` - The absolute path to the user's home directory.
13
+ #
14
+ module Home
15
+ include Env
16
+
17
+ module ModuleMethods
18
+ #
19
+ # Extends {ClassMethods} or {ModuleMethods}, depending on whether
20
+ # {Env::Home} is being included into a class or a module..
21
+ #
22
+ # @param [Class, Module] context
23
+ # The class or module which is including {Home}.
24
+ #
25
+ def included(context)
26
+ super(context)
27
+
28
+ if context.class == Module
29
+ context.extend ModuleMethods
30
+ else
31
+ context.extend ClassMethods
32
+ end
33
+ end
34
+ end
35
+
36
+ extend ModuleMethods
37
+
38
+ #
39
+ # Class-level methods.
40
+ #
41
+ module ClassMethods
42
+ #
43
+ # The default home directory.
44
+ #
45
+ # @return [String]
46
+ #
47
+ def home_dir
48
+ Gem.user_home
49
+ end
50
+ end
51
+
52
+ # The home directory.
53
+ #
54
+ # @return [String]
55
+ attr_reader :home_dir
56
+
57
+ #
58
+ # Initializes {#home_dir} to either `env['HOME']` or
59
+ # {ClassMethods#home_dir self.class.home_dir}.
60
+ #
61
+ # @param [Hash{Symbol => Object}] kwargs
62
+ # Additional keyword arguments.
63
+ #
64
+ def initialize(**kwargs)
65
+ super(**kwargs)
66
+
67
+ @home_dir = env.fetch('HOME') { self.class.home_dir }
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'command_kit/env'
4
+
5
+ module CommandKit
6
+ module Env
7
+ #
8
+ # Provides access to the `PATH` environment variable.
9
+ #
10
+ # ## Environment Variables
11
+ #
12
+ # * `PATH` - The list of directories to search for commands within.
13
+ #
14
+ module Path
15
+ include Env
16
+
17
+ # The home directory.
18
+ #
19
+ # @return [String]
20
+ attr_reader :path_dirs
21
+
22
+ #
23
+ # Initializes {#path_dirs} using `env['PATH']`.
24
+ #
25
+ # @param [Hash{Symbol => Object}] kwargs
26
+ # Additional keyword arguments.
27
+ #
28
+ def initialize(**kwargs)
29
+ super(**kwargs)
30
+
31
+ @path_dirs = env.fetch('PATH','').split(File::PATH_SEPARATOR)
32
+ end
33
+
34
+ #
35
+ # Searches for the command in {#path_dirs}.
36
+ #
37
+ # @param [String, Symbol] name
38
+ # The command name.
39
+ #
40
+ # @return [String, nil]
41
+ # The absolute path to the executable file, or `nil` if the command
42
+ # could not be found in any of the {#path_dirs}.
43
+ #
44
+ def find_command(name)
45
+ name = name.to_s
46
+
47
+ @path_dirs.each do |dir|
48
+ file = File.join(dir,name)
49
+
50
+ return file if File.file?(file) && File.executable?(file)
51
+ end
52
+
53
+ return nil
54
+ end
55
+
56
+ #
57
+ # Determines if the command is present on the system.
58
+ #
59
+ # @param [String, Symbol] name
60
+ # The command name.
61
+ #
62
+ # @return [Boolean]
63
+ # Specifies whether a command with the given name exists in one of the
64
+ # {#path_dirs}.
65
+ #
66
+ def command_installed?(name)
67
+ !find_command(name).nil?
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,99 @@
1
+ require 'command_kit/help'
2
+ require 'command_kit/command_name'
3
+
4
+ module CommandKit
5
+ #
6
+ # Allows defining example commands for the command class.
7
+ #
8
+ # ## Examples
9
+ #
10
+ # include CommandKit::Examples
11
+ #
12
+ # examples [
13
+ # "my_cmd -o output.txt path/to/file"
14
+ # ]
15
+ #
16
+ module Examples
17
+ include Help
18
+ include CommandName
19
+
20
+ module ModuleMethods
21
+ #
22
+ # Extends {ClassMethods} or {ModuleMethods}, depending on whether
23
+ # {Examples} is being included into a class or a module.
24
+ #
25
+ # @param [Class, Module] context
26
+ # The class or module which is including {Examples}.
27
+ #
28
+ def included(context)
29
+ super(context)
30
+
31
+ if context.class == Module
32
+ context.extend ModuleMethods
33
+ else
34
+ context.extend ClassMethods
35
+ end
36
+ end
37
+ end
38
+
39
+ extend ModuleMethods
40
+
41
+ #
42
+ # Defines class-level methods.
43
+ #
44
+ module ClassMethods
45
+ #
46
+ # Gets or sets the example commands.
47
+ #
48
+ # @param [Array<String>, String, nil] new_examples
49
+ # If a String or Array of Strings is given, it will set the class'es
50
+ # example commands.
51
+ #
52
+ # @return [Array<String>, nil]
53
+ # The class'es or superclass'es example commands.
54
+ #
55
+ # @example
56
+ # examples [
57
+ # "-o output.txt path/to/file"
58
+ # ]
59
+ #
60
+ def examples(new_examples=nil)
61
+ if new_examples
62
+ @examples = Array(new_examples)
63
+ else
64
+ @examples || (superclass.examples if superclass.kind_of?(ClassMethods))
65
+ end
66
+ end
67
+ end
68
+
69
+ #
70
+ # @see ClassMethods#examples
71
+ #
72
+ def examples
73
+ self.class.examples
74
+ end
75
+
76
+ #
77
+ # Prints the command class'es example commands.
78
+ #
79
+ def help_examples
80
+ if (examples = self.examples)
81
+ puts
82
+ puts "Examples:"
83
+ examples.each do |command|
84
+ puts " #{command_name} #{command}"
85
+ end
86
+ end
87
+ end
88
+
89
+ #
90
+ # Calls the superclass'es `#help` method, if it's defined, then calls
91
+ # {#help_examples}.
92
+ #
93
+ def help
94
+ super if defined?(super)
95
+
96
+ help_examples
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,55 @@
1
+ require 'command_kit/main'
2
+ require 'command_kit/printing'
3
+
4
+ module CommandKit
5
+ #
6
+ # Adds exception handling and printing.
7
+ #
8
+ # ## Examples
9
+ #
10
+ # include CommandKit::Main
11
+ # include CommandKit::ExceptionHandler
12
+ #
13
+ # ### Custom Exception Handling
14
+ #
15
+ # include CommandKit::Main
16
+ # include CommandKit::ExceptionHandler
17
+ #
18
+ # def on_exception(error)
19
+ # print_error "error: #{error.message}"
20
+ # exit(1)
21
+ # end
22
+ #
23
+ module ExceptionHandler
24
+ include Printing
25
+
26
+ #
27
+ # Calls superclass'es `#main` method, but rescues any uncaught exceptions
28
+ # and passes them to {#on_exception}.
29
+ #
30
+ # @param [Array<String>] argv
31
+ # The given arguments Array.
32
+ #
33
+ # @return [Integer]
34
+ # The exit status of the command.
35
+ #
36
+ def main(argv=[])
37
+ super(argv)
38
+ rescue Interrupt, Errno::EPIPE => error
39
+ raise(error)
40
+ rescue Exception => error
41
+ on_exception(error)
42
+ end
43
+
44
+ #
45
+ # Default method for handling when an exception is raised by `#main`.
46
+ #
47
+ # @param [Exception] error
48
+ # The raised exception.
49
+ #
50
+ def on_exception(error)
51
+ print_exception(error)
52
+ exit(1)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,62 @@
1
+ module CommandKit
2
+ #
3
+ # Defines a place-holder {Help#help help} method.
4
+ #
5
+ # ## Examples
6
+ #
7
+ # class MyCmd
8
+ # include CommandKit::Help
9
+ #
10
+ # def help
11
+ # puts "..."
12
+ # end
13
+ # end
14
+ #
15
+ # MyCmd.help
16
+ #
17
+ module Help
18
+ module ModuleMethods
19
+ #
20
+ # Extends {ClassMethods} or {ModuleMethods}, depending on whether {Help}
21
+ # is being included into a class or a module.
22
+ #
23
+ # @param [Class, Module] context
24
+ # The class or module which is extending {ClassMethods}.
25
+ #
26
+ def included(context)
27
+ super(context)
28
+
29
+ if context.class == Module
30
+ context.extend ModuleMethods
31
+ else
32
+ context.extend ClassMethods
33
+ end
34
+ end
35
+ end
36
+
37
+ extend ModuleMethods
38
+
39
+ module ClassMethods
40
+ #
41
+ # Prints `--help` information.
42
+ #
43
+ # @param [Hash{Symbol => Object}] kwargs
44
+ # Additional keyword arguments for `#initialize`.
45
+ #
46
+ # @see Help#help
47
+ #
48
+ def help(**kwargs)
49
+ new(**kwargs).help
50
+ end
51
+ end
52
+
53
+ #
54
+ # Prints `--help` information.
55
+ #
56
+ # @abstract
57
+ #
58
+ def help
59
+ super if defined?(super)
60
+ end
61
+ end
62
+ end