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
@@ -1,14 +1,23 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'cliqr/cli/argument_operator_context'
3
+ require 'cliqr/command/color'
4
+ require 'cliqr/command/argument_operator_context'
5
+ require 'cliqr/events/invoker'
4
6
 
5
7
  module Cliqr
6
8
  # Definition and builder for command context
7
- module CLI
9
+ module Command
8
10
  # Manages things like arguments and input/output for a command
9
11
  #
10
12
  # @api private
11
13
  class CommandContext
14
+ include Cliqr::Command::Color
15
+
16
+ # Base command name
17
+ #
18
+ # @return [String]
19
+ attr_accessor :base_command
20
+
12
21
  # Command name
13
22
  #
14
23
  # @return [String]
@@ -24,29 +33,37 @@ module Cliqr
24
33
  # @return [String]
25
34
  attr_accessor :action_name
26
35
 
36
+ # Environment type
37
+ #
38
+ # @return [String]
39
+ attr_reader :environment
40
+
27
41
  # Build a instance of command context based on the parsed set of arguments
28
42
  #
29
- # @param [Cliqr::CLI::Config] config The configuration settings for command's action config
43
+ # @param [Cliqr::Config::Command] config The configuration settings
30
44
  # @param [Cliqr::Parser::ParsedInput] parsed_input Parsed input object
31
45
  # @param [Hash] options Options for command execution
32
46
  # @param [Proc] executor Executes forwarded commands
33
47
  #
34
- # @return [Cliqr::CLI::CommandContext]
48
+ # @return [Cliqr::Command::CommandContext]
35
49
  def self.build(config, parsed_input, options, &executor)
36
50
  CommandContextBuilder.new(config, parsed_input, options, executor).build
37
51
  end
38
52
 
39
53
  # Initialize the command context (called by the CommandContextBuilder)
40
54
  #
41
- # @return [Cliqr::CLI::CommandContext]
55
+ # @return [Cliqr::Command::CommandContext]
42
56
  def initialize(config, options, arguments, environment, executor)
57
+ super(config)
58
+
43
59
  @config = config
60
+ @base_command = config.root.name
44
61
  @command = config.command
45
62
  @arguments = arguments
46
63
  @action_name = config.name
47
- @context = self
48
64
  @environment = environment
49
65
  @executor = executor
66
+ @event_invoker = Events::Invoker.new(config, self)
50
67
 
51
68
  # make option map from array
52
69
  @options = Hash[*options.collect { |option| [option.name, option] }.flatten]
@@ -54,22 +71,22 @@ module Cliqr
54
71
 
55
72
  # List of parsed options
56
73
  #
57
- # @return [Array<Cliqr::CLI::CommandOption>]
74
+ # @return [Array<Cliqr::Command::CommandOption>]
58
75
  def options
59
76
  @options.values
60
77
  end
61
78
 
62
- # Get a option by name
79
+ # Get an option by name
63
80
  #
64
81
  # @param [String] name Name of the option
65
82
  #
66
- # @return [Cliqr::CLI::CommandOption] Instance of CommandOption for option
83
+ # @return [Cliqr::Command::CommandOption] Instance of CommandOption for option
67
84
  def option(name)
68
85
  return @options[name] if option?(name)
69
86
  default(name)
70
87
  end
71
88
 
72
- # Check if a option with a specified name has been passed
89
+ # Check if an option with a specified name has been passed
73
90
  #
74
91
  # @param [String] name Name of the option
75
92
  #
@@ -78,10 +95,17 @@ module Cliqr
78
95
  @options.key?(name)
79
96
  end
80
97
 
81
- # Check whether the current context is based off of a sub-action
98
+ # Check whether a action is valid in current context
99
+ #
100
+ # @param [String] name Name of the action to check
82
101
  #
83
- # @return [Boolean] <tt>true</tt> if this context is based off a sub-action
84
- def action?
102
+ # @return [Boolean] <tt>true</tt> if this context has the requested action
103
+ def action?(name)
104
+ @config.action?(name)
105
+ end
106
+
107
+ # Check if the current context if for a action
108
+ def action_type?
85
109
  @config.parent?
