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,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