cliqr 1.2.0 → 2.0.0

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +95 -0
  3. data/README.md +9 -71
  4. data/examples/numbers +1 -2
  5. data/examples/vagrant +0 -3
  6. data/lib/cliqr.rb +52 -11
  7. data/lib/cliqr/argument_validation/argument_type_validator.rb +2 -2
  8. data/lib/cliqr/argument_validation/validator.rb +3 -3
  9. data/lib/cliqr/{cli → command}/argument_operator.rb +2 -2
  10. data/lib/cliqr/{cli → command}/argument_operator_context.rb +1 -1
  11. data/lib/cliqr/{cli/command.rb → command/base_command.rb} +2 -2
  12. data/lib/cliqr/command/color.rb +174 -0
  13. data/lib/cliqr/{cli → command}/command_context.rb +68 -20
  14. data/lib/cliqr/command/shell_banner_builder.rb +17 -0
  15. data/lib/cliqr/command/shell_command.rb +125 -0
  16. data/lib/cliqr/command/shell_prompt_builder.rb +26 -0
  17. data/lib/cliqr/config/action.rb +226 -0
  18. data/lib/cliqr/config/base.rb +84 -0
  19. data/lib/cliqr/config/command.rb +137 -0
  20. data/lib/cliqr/config/dsl.rb +81 -0
  21. data/lib/cliqr/config/event.rb +43 -0
  22. data/lib/cliqr/config/event_based.rb +78 -0
  23. data/lib/cliqr/config/named.rb +55 -0
  24. data/lib/cliqr/config/option.rb +95 -0
  25. data/lib/cliqr/config/option_based.rb +130 -0
  26. data/lib/cliqr/config/shell.rb +87 -0
  27. data/lib/cliqr/config/validation/validation_set.rb +66 -0
  28. data/lib/cliqr/config/validation/validator_factory.rb +403 -0
  29. data/lib/cliqr/config/validation/verifiable.rb +91 -0
  30. data/lib/cliqr/error.rb +20 -4
  31. data/lib/cliqr/events/event.rb +56 -0
  32. data/lib/cliqr/events/event_context.rb +31 -0
  33. data/lib/cliqr/events/handler.rb +32 -0
  34. data/lib/cliqr/events/invoker.rb +70 -0
  35. data/lib/cliqr/{cli → executor}/command_runner_factory.rb +3 -3
  36. data/lib/cliqr/{cli → executor}/router.rb +4 -4
  37. data/lib/cliqr/{cli/executor.rb → executor/runner.rb} +25 -10
  38. data/lib/cliqr/interface.rb +98 -0
  39. data/lib/cliqr/parser/token_factory.rb +1 -1
  40. data/lib/cliqr/usage/command_usage_context.rb +94 -0
  41. data/lib/cliqr/usage/option_usage_context.rb +86 -0
  42. data/lib/cliqr/usage/templates/partial/action_list.erb +10 -0
  43. data/lib/cliqr/usage/templates/partial/command_name.erb +3 -0
  44. data/lib/cliqr/usage/templates/partial/option_list.erb +18 -0
  45. data/lib/cliqr/usage/templates/partial/usage_info.erb +5 -0
  46. data/lib/cliqr/usage/templates/usage/cli.erb +4 -0
  47. data/lib/cliqr/usage/templates/usage/shell.erb +2 -0
  48. data/lib/cliqr/usage/usage_builder.rb +59 -0
  49. data/lib/cliqr/util.rb +81 -34
  50. data/lib/cliqr/version.rb +1 -1
  51. data/spec/config/action_config_validator_spec.rb +127 -5
  52. data/spec/config/config_finalize_spec.rb +3 -3
  53. data/spec/config/config_validator_spec.rb +120 -17
  54. data/spec/config/option_config_validator_spec.rb +1 -1
  55. data/spec/dsl/interface_spec.rb +2 -2
  56. data/spec/dsl/usage_spec.rb +461 -465
  57. data/spec/executor/action_executor_spec.rb +1 -1
  58. data/spec/executor/color_executor_spec.rb +125 -0
  59. data/spec/executor/command_runner_spec.rb +6 -8
  60. data/spec/executor/event_executor_spec.rb +365 -0
  61. data/spec/executor/executor_spec.rb +49 -11
  62. data/spec/executor/help_executor_spec.rb +107 -103
  63. data/spec/fixtures/action_reader_command.rb +1 -1
  64. data/spec/fixtures/test_arg_printer_event_handler.rb +9 -0
  65. data/spec/fixtures/test_color_shell_prompt.rb +13 -0
  66. data/spec/fixtures/test_empty_event_handler.rb +5 -0
  67. data/spec/fixtures/test_invoker_event_handler.rb +9 -0
  68. data/spec/fixtures/test_shell_banner.rb +8 -0
  69. data/spec/fixtures/test_shell_prompt.rb +13 -0
  70. data/spec/shell/shell_executor_spec.rb +700 -0
  71. data/spec/validation/validation_spec.rb +2 -2
  72. metadata +65 -27
  73. data/lib/cliqr/cli/config.rb +0 -554
  74. data/lib/cliqr/cli/interface.rb +0 -107
  75. data/lib/cliqr/cli/shell_command.rb +0 -69
  76. data/lib/cliqr/cli/usage_builder.rb +0 -185
  77. data/lib/cliqr/config_validation/validation_set.rb +0 -48
  78. data/lib/cliqr/config_validation/validator_factory.rb +0 -319
  79. data/lib/cliqr/config_validation/verifiable.rb +0 -89
  80. data/lib/cliqr/dsl.rb +0 -59
  81. data/spec/executor/shell_executor_spec.rb +0 -233
  82. data/templates/usage.erb +0 -39