86
110
  end
87
111
 
@@ -92,10 +116,24 @@ module Cliqr
92
116
  @executor.call(args, options)
93
117
  end
94
118
 
119
+ # Invoke an event
120
+ #
121
+ # @return [Boolean] <tt>true</tt> if the event was handled by any event handler
122
+ def invoke(event_name, *args)
123
+ @event_invoker.invoke(event_name, nil, *args)
124
+ end
125
+
95
126
  # Handle the case when a method is invoked to get an option value
96
127
  #
97
128
  # @return [Object] Option's value
98
129
  def method_missing(name, *_args, &_block)
130
+ get_or_check_option(name)
131
+ end
132
+
133
+ # Get option value or check if it exists
134
+ #
135
+ # @return [Object]
136
+ def get_or_check_option(name)
99
137
  option_name = name.to_s.chomp('?')
100
138
  existence_check = name.to_s.end_with?('?')
101
139
  existence_check ? option?(option_name) : option(option_name).value \
@@ -114,7 +152,17 @@ module Cliqr
114
152
  #
115
153
  # @return [Boolean]
116
154
  def bash?
117
- @environment == :bash
155
+ @environment == :cli
156
+ end
157
+
158
+ # Transform this context to the root context
159
+ #
160
+ # @param [Symbol] environment_type Optional environment type
161
+ #
162
+ # @return [Cliqr::Command::CommandContext]
163
+ def root(environment_type = nil)
164
+ environment_type = @environment if environment_type.nil?
165
+ CommandContext.new(@config.root, [], [], environment_type, @executor)
118
166
  end
119
167
 
120
168
  private :initialize, :default
@@ -128,12 +176,12 @@ module Cliqr
128
176
  class CommandContextBuilder
129
177
  # Initialize builder for CommandContext
130
178
  #
131
- # @param [Cliqr::CLI::Config] config The configuration settings for command's action config
179
+ # @param [Cliqr::Command::Config] config The configuration settings
132
180
  # @param [Cliqr::Parser::ParsedInput] parsed_input Parsed and validated command line arguments
133
181
  # @param [Hash] options Options for command execution
134
182
  # @param [Proc] executor Executes forwarded commands
135
183
  #
136
- # @return [Cliqr::CLI::CommandContextBuilder]
184
+ # @return [Cliqr::Command::CommandContextBuilder]
137
185
  def initialize(config, parsed_input, options, executor)
138
186
  @config = config
139
187
  @parsed_input = parsed_input
@@ -143,7 +191,7 @@ module Cliqr
143
191
 
144
192
  # Build a new instance of CommandContext
145
193
  #
146
- # @return [Cliqr::CLI::CommandContext] A newly created CommandContext instance
194
+ # @return [Cliqr::Command::CommandContext] A newly created CommandContext instance
147
195
  def build
148
196
  option_contexts = @parsed_input.options.map do |option|
149
197
  CommandOption.new(option, @config.option(option.first))
@@ -174,9 +222,9 @@ module Cliqr
174
222
  # Create a new command line option instance
175
223
  #
176
224
  # @param [Array] option Parsed arguments for creating a command line option
177
- # @param [Cliqr::CLI::OptionConfig] option_config Option's config settings
225
+ # @param [Cliqr::Config::Option] option_config Option's config settings
178
226
  #
179
- # @return [Cliqr::CLI::CommandContext] A new CommandOption object
227
+ # @return [Cliqr::Command::CommandContext] A new CommandOption object
180
228
  def initialize(option, option_config)
181
229
  @value = run_value_operator(option.pop, option_config.operator)
182
230
  @name = option.pop
@@ -189,7 +237,7 @@ module Cliqr
189
237
  # @return [Nothing]
190
238
  def run_value_operator(value, operator)
191
239
  if operator.is_a?(Proc)
192
- ArgumentOperatorContext.new(value).instance_eval(&operator)
240
+ Command::ArgumentOperatorContext.new(value).instance_eval(&operator)
193
241
  else
194
242
  operator.operate(value)
195
243
  end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ module Cliqr
4
+ module Command
5
+ # Used to build a banner string for shell
6
+ #
7
+ # @api private
8
+ class ShellBannerBuilder
9
+ # Build the banner based on current context
10
+ #
11
+ # @return [String]
12
+ def build(context)
13
+ "Starting shell for command \"#{context.command}\""
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,125 @@
1
+ # encoding: utf-8
2
+
3
+ module Cliqr
4
+ # @api private
5
+ module Command
6
+ # The default command executed to run a shell action
7
+ #
8
+ # @api private
9
+ class ShellCommand < Cliqr::Command::BaseCommand
10
+ # Create a new shell command handler
11
+ def initialize(shell_config)
12
+ @shell_config = shell_config
13
+ end
14
+
15
+ # Start a shell in the context of some other command
16
+ #
17
+ # @return [Integer] Exit code
18
+ def execute(context)
19
+ validate_environment(context)
20
+
21
+ root_context = context.root(:shell)
22
+
23
+ puts banner(root_context, build_proc(@shell_config.banner))
24
+
25
+ context.invoke(:shell_start)
26
+ exit_code = build_runner(context, root_context).run
27
+ context.invoke(:shell_stop)
28
+ puts "shell exited with code #{exit_code}"
29
+ exit_code
30
+ end
31
+
32
+ private
33
+
34
+ # Build an instance of the ShellRunner
35
+ #
36
+ # @return [Cliqr::Command::ShellRunner]
37
+ def build_runner(context, root_context)
38
+ ShellRunner.new(context.base_command,
39
+ root_context,
40
+ build_proc(@shell_config.prompt))
41
+ end
42
+
43
+ # Make sure a shell cannot be run inside an already running shell
44
+ #
45
+ # @return Nothing
46
+ def validate_environment(context)
47
+ fail(Cliqr::Error::IllegalCommandError,
48
+ 'Cannot run another shell within an already running shell') unless context.bash?
49
+ end
50
+
51
+ # Banner string for current command
52
+ #
53
+ # @return [String]
54
+ def banner(context, block)
55
+ context.instance_eval(&block)
56
+ end
57
+
58
+ # Build an anonymous method to wrap an attribute value
59
+ #
60
+ # @return [Proc]
61
+ def build_proc(value)
62
+ if value.is_a?(String)
63
+ proc { value }
64
+ elsif value.is_a?(Proc)
65
+ return value
66
+ else
67
+ builder = value
68
+ builder = value.new if value.is_a?(Class)
69
+ proc do
70
+ builder.build(self)
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ # The runner for shell command
79
+ class ShellRunner
80
+ # Create the runner instance
81
+ def initialize(base_command, context, prompt)
82
+ @base_command = base_command
83
+ @context = context
84
+ @prompt = prompt
85
+ end
86
+
87
+ # Start shell
88
+ #
89
+ # @return [Integer] Exit code
90
+ def run
91
+ loop do
92
+ command = prompt
93
+ execute(command) unless command == 'exit'
94
+ break if command == 'exit'
95
+ end
96
+ 0
97
+ end
98
+
99
+ private
100
+
101
+ # Execute a shell command
102
+ #
103
+ # @return [Integer] Exit code of the command executed
104
+ def execute(command)
105
+ return if command.empty?
106
+ action_name = command.split(' ').first
107
+ unless @context.action?(action_name)
108
+ puts "unknown action \"#{action_name}\""
109
+ return Cliqr::Executor::ExitCode.code(nil)
110
+ end
111
+ @context.forward("#{@base_command} #{command}", :environment => @context.environment)
112
+ rescue StandardError => e
113
+ puts e.message
114
+ end
115
+
116
+ # Show a prompt and ask for input
117
+ #
118
+ # @return [String]
119
+ def prompt
120
+ print @context.instance_eval(&@prompt)
121
+ $stdin.gets.chomp
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ module Cliqr
4
+ module Command
5
+ # Builds a shell prompt
6
+ #
7
+ # @api private
8
+ class ShellPromptBuilder
9
+ include Cliqr::Command::Color
10
+
11
+ # Create a new shell prompt builder with optional command config
12
+ def initialize(config = nil)
13
+ super
14
+ @count = 0
15
+ end
16
+
17
+ # Build a prompt for current command
18
+ #
19
+ # @return [String]
20
+ def build(context)
21
+ @count += 1
22
+ "[#{cyan(context.command)}][#{@count}] #{bold('$')} "
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,226 @@
1
+ # encoding: utf-8
2
+
3
+ require 'cliqr/util'
4
+ require 'cliqr/command/base_command'
5
+ require 'cliqr/config/base'
6
+ require 'cliqr/config/option_based'
7
+
8
+ module Cliqr
9
+ module Config
10
+ # Configuration setting for an action
11
+ #
12
+ # @api private
13
+ class Action < Cliqr::Config::OptionBased
14
+ # Command handler for the base command
15
+ #
16
+ # @return [Class<Cliqr::Command::BaseCommand>]
17
+ attr_accessor :handler
18
+ validates :handler,
19
+ one_of: [
20
+ { extend: Cliqr::Command::BaseCommand },
21
+ { type_of: Proc }
22
+ ]
23
+
24
+ # Dictates whether this command can take arbitrary arguments (optional)
25
+ #
26
+ # @return [Symbol] Either <tt>#ENABLE_CONFIG</tt> or <tt>#DISABLE_CONFIG</tt>
27
+ attr_accessor :arguments
28
+ validates :arguments,
29
+ inclusion: [ENABLE_CONFIG, DISABLE_CONFIG]
30
+
31
+ # Array of children action configs for this action
32
+ #
33
+ # @return [Array<Cliqr::CLI::Config>]
34
+ attr_accessor :actions
35
+ validates :actions,
36
+ collection: true
37
+
38
+ # Enable or disable help command and option
39
+ #
40
+ # @return [Symbol] Either <tt>#ENABLE_CONFIG</tt> or <tt>#DISABLE_CONFIG</tt>
41
+ attr_accessor :help
42
+ validates :help,
43
+ inclusion: [ENABLE_CONFIG, DISABLE_CONFIG]
44
+
45
+ # Parent configuration
46
+ #
47
+ # @return [Cliqr::Config::CommandConfig]
48
+ attr_accessor :parent
49
+
50
+ # New config instance with all attributes set as UNSET
51
+ def initialize
52
+ super
53
+
54
+ @handler = UNSET
55
+ @arguments = UNSET
56
+ @help = UNSET
57
+
58
+ @actions = []
59
+ @action_index = {}
60
+ end
61
+
62
+ # Finalize config by adding default values for unset values
63
+ #
64
+ # @return [Cliqr::Config::Action]
65
+ def finalize
66
+ super
67
+
68
+ @handler = Util.ensure_instance(Config.get_if_unset(@handler, nil))
69
+ @arguments = Config.get_if_unset(@arguments, ENABLE_CONFIG)
70
+ @help = Config.get_if_unset(@help, ENABLE_CONFIG)
71
+
72
+ self
73
+ end
74
+
75
+ # Set up default attributes for this configuration
76
+ #
77
+ # @return [Cliqr::Config::Command] Update config
78
+ def setup_defaults
79
+ add_help
80
+ @handler = Cliqr::Util.forward_to_help_handler if @handler.nil? && help? && actions?
81
+ @actions.each(&:setup_defaults)
82
+ end
83
+
84
+ # Set value for a config option
85
+ #
86
+ # @param [Symbol] name Name of the config parameter
87
+ # @param [Object] value Value for the config parameter
88
+ # @param [Proc] block Function which populates configuration for a sub-attribute
89
+ #
90
+ # @return [Object] if setting a attribute's value
91
+ # @return [Cliqr::Config::Base] if adding a new action or option
92
+ def set_config(name, value, *args, &block)
93
+ case name
94
+ when :action
95
+ handle_action(value, &block) # value is action's name
96
+ else
97
+ super
98
+ end
99
+ end
100
+
101
+ # Check if particular action exists
102
+ #
103
+ # @param [String] name Name of the action to check
104
+ #
105
+ # @return [Boolean] <tt>true</tt> if the action exists in the configuration
106
+ def action?(name)
107
+ return false if name.nil?
108
+ @action_index.key?(name.to_sym)
109
+ end
110
+
111
+ # Check if this config has sub actions
112
+ #
113
+ # @return [Boolean]
114
+ def actions?
115
+ !@actions.empty?
116
+ end
117
+
118
+ # Get action config by name
119
+ #
120
+ # @param [String] name Name of the action
121
+ #
122
+ # @return [Cliqr::Config::Action] Configuration of the action
123
+ def action(name)
124
+ @action_index[name.to_sym]
125
+ end
126
+
127
+ # Check if arguments are enabled for this configuration
128
+ #
129
+ # @return [Boolean] <tt>true</tt> if arguments are enabled
130
+ def arguments?
131
+ @arguments == ENABLE_CONFIG
132
+ end
133
+
134
+ # Get name of the command along with the action upto this config
135
+ #
136
+ # @return [String] Serialized command name
137
+ def command
138
+ return name unless parent?
139
+ "#{@parent.command} #{name}"
140
+ end
141
+
142
+ # Check if this config has a parent config
143
+ #
144
+ # @return [Boolean] <tt>true</tt> if there exists a parent action for this action
145
+ def parent?
146
+ !@parent.nil?
147
+ end
148
+
149
+ # Check if help is enabled for this command
150
+ #
151
+ # @return [Boolean] <tt>true</tt> if help is enabled
152
+ def help?
153
+ @help == ENABLE_CONFIG
154
+ end
155
+
156
+ # The root of action config is itself unless otherwise configured
157
+ #
158
+ # @return [Cliqr::Config::Action]
159
+ def root
160
+ self
161
+ end
162
+
163
+ # Check if this action is the root
164
+ def root?
165
+ self == root
166
+ end
167
+
168
+ private
169
+
170
+ # Handle configuration for a new action
171
+ #
172
+ # @param [String] name Name of the action
173
+ # @param [Function] block The block which configures this action
174
+ #
175
+ # @return [Cliqr::Config::Action] The newly configured action
176
+ def handle_action(name, &block)
177
+ action_config = Action.build(&block)
178
+ action_config.name = name
179
+ add_action(action_config)
180
+ end
181
+
182
+ # Add a new action
183
+ #
184
+ # @return [Cliqr::Config::Action] The newly added action
185
+ def add_action(action_config)
186
+ parent = self
187
+ action_config.parent = parent
188
+ action_config.instance_eval do
189
+ def root
190
+ parent.root
191
+ end
192
+ end
193
+
194
+ validate_action_name(action_config)
195
+
196
+ @actions.push(action_config)
197
+ @action_index[action_config.name.to_sym] = action_config \
198
+ unless action_config.name.nil?
199
+
200
+ action_config
201
+ end
202
+
203
+ # Make sure that the action's name is unique
204
+ #
205
+ # @param [Cliqr::Config::Action] action_config Config for this particular action
206
+ #
207
+ # @return [Cliqr::Config::Action] Validated action's Config instance
208
+ def validate_action_name(action_config)
209
+ fail Cliqr::Error::DuplicateActions,
210
+ "multiple actions named \"#{action_config.name}\"" \
211
+ if action?(action_config.name)
212
+
213
+ action_config
214
+ end
215
+
216
+ # Add help command and option to this config
217
+ #
218
+ # @return [Cliqr::Config::Base] Updated config
219
+ def add_help
220
+ return self unless help?
221
+ add_action(Cliqr::Util.build_help_action(self)) unless action?('help')
222
+ add_option(Cliqr::Util.build_help_option(self)) unless option?('help')
223
+ end
224
+ end
225
+ end
226
+ end