@@ -61,7 +61,7 @@ module Cliqr
61
61
  # @param [String] name Long name of the option
62
62
  # @param [String] arg THe argument that was parsed to get the option name
63
63
  #
64
- # @return [Cliqr::CLI::OptionConfig] Requested option configuration
64
+ # @return [Cliqr::CLI::Option] Requested option configuration
65
65
  def get_option_config(name, arg)
66
66
  fail Cliqr::Error::UnknownCommandOption,
67
67
  "unknown option \"#{arg}\"" unless @config.option?(name)
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+
3
+ require 'cliqr/usage/option_usage_context'
4
+
5
+ module Cliqr
6
+ module Usage
7
+ # The context in which the usage template will be executed
8
+ #
9
+ # @api private
10
+ class CommandUsageContext
11
+ include Cliqr::Command::Color
12
+
13
+ # Name of the current command in context
14
+ #
15
+ # @return [String]
16
+ attr_reader :name
17
+
18
+ # Description of the current command
19
+ #
20
+ # @return [String]
21
+ attr_reader :description
22
+
23
+ # Pre-configured command's actions
24
+ #
25
+ # @return [Array<Cliqr::CLI::CommandUsageContext>]
26
+ attr_reader :actions
27
+
28
+ # List of options configured for current context
29
+ #
30
+ # @return [Array<Cliqr::CLI::OptionUsageContext>]
31
+ attr_reader :options
32
+
33
+ # Command for the current context
34
+ #
35
+ # @return [String]
36
+ attr_reader :command
37
+
38
+ # Wrap a [Cliqr::CLI::Config] instance for usage template
39
+ def initialize(type, config)
40
+ super(config)
41
+
42
+ @type = type
43
+ @config = config
44
+
45
+ @name = config.name
46
+ @description = config.description
47
+ @actions = @config.actions
48
+ .map { |action| CommandUsageContext.new(type, action) }
49
+ .select { |action| type == :shell ? action.name != 'shell' : true }
50
+ @options = @config.options.values.map { |option| Usage::OptionUsageContext.new(option) }
51
+ @command = @config.command
52
+ end
53
+
54
+ # Check if command has a description
55
+ def description?
56
+ non_empty?(@description)
57
+ end
58
+
59
+ # Check if there are any preconfigured options
60
+ def options?
61
+ non_empty?(@config.options)
62
+ end
63
+
64
+ # Check if current command allows arguments
65
+ def arguments?
66
+ @config.arguments == Cliqr::Config::ENABLE_CONFIG
67
+ end
68
+
69
+ # Check if current command has any actions
70
+ def actions?
71
+ non_empty?(@actions)
72
+ end
73
+
74
+ # Check if the help is enabled
75
+ #
76
+ # @return [Boolean]
77
+ def help?
78
+ @config.help?
79
+ end
80
+
81
+ # Check if running inside shell
82
+ def shell?
83
+ @type == :shell
84
+ end
85
+
86
+ private
87
+
88
+ # Check if a obj is non-empty
89
+ def non_empty?(obj)
90
+ !(obj.nil? || obj.empty?)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+
3
+ module Cliqr
4
+ module Usage
5
+ # Wrapper of [Cliqr::CLI::OptionConfig] to be used in usage rendering
6
+ #
7
+ # @api private
8
+ class OptionUsageContext
9
+ # Name of the option
10
+ #
11
+ # @return [String]
12
+ attr_reader :name
13
+
14
+ # Short name of the option
15
+ #
16
+ # @return [String]
17
+ attr_reader :short
18
+
19
+ # Option's type
20
+ #
21
+ # @return [Symbol]
22
+ attr_reader :type
23
+
24
+ # Option's description
25
+ #
26
+ # @return [String]
27
+ attr_reader :description
28
+
29
+ # Default value for this option
30
+ #
31
+ # @return [Object]
32
+ attr_reader :default
33
+
34
+ # Create a new option usage context
35
+ def initialize(option_config)
36
+ @option_config = option_config
37
+
38
+ @name = @option_config.name
39
+ @short = @option_config.short
40
+ @type = @option_config.type
41
+ @description = @option_config.description
42
+ @default = @option_config.default
43
+ end
44
+
45
+ # Check if current option is a boolean option
46
+ def boolean?
47
+ @option_config.boolean? && !help? && !version?
48
+ end
49
+
50
+ # Check if the option has a short name
51
+ def short?
52
+ @option_config.short?
53
+ end
54
+
55
+ # Check if the option has non-empty description
56
+ def description?
57
+ @option_config.description?
58
+ end
59
+
60
+ # Assert if the details of this options should be printed
61
+ def details?
62
+ @option_config.description? || @option_config.type? || @option_config.default?
63
+ end
64
+
65
+ # Check if the option has a non-default type
66
+ def type?
67
+ @option_config.type? && !help? && !version?
68
+ end
69
+
70
+ # check if the option should display default setting
71
+ def default?
72
+ @option_config.default? && !help? && !version?
73
+ end
74
+
75
+ # Check if the option is for getting help
76
+ def help?
77
+ @option_config.name == 'help'
78
+ end
79
+
80
+ # Check if the option is for version
81
+ def version?
82
+ @option_config.name == 'version'
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,10 @@
1
+ <% if actions? %>
2
+ <%= bold('Available actions:') %>
3
+ <%= black("[ Type \"#{"#{command} " unless shell?}help [action-name]\" to get more information about that action ]\n") if help? %>
4
+ <%
5
+ actions.each { |action|
6
+ %> <%= green(action.name) %><%= " -- #{action.description}" if action.description? %>
7
+ <%
8
+ }
9
+ %>
10
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <% parts = command.to_s.split(' ')
2
+ %><%= "#{black(parts[0...-1].join(' '))}#{parts.length > 1 ? ' ' : ''}#{bold(yellow(parts[parts.length - 1]))}"
3
+ %><% unless description.nil? || description.empty? %> -- <%= description %><% end %>
@@ -0,0 +1,18 @@
1
+ <% if options? %>
2
+ <%= bold('Available options:') %>
3
+ <%
4
+ options.each { |option|
5
+ %>
6
+ --<%=
7
+ '[no-]' if option.boolean?
8
+ %><%=
9
+ option.name
10
+ %><%=
11
+ ", -#{option.short}" if option.short?
12
+ %><%=
13
+ if option.details?
14
+ " : #{" <#{option.type}>" if option.type?}#{" #{option.description}" if option.description?}#{" (default => #{option.default.inspect})" if option.default?}"
15
+ end
16
+ %><%
17
+ }
18
+ end %>
@@ -0,0 +1,5 @@
1
+ <%= bold('USAGE:') %>
2
+ <%= command %><%=
3
+ ' [actions]' if actions? %><%=
4
+ ' [options]' if options? %><%=
5
+ ' [arguments]' if arguments? %>
@@ -0,0 +1,4 @@
1
+ <%= render(:command_name) %>
2
+
3
+ <%= render(:usage_info) %>
4
+ <%= render(:option_list) %><%= "\n" if options? %><%= render(:action_list) %>
@@ -0,0 +1,2 @@
1
+ <%= render(:command_name) %>
2
+ <%= render(:action_list) %>
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ require 'erb'
4
+ require 'cliqr/error'
5
+ require 'cliqr/usage/command_usage_context'
6
+
7
+ module Cliqr
8
+ module Usage
9
+ # Builds the usage information based on the configuration settings
10
+ #
11
+ # @api private
12
+ class UsageBuilder
13
+ TEMPLATES_PATH = "#{File.expand_path(File.dirname(__FILE__))}/templates"
14
+
15
+ USAGE_TYPES = {
16
+ :cli => "#{TEMPLATES_PATH}/usage/cli.erb",
17
+ :shell => "#{TEMPLATES_PATH}/usage/shell.erb"
18
+ }
19
+
20
+ # Create a new usage builder
21
+ def initialize(type)
22
+ @type = type
23
+ @template_file = USAGE_TYPES[type]
24
+ end
25
+
26
+ # Build the usage information
27
+ #
28
+ # @param [Cliqr::Config::Command] config Configuration of the command line interface
29
+ #
30
+ # @return [String]
31
+ def build(config)
32
+ usage_context = Usage::CommandUsageContext.new(@type, config)
33
+ usage_context.instance_eval do
34
+ def render(partial_name)
35
+ TemplateRenderer.render("#{TEMPLATES_PATH}/partial/#{partial_name}.erb", self)
36
+ end
37
+ end
38
+
39
+ # Add a extra newline at the end of usage text
40
+ "#{TemplateRenderer.render(@template_file, usage_context)}\n"
41
+ end
42
+ end
43
+
44
+ # Renders a template file based on configuration settings
45
+ #
46
+ # @api private
47
+ class TemplateRenderer
48
+ # Render a partial script
49
+ #
50
+ # @return [Nothing]
51
+ def self.render(template_file_path, context)
52
+ template_file_path = File.expand_path(template_file_path, __FILE__)
53
+ template = ERB.new(File.new(template_file_path).read, nil, '%')
54
+ result = template.result(context.instance_eval { binding })
55
+ Cliqr::Util.trim_newlines(result)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'cliqr/cli/shell_command'
3
+ require 'cliqr/command/shell_command'
4
4
 
5
5
  module Cliqr
6
6
  # Utility methods
@@ -17,59 +17,59 @@ module Cliqr
17
17
 
18
18
  # Build a help action for a parent config
19
19
  #
20
- # @return [Cliqr::CLI::Config] New action config
20
+ # @return [Cliqr::CLI::Action] New action config
21
21
  def self.build_help_action(config)
22
- cli = Cliqr.interface do
23
- name 'help'
24
- description "The help action for command \"#{config.command}\" which provides details " \
25
- 'and usage information on how to use the command.'
26
- handler Util.help_action_handler(config)
27
- help :disable if config.help?
28
- shell :disable
22
+ Cliqr::Config::Action.new.tap do |action_config|
23
+ action_config.name = 'help'
24
+ action_config.description = \
25
+ "The help action for command \"#{config.command}\" which provides details " \
26
+ 'and usage information on how to use the command.'
27
+ action_config.handler = Util.help_action_handler(config)
28
+ action_config.help = :disable if config.help?
29
+ action_config.finalize
29
30
  end
30
- cli.config
31
31
  end
32
32
 
33
33
  # Build a help option for a parent config
34
34
  #
35
- # @return [Cliqr::CLI::OptionConfig] New option config
35
+ # @return [Cliqr::CLI::Option] New option config
36
36
  def self.build_help_option(config)
37
- Cliqr::CLI::OptionConfig.new.tap do |option_config|
37
+ Cliqr::Config::Option.new.tap do |option_config|
38
38
  option_config.name = 'help'
39
39
  option_config.short = 'h'
40
40
  option_config.description = "Get helpful information for action \"#{config.command}\" " \
41
41
  'along with its usage information.'
42
- option_config.type = Cliqr::CLI::BOOLEAN_ARGUMENT_TYPE
43
- option_config.operator = Cliqr::CLI::ArgumentOperator::DEFAULT_ARGUMENT_OPERATOR
42
+ option_config.type = Cliqr::Config::BOOLEAN_ARGUMENT_TYPE
43
+ option_config.operator = Cliqr::Command::ArgumentOperator::DEFAULT_ARGUMENT_OPERATOR
44
44
  option_config.finalize
45
45
  end
46
46
  end
47
47
 
48
48
  # Build a version action for a parent config
49
49
  #
50
- # @return [Cliqr::CLI::Config] New action config
50
+ # @return [Cliqr::CLI::Action] New action config
51
51
  def self.build_version_action(config)
52
- cli = Cliqr.interface do
53
- name 'version'
54
- description "Get version information for command \"#{config.command}\"."
55
- handler do
52
+ Cliqr::Config::Action.new.tap do |action_config|
53
+ action_config.name = 'version'
54
+ action_config.description = "Get version information for command \"#{config.command}\"."
55
+ action_config.arguments = Cliqr::Config::DISABLE_CONFIG
56
+ action_config.handler = proc do
56
57
  puts config.version
57
58
  end
58
- shell :disable
59
+ action_config.finalize
59
60
  end
60
- cli.config
61
61
  end
62
62
 
63
63
  # Build a version option for a parent config
64
64
  #
65
- # @return [Cliqr::CLI::OptionConfig] New option config
65
+ # @return [Cliqr::CLI::Option] New option config
66
66
  def self.build_version_option(config)
67
- Cliqr::CLI::OptionConfig.new.tap do |option_config|
67
+ Cliqr::Config::Option.new.tap do |option_config|
68
68
  option_config.name = 'version'
69
69
  option_config.short = 'v'
70
70
  option_config.description = "Get version information for command \"#{config.command}\"."
71
- option_config.type = Cliqr::CLI::BOOLEAN_ARGUMENT_TYPE
72
- option_config.operator = Cliqr::CLI::ArgumentOperator::DEFAULT_ARGUMENT_OPERATOR
71
+ option_config.type = Cliqr::Config::BOOLEAN_ARGUMENT_TYPE
72
+ option_config.operator = Cliqr::Command::ArgumentOperator::DEFAULT_ARGUMENT_OPERATOR
73
73
  option_config.finalize
74
74
  end
75
75
  end
@@ -82,21 +82,53 @@ module Cliqr
82
82
  fail Cliqr::Error::IllegalArgumentError,
83
83
  "too many arguments for \"#{command}\" command" if arguments.length > 1
84
84
  action_config = arguments.length == 0 ? config : config.action(arguments.first)
85
- puts Cliqr::CLI::UsageBuilder.build(action_config)
85
+ puts Cliqr::Usage::UsageBuilder.new(environment).build(action_config)
86
86
  end
87
87
  end
88
88
 
89
89
  # Build a shell action for a parent config
90
90
  #
91
- # @return [Cliqr::CLI::Config] New action config
92
- def self.build_shell_action(config)
93
- cli = Cliqr.interface do
94
- name 'shell'
95
- description "Execute a shell in the context of \"#{config.command}\" command."
96
- handler Cliqr::CLI::ShellCommand
97
- shell :disable
91
+ # @return [Cliqr::CLI::Action] New action config
92
+ def self.build_shell_action(config, shell_config)
93
+ Cliqr::Config::Action.new.tap do |action_config|
94
+ if shell_config.name?
95
+ action_config.name = shell_config.name
96
+ else
97
+ action_config.name = 'shell'
98
+ end
99
+
100
+ action_config.handler = Cliqr::Command::ShellCommand.new(shell_config)
101
+ action_config.arguments = Cliqr::Config::DISABLE_CONFIG
102
+
103
+ # allow description to be overridden
104
+ description = shell_config.description
105
+ description = "Execute a shell in the context of \"#{config.command}\" command." \
106
+ unless shell_config.description?
107
+ action_config.description = description
108
+
109
+ action_config.options = shell_config.options
110
+ action_config.events = shell_config.events
111
+
112
+ action_config.instance_eval do
113
+ def skip_validation?
114
+ true
115
+ end
116
+ end
117
+
118
+ action_config.finalize
119
+ end
120
+ end
121
+
122
+ # Build shell config for a parent config
123
+ #
124
+ # @return [Cliqr::CLI::Shell] New action config
125
+ def self.build_shell_config(config)
126
+ Cliqr::Config::Shell.new.tap do |shell_config|
127
+ shell_config.enabled = config.actions?
128
+ shell_config.prompt = Command::ShellPromptBuilder.new(config)
129
+ shell_config.banner = Command::ShellBannerBuilder.new
130
+ shell_config.finalize
98
131
  end
99
- cli.config
100
132
  end
101
133
 
102
134
  # Sanitize raw command line arguments
@@ -128,8 +160,23 @@ module Cliqr
128
160
  # @return [Proc]
129
161
  def self.forward_to_help_handler
130
162
  proc do
163
+ fail Cliqr::Error::IllegalArgumentError,
164
+ 'no arguments allowed for default help action' unless arguments.empty?
131
165
  forward "#{command} help"
132
166
  end
133
167
  end
168
+
169
+ # Remove newlines from the end of a string
170
+ #
171
+ # @return [String]
172
+ def self.trim_newlines(str)
173
+ index = str.length - 1
174
+ count = 0
175
+ while str[index] == "\n" && index >= 0
176
+ count += 1
177
+ index -= 1
178
+ end
179
+ str[0...-count]
180
+ end
134
181
  end
135
182
  end