bovem 2.4.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/Gemfile +1 -1
  4. data/README.md +98 -2
  5. data/bovem.gemspec +3 -3
  6. data/doc/Bovem.html +25 -6
  7. data/doc/Bovem/Application.html +3057 -0
  8. data/doc/Bovem/Command.html +7031 -0
  9. data/doc/Bovem/CommandMethods.html +125 -0
  10. data/doc/Bovem/CommandMethods/Children.html +1285 -0
  11. data/doc/Bovem/CommandMethods/Help.html +209 -0
  12. data/doc/Bovem/Configuration.html +3 -3
  13. data/doc/Bovem/Console.html +8 -8
  14. data/doc/Bovem/ConsoleMethods.html +3 -3
  15. data/doc/Bovem/ConsoleMethods/Interactions.html +3 -3
  16. data/doc/Bovem/ConsoleMethods/Interactions/ClassMethods.html +3 -3
  17. data/doc/Bovem/ConsoleMethods/Logging.html +4 -4
  18. data/doc/Bovem/ConsoleMethods/Logging/ClassMethods.html +3 -3
  19. data/doc/Bovem/ConsoleMethods/Output.html +3 -3
  20. data/doc/Bovem/ConsoleMethods/StyleHandling.html +4 -4
  21. data/doc/Bovem/ConsoleMethods/StyleHandling/ClassMethods.html +8 -8
  22. data/doc/Bovem/Errors.html +4 -4
  23. data/doc/Bovem/Errors/Error.html +631 -0
  24. data/doc/Bovem/Errors/InvalidConfiguration.html +3 -3
  25. data/doc/Bovem/Errors/InvalidLogger.html +3 -3
  26. data/doc/Bovem/Localizer.html +376 -0
  27. data/doc/Bovem/Logger.html +64 -160
  28. data/doc/Bovem/Option.html +7009 -0
  29. data/doc/Bovem/Parser.html +276 -0
  30. data/doc/Bovem/ParserMethods.html +125 -0
  31. data/doc/Bovem/ParserMethods/General.html +134 -0
  32. data/doc/Bovem/ParserMethods/General/ClassMethods.html +574 -0
  33. data/doc/Bovem/Shell.html +8 -8
  34. data/doc/Bovem/ShellMethods.html +3 -3
  35. data/doc/Bovem/ShellMethods/Directories.html +3 -3
  36. data/doc/Bovem/ShellMethods/Execute.html +3 -3
  37. data/doc/Bovem/ShellMethods/General.html +3 -3
  38. data/doc/Bovem/ShellMethods/Read.html +3 -3
  39. data/doc/Bovem/ShellMethods/Write.html +3 -3
  40. data/doc/Bovem/Version.html +6 -6
  41. data/doc/_index.html +119 -11
  42. data/doc/class_list.html +1 -1
  43. data/doc/file.README.html +98 -5
  44. data/doc/frames.html +1 -1
  45. data/doc/index.html +98 -5
  46. data/doc/method_list.html +476 -26
  47. data/doc/top-level-namespace.html +3 -3
  48. data/lib/bovem.rb +8 -1
  49. data/lib/bovem/application.rb +158 -0
  50. data/lib/bovem/command.rb +529 -0
  51. data/lib/bovem/console.rb +8 -8
  52. data/lib/bovem/errors.rb +27 -0
  53. data/lib/bovem/localizer.rb +27 -0
  54. data/lib/bovem/logger.rb +2 -8
  55. data/lib/bovem/option.rb +250 -0
  56. data/lib/bovem/parser.rb +317 -0
  57. data/lib/bovem/shell.rb +2 -2
  58. data/lib/bovem/version.rb +3 -3
  59. data/locales/en.yml +33 -0
  60. data/locales/it.yml +33 -0
  61. data/spec/bovem/application_spec.rb +170 -0
  62. data/spec/bovem/command_spec.rb +526 -0
  63. data/spec/bovem/configuration_spec.rb +4 -4
  64. data/spec/bovem/console_spec.rb +22 -22
  65. data/spec/bovem/errors_spec.rb +18 -0
  66. data/spec/bovem/logger_spec.rb +22 -12
  67. data/spec/bovem/option_spec.rb +307 -0
  68. data/spec/bovem/parser_spec.rb +126 -0
  69. data/spec/bovem/shell_spec.rb +4 -4
  70. metadata +32 -5
@@ -6,7 +6,7 @@
6
6
  <title>
7
7
  Top Level Namespace
8
8
 
9
- &mdash; Documentation by YARD 0.8.6.2
9
+ &mdash; Documentation by YARD 0.8.7
10
10
 
11
11
  </title>
12
12
 
@@ -103,9 +103,9 @@
103
103
  </div>
104
104
 
105
105
  <div id="footer">
106
- Generated on Sat Aug 3 12:38:42 2013 by
106
+ Generated on Sat Aug 3 13:50:14 2013 by
107
107
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
- 0.8.6.2 (ruby-2.0.0).
108
+ 0.8.7 (ruby-2.0.0).
109
109
  </div>
110
110
 
111
111
  </body>
data/lib/bovem.rb CHANGED
@@ -4,6 +4,8 @@
4
4
  # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
5
  #
6
6
 
7
+ require "optparse"
8
+ require "prettyprint"
7
9
  require "logger"
8
10
  require "open4"
9
11
  require "find"
@@ -14,7 +16,12 @@ Lazier.load!(:object, :boolean, :math)
14
16
 
15
17
  require "bovem/version" if !defined?(Bovem::Version)
16
18
  require "bovem/errors"
19
+ require "bovem/localizer"
17
20
  require "bovem/configuration"
18
21
  require "bovem/logger"
19
22
  require "bovem/console"
20
- require "bovem/shell"
23
+ require "bovem/shell"
24
+ require "bovem/command"
25
+ require "bovem/option"
26
+ require "bovem/application"
27
+ require "bovem/parser"
@@ -0,0 +1,158 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the bovem gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Bovem
8
+ # This is the main class for a Bovem application.
9
+ #
10
+ # Basically is the same of a command, but it adds support for application version.
11
+ #
12
+ # @attribute version
13
+ # @return [String] The version of the application.
14
+ # @attribute shell
15
+ # @return [Bovem::Shell] A shell helper.
16
+ # @attribute console
17
+ # @return [Bovem::Console] A console helper.
18
+ # @attribute skip_commands
19
+ # @return [Boolean] If to skip commands run via {#run}.
20
+ # @attribute show_commands
21
+ # @return [Boolean] If to show command lines run via {#run}.
22
+ # @attribute output_commands
23
+ # @return [Boolean] If to show the output of the commands run via {#run}.
24
+ class Application < Bovem::Command
25
+ attr_accessor :version
26
+ attr_accessor :shell
27
+ attr_accessor :console
28
+ attr_accessor :skip_commands
29
+ attr_accessor :show_commands
30
+ attr_accessor :output_commands
31
+
32
+ # Initializes a new Bovem application.
33
+ #
34
+ # In options, you can override the command line arguments with `:__args__`, and you can skip execution by specifying `run: false`.
35
+ #
36
+ # @see Command#setup_with
37
+ #
38
+ # @param options [Hash] The settings to initialize the application with.
39
+ # @return [Application] The created application.
40
+ def self.create(options = {}, &block)
41
+ raise Bovem::Errors::Error.new(Bovem::Application, :missing_block, Bovem::Localizer.localize_on_locale(options[:locale], :missing_app_block)) if !block_given?
42
+ run, args, options = setup_application_option(options)
43
+
44
+ begin
45
+ create_application(run, args, options, &block)
46
+ rescue => e
47
+ Kernel.puts(e.to_s)
48
+ Kernel.exit(1)
49
+ end
50
+ end
51
+
52
+ # Creates a new application.
53
+ #
54
+ # @param options [Hash] The settings to initialize the application with.
55
+ def initialize(options = {}, &block)
56
+ super(options, &block)
57
+
58
+ @shell = Bovem::Shell.instance
59
+ @console = @shell.console
60
+ @skip_commands = false
61
+ @show_commands = false
62
+ @output_commands = false
63
+
64
+ help_option
65
+ end
66
+
67
+ # Reads and optionally sets the version of this application.
68
+ #
69
+ # @param value [String|nil] The new version of this application.
70
+ # @return [String|nil] The version of this application.
71
+ def version(value = nil)
72
+ @version = value.ensure_string if !value.nil?
73
+ @version
74
+ end
75
+
76
+ # Executes this application.
77
+ #
78
+ # @param args [Array] The command line to pass to this application. Defaults to `ARGV`.
79
+ def execute(args = nil)
80
+ super(args || ARGV)
81
+ end
82
+
83
+ # Adds a help command and a help option to this application.
84
+ def help_option
85
+ command(:help, description: i18n.help_command_description) do
86
+ action { |command| application.command_help(command) }
87
+ end
88
+
89
+ option(:help, [i18n.help_option_short_form, i18n.help_option_long_form], help: i18n.help_message){ |application, _| application.show_help }
90
+ end
91
+
92
+ # The name of the current executable.
93
+ #
94
+ # @return [String] The name of the current executable.
95
+ def executable_name
96
+ $0
97
+ end
98
+
99
+ # Shows a help about a command.
100
+ #
101
+ # @param command [Command] The command to show help for.
102
+ def command_help(command)
103
+ fetch_commands_for_help(command).each do |arg|
104
+ # Find the command across
105
+ next_command = Bovem::Parser.find_command(arg, command, [])
106
+
107
+ if next_command then
108
+ command = command.commands[next_command[:name]]
109
+ else
110
+ break
111
+ end
112
+ end
113
+
114
+ command.show_help
115
+ end
116
+
117
+ # Runs a command into the shell.
118
+ #
119
+ # @param command [String] The string to run.
120
+ # @param message [String] A message to show before running.
121
+ # @param show_exit [Boolean] If show the exit status.
122
+ # @param fatal [Boolean] If quit in case of fatal errors.
123
+ # @return [Hash] An hash with `status` and `output` keys.
124
+ def run(command, message = nil, show_exit = true, fatal = true)
125
+ @shell.run(command, message, !@skip_commands, show_exit, @output_commands, @show_commands, fatal)
126
+ end
127
+
128
+ private
129
+ # Setup options for application creation.
130
+ #
131
+ # @param options [Hash] The options to setups.
132
+ # @return [Array] If to run the application, the arguments and the specified options.
133
+ def self.setup_application_option(options)
134
+ options = {name: Bovem::Localizer.localize_on_locale(options[:locale], :default_application_name), parent: nil, application: nil}.merge(options.ensure_hash)
135
+ run = options.delete(:run)
136
+ [(!run.nil? ? run : true).to_boolean, options.delete(:__args__), options]
137
+ end
138
+
139
+ # Create the application.
140
+ #
141
+ # @param run [Boolean ]If to run the application.
142
+ # @param args [Hash] The arguments to use for running.
143
+ # @param options [Hash] The options of the application.
144
+ # @return [Application] The new application.
145
+ def self.create_application(run, args, options, &block)
146
+ application = new(options, &block)
147
+ application.execute(args) if application && run
148
+ application
149
+ end
150
+
151
+ # Fetch a command list for showing help.
152
+ #
153
+ # @param command [Command] The command to show help for.
154
+ def fetch_commands_for_help(command)
155
+ command.arguments.collect {|c| c.split(":") }.flatten.collect(&:strip).select(&:present?)
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,529 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the bovem gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ module Bovem
8
+ # Methods for the {Command Command} class.
9
+ module CommandMethods
10
+ # Methods for showing help messages.
11
+ module Help
12
+ # Shows a help about this command.
13
+ def show_help
14
+ console = is_application? ? self.console : application.console
15
+ is_application? ? show_help_application_summary(console) : show_help_command_summary(console)
16
+ show_help_banner(console) if has_banner?
17
+ show_help_options(console) if has_options?
18
+ show_help_commands(console) if has_commands?
19
+ Kernel.exit(0)
20
+ end
21
+
22
+ private
23
+ # Prints a help summary about the application.
24
+ #
25
+ # @param console [Bovem::Console] The console object to use to print.
26
+ def show_help_application_summary(console)
27
+ # Application
28
+ console.write(i18n.help_name)
29
+ console.write("%s %s%s" % [name, version, has_description? ? " - " + description : ""], "\n", 4, true)
30
+ show_synopsis(console)
31
+ end
32
+
33
+ # Prints a synopsis about the application.
34
+ #
35
+ # @param console [Bovem::Console] The console object to use to print.
36
+ def show_synopsis(console)
37
+ console.write("")
38
+ console.write(i18n.help_synopsis)
39
+ console.write(synopsis.present? ? synopsis : i18n.help_application_synopsis % [executable_name, has_commands? ? i18n.help_subcommand_invocation : ""], "\n", 4, true)
40
+ end
41
+
42
+ # Prints a help summary about the command.
43
+ #
44
+ # @param console [Bovem::Console] The console object to use to print.
45
+ def show_help_command_summary(console)
46
+ console.write(i18n.help_synopsis)
47
+ console.write(synopsis.present? ? synopsis : i18n.help_command_synopsis % [application.executable_name, full_name(nil, " "), has_commands? ? i18n.help_subsubcommand_invocation : ""], "\n", 4, true)
48
+ end
49
+
50
+ # Prints the description of the command.
51
+ #
52
+ # @param console [Bovem::Console] The console object to use to print.
53
+ def show_help_banner(console)
54
+ console.write("")
55
+ console.write(i18n.help_description)
56
+ console.write(banner, "\n", 4, true)
57
+ end
58
+
59
+ # Prints information about the command's options.
60
+ #
61
+ # @param console [Bovem::Console] The console object to use to print.
62
+ def show_help_options(console)
63
+ console.write("")
64
+ console.write(is_application? ? i18n.help_global_options : i18n.help_options)
65
+
66
+ # First of all, grab all options and construct labels
67
+ lefts = show_help_options_build_labels
68
+
69
+ console.with_indentation(4) do
70
+ lefts.keys.sort.each do |head|
71
+ show_help_option(console, lefts, head)
72
+ end
73
+ end
74
+ end
75
+
76
+ # Adjusts options names for printing.
77
+ #
78
+ # @return [Hash] The adjusted options for printing.
79
+ def show_help_options_build_labels
80
+ options.values.inject({}) do |lefts, option|
81
+ left = [option.complete_short, option.complete_long]
82
+ left.collect!{|l| l + " " + option.meta } if option.requires_argument?
83
+ lefts[left.join(", ")] = option.has_help? ? option.help : i18n.help_no_description
84
+ lefts
85
+ end
86
+ end
87
+
88
+ # Prints information about an option.
89
+ #
90
+ # @param console [Bovem::Console] The console object to use to print.
91
+ # @param lefts [Hash] The list of adjusted options.
92
+ # @param head [String] The option to print.
93
+ def show_help_option(console, lefts, head)
94
+ alignment = lefts.keys.collect(&:length).max
95
+ help = lefts[head]
96
+ console.write("%s - %s" % [head.ljust(alignment, " "), help], "\n", true, true)
97
+ end
98
+
99
+ # Prints information about the command's subcommands.
100
+ #
101
+ # @param console [Bovem::Console] The console object to use to print.
102
+ def show_help_commands(console)
103
+ alignment = prepare_show_help_commands(console)
104
+
105
+ console.with_indentation(4) do
106
+ commands.keys.sort.each do |name|
107
+ show_help_command(console, name, alignment)
108
+ end
109
+ end
110
+ end
111
+
112
+ # Starts printing information about the command's subcommands.
113
+ #
114
+ # @param console [Bovem::Console] The console object to use to print.
115
+ def prepare_show_help_commands(console)
116
+ console.write("")
117
+ console.write(is_application? ? i18n.help_commands : i18n.help_subcommands)
118
+ commands.keys.collect(&:length).max
119
+ end
120
+
121
+ # Prints information about a command's subcommand.
122
+ #
123
+ # @param name [String] The name of command to print.
124
+ # @param console [Bovem::Console] The console object to use to print.
125
+ def show_help_command(console, name, alignment)
126
+ # Find the maximum length of the commands
127
+ command = commands[name]
128
+ console.write("%s - %s" % [name.ljust(alignment, " "), command.description.present? ? command.description : i18n.help_no_description], "\n", true, true)
129
+ end
130
+ end
131
+
132
+ # Methods to manage options and subcommands.
133
+ module Children
134
+ attr_reader :commands
135
+ attr_reader :options
136
+
137
+ # Adds a new subcommand to this command.
138
+ #
139
+ # @param name [String] The name of this command. Must be unique.
140
+ # @param options [Hash] A set of options for this command.
141
+ # @return [Command] The newly added command.
142
+ def command(name, options = {}, &block)
143
+ @commands ||= HashWithIndifferentAccess.new
144
+
145
+ options = {name: name.to_s, parent: self, application: application}.merge(options.ensure_hash)
146
+ raise Bovem::Errors::Error.new(self, :duplicate_command, i18n.existing_command(full_name(name))) if @commands[name.to_s]
147
+
148
+ create_command(name, options, &block)
149
+ end
150
+
151
+ # Adds a new option to this command.
152
+ #
153
+ # @see Option#initialize
154
+ #
155
+ # @param name [String] The name of the option. Must be unique.
156
+ # @param forms [Array] An array of short and long forms for this option.
157
+ # @param options [Hash] The settings for the option.
158
+ # @param action [Proc] An optional action to pass to the option.
159
+ # @return [Option] The newly added option.
160
+ def option(name, forms = [], options = {}, &action)
161
+ name = name.ensure_string
162
+ @options ||= HashWithIndifferentAccess.new
163
+
164
+ if @options[name] then
165
+ if is_application? then
166
+ raise Bovem::Errors::Error.new(self, :duplicate_option, i18n.existing_option_global(name))
167
+ else
168
+ raise Bovem::Errors::Error.new(self, :duplicate_option, i18n.existing_option(name, full_name))
169
+ end
170
+ end
171
+
172
+ option = Bovem::Option.new(name, forms, options, &action)
173
+ option.parent = self
174
+ @options[name] = option
175
+ option
176
+ end
177
+
178
+ # Returns the list of subcommands of this command.
179
+ #
180
+ # @return [HashWithIndifferentAccess] The list of subcommands of this command.
181
+ def commands
182
+ @commands || HashWithIndifferentAccess.new
183
+ end
184
+
185
+ # Clear all subcommands of this commands.
186
+ #
187
+ # @return [Hash] The new (empty) list of subcommands of this command.
188
+ def clear_commands
189
+ @commands = {}
190
+ end
191
+
192
+ # Check if this command has subcommands.
193
+ #
194
+ # @return [Boolean] `true` if this command has subcommands, `false` otherwise.
195
+ def has_commands?
196
+ commands.length > 0
197
+ end
198
+
199
+ # Returns the list of options of this command.
200
+ #
201
+ # @return [HashWithIndifferentAccess] The list of options of this command.
202
+ def options
203
+ @options || HashWithIndifferentAccess.new
204
+ end
205
+
206
+ # Clear all the options of this commands.
207
+ # @return [Hash] The new (empty) list of the options of this command.
208
+ def clear_options
209
+ @options = {}
210
+ end
211
+
212
+ # Check if this command has options.
213
+ #
214
+ # @return [Boolean] `true` if this command has options, `false` otherwise.
215
+ def has_options?
216
+ options.length > 0
217
+ end
218
+
219
+ # Adds a new argument to this command.
220
+ #
221
+ # @param value [String] The argument to add.
222
+ def argument(value)
223
+ @args ||= []
224
+ @args << value
225
+ end
226
+
227
+ # Returns the list of arguments of this command.
228
+ #
229
+ # @return [Array] The list of arguments of this command.
230
+ def arguments
231
+ @args || []
232
+ end
233
+
234
+ # Get the list of the options of this command as an hash, where the keys are the options and the values are either
235
+ # the user inputs or the defaults values.
236
+ #
237
+ # If the two prefixes collides, the command options take precedence over application options.
238
+ #
239
+ # @param unprovided [Boolean] If to include also options that were not provided by the user and that don't have any default value.
240
+ # @param application [String] The prefix to use for including application's options. If falsy, only current command options will be included.
241
+ # @param prefix [String] The prefix to add to the option of this command.
242
+ # @param whitelist [Array] The list of options to include. By default all options are included.
243
+ # @return [HashWithIndifferentAccess] The requested options.
244
+ def get_options(unprovided = false, application = "application_", prefix = "", *whitelist)
245
+ rv = HashWithIndifferentAccess.new
246
+ rv.merge!(self.application.get_options(unprovided, nil, application, *whitelist)) if application && !is_application?
247
+ rv.merge!(get_current_options(unprovided, prefix, whitelist))
248
+ rv
249
+ end
250
+
251
+ private
252
+ # Creates a new command.
253
+ #
254
+ # @param name [String] The name of this command.
255
+ # @param options [Hash] The settings for this command.
256
+ # @return [Command] The new command.
257
+ def create_command(name, options, &block)
258
+ command = Bovem::Command.new(options, &block)
259
+ command.option(:help, [i18n.help_option_short_form, i18n.help_option_long_form], help: i18n.help_message){|c, _| c.show_help }
260
+ @commands[name.to_s] = command
261
+ command
262
+ end
263
+
264
+ # Gets the list of the options of this command.
265
+ # @param unprovided [Boolean] If to include also options that were not provided by the user and that don't have any default value.
266
+ # @param prefix [String] The prefix to add to the option of this command.
267
+ # @param whitelist [Array] The list of options to include. By default all options are included.
268
+ # @return [HashWithIndifferentAccess] The requested options.
269
+ def get_current_options(unprovided, prefix, whitelist)
270
+ rv = HashWithIndifferentAccess.new
271
+ whitelist = (whitelist.present? ? whitelist : options.keys).collect(&:to_s)
272
+
273
+ options.each do |key, option|
274
+ rv["#{prefix}#{key}"] = option.value if include_option?(whitelist, unprovided, key, option)
275
+ end
276
+
277
+ rv
278
+ end
279
+
280
+ # Checks if a option must be included in a hash.
281
+ #
282
+ # @param whitelist [Array] The list of options to include.
283
+ # @param unprovided [Boolean] If to include also options that were not provided by the user and that don't have any default value.
284
+ # @param key [String] The option name.
285
+ # @param option [Option] The option to include.
286
+ # @return [Boolean] Whether to include the option.
287
+ def include_option?(whitelist, unprovided, key, option)
288
+ whitelist.include?(key.to_s) && (option.provided? || option.has_default? || (unprovided && option.action.nil?))
289
+ end
290
+ end
291
+ end
292
+
293
+ # This class represent a command (action) for Bovem.
294
+ #
295
+ # Every command has the execution block and a set of option. Optionally, it also has before and after hooks.
296
+ #
297
+ # @attribute name
298
+ # @return [String] The name of this command. At runtime you can invoke it using the minimum number of letters to uniquely distinguish it from others.
299
+ # @attribute description
300
+ # @return [String] A very short description of what this command does.
301
+ # @attribute banner
302
+ # @return [String] A long description of this command.
303
+ # @attribute synopsis
304
+ # @return [String] A synopsis of the typical command line usage.
305
+ # @attribute before
306
+ # @return [Proc] A hook to execute before the command's action. It is executed only if no subcommand is executed.
307
+ # @attribute action
308
+ # @return [Proc] The action of this command. It is executed only if no subcommand is executed.
309
+ # @attribute after
310
+ # @return [Proc] A hook to execute after the command's action. It is executed only if no subcommand is executed.
311
+ # @attribute application
312
+ # @return [Application] The application this command belongs to.
313
+ # @attribute parent
314
+ # @return [Command] The parent of this command.
315
+ # @attribute [r] commands
316
+ # @return [Array] The subcommands associated to this command.
317
+ # @attribute [r] options
318
+ # @return [Array] The options available for this command.
319
+ # @attribute [r] arguments
320
+ # @return [Array] The arguments provided to this command.
321
+ class Command
322
+ attr_accessor :name
323
+ attr_accessor :description
324
+ attr_accessor :banner
325
+ attr_accessor :synopsis
326
+ attr_accessor :before
327
+ attr_accessor :action
328
+ attr_accessor :after
329
+ attr_accessor :application
330
+ attr_accessor :parent
331
+
332
+ include Lazier::I18n
333
+ include Bovem::CommandMethods::Help
334
+ include Bovem::CommandMethods::Children
335
+
336
+ # Creates a new command.
337
+ #
338
+ # @param options [Hash] The settings to initialize the command with.
339
+ def initialize(options = {}, &block)
340
+ setup_with(options)
341
+ instance_eval(&block) if block_given?
342
+ end
343
+
344
+ # Reads and optionally sets the name of this command.
345
+ #
346
+ # @param value [NilClass|Object] The new name of this command.
347
+ # @return [String] The name of this command.
348
+ def name(value = nil)
349
+ @name = value if !value.nil?
350
+ @name
351
+ end
352
+
353
+ # Gets a full name, that is the name of this command and its ancestor. Optionally it also appends a suffix
354
+ #
355
+ # @param suffix [String] A suffix to append.
356
+ # @param separator [String] The separator to use for components.
357
+ # @return [String] The full name.
358
+ def full_name(suffix = nil, separator = ":")
359
+ if is_application? then
360
+ nil
361
+ else
362
+ [@parent ? @parent.full_name(nil, separator) : nil, !is_application? ? name : nil, suffix].compact.join(separator)
363
+ end
364
+ end
365
+
366
+ # Reads and optionally sets the short description of this command.
367
+ #
368
+ # @param value [NilClass|Object] The new short description of this command.
369
+ # @return [String] The short description of this command.
370
+ def description(value = nil)
371
+ @description = value if !value.nil?
372
+ @description
373
+ end
374
+
375
+ # Reads and optionally sets the description of this command.
376
+ #
377
+ # @param value [NilClass|Object] The new description of this command.
378
+ # @return [String] The description of this command.
379
+ def banner(value = nil)
380
+ @banner = value if !value.nil?
381
+ @banner
382
+ end
383
+
384
+ # Reads and optionally sets the synopsis of this command.
385
+ #
386
+ # @param value [NilClass|Object] The new synopsis of this command.
387
+ # @return [String] The synopsis of this command.
388
+ def synopsis(value = nil)
389
+ @synopsis = value if !value.nil?
390
+ @synopsis
391
+ end
392
+
393
+ # Reads and optionally sets the before hook, that is a block executed before the action of this command.
394
+ #
395
+ # This hook is only executed if no subcommand is executed.
396
+ #
397
+ # @param method [String|Symbol|NilClass] The method of the application to hookup.
398
+ # @param hook [Proc] The block to hookup if method is not provided.
399
+ # @return [Proc|Symbol|NilClass] The before hook of this command.
400
+ def before(method = nil, &hook)
401
+ @before = assign_hook(method, &hook) if method || hook
402
+ @before
403
+ end
404
+
405
+ # Reads and optionally sets the action of this command.
406
+ #
407
+ # A command action is only executed if no subcommand is executed.
408
+ #
409
+ # @param method [String|Symbol|NilClass] The method of the application to hookup.
410
+ # @param hook [Proc] The block to hookup if method is not provided.
411
+ # @return [Proc|Symbol|NilClass] The action of this command.
412
+ def action(method = nil, &hook)
413
+ @action = assign_hook(method, &hook) if method || hook
414
+ @action
415
+ end
416
+
417
+ # Sets the after hook, that is a block executed after the action of this command.
418
+ #
419
+ # This hook is only executed if no subcommand is executed.
420
+ #
421
+ # @param method [String|Symbol|NilClass] The method of the application to hookup.
422
+ # @param hook [Proc] The block to hookup if method is not provided.
423
+ # @return [Proc|Symbol|NilClass] The after hook of this command.
424
+ def after(method = nil, &hook)
425
+ @after = assign_hook(method, &hook) if method || hook
426
+ @after
427
+ end
428
+
429
+ # Returns the application this command belongs to.
430
+ #
431
+ # @return [Application] The application this command belongs to or `self`, if the command is an Application.
432
+ def application
433
+ is_application? ? self : @application
434
+ end
435
+
436
+ # Checks if the command is an application.
437
+ #
438
+ # @return [Boolean] `true` if command is an application, `false` otherwise.
439
+ def is_application?
440
+ is_a?(Bovem::Application)
441
+ end
442
+
443
+ # Check if this command has a description.
444
+ #
445
+ # @return [Boolean] `true` if this command has a description, `false` otherwise.
446
+ def has_description?
447
+ description.present?
448
+ end
449
+
450
+ # Check if this command has a banner.
451
+ #
452
+ # @return [Boolean] `true` if this command has a banner, `false` otherwise.
453
+ def has_banner?
454
+ banner.present?
455
+ end
456
+
457
+ # Setups the command.
458
+ #
459
+ # @param options [Hash] The settings for this command.
460
+ # @return [Command] The command.
461
+ def setup_with(options = {})
462
+ options = {} if !options.is_a?(::Hash)
463
+ setup_i18n(options)
464
+
465
+ options.each_pair do |option, value|
466
+ method = option.to_s
467
+
468
+ if respond_to?(method) && self.method(method).arity != 0 then
469
+ send(method, value)
470
+ elsif respond_to?(method + "=") then
471
+ send(method + "=", value)
472
+ end
473
+ end
474
+
475
+ self
476
+ end
477
+
478
+ # Executes this command, running its action or a subcommand.
479
+ #
480
+ # @param args [Array] The arguments to pass to the command.
481
+ def execute(args)
482
+ subcommand = Bovem::Parser.parse(self, args)
483
+
484
+ if subcommand.present? then # We have a subcommand to call
485
+ commands[subcommand[:name]].execute(subcommand[:args])
486
+ elsif action then # Run our action
487
+ # Run the before hook
488
+ execute_hook(before)
489
+
490
+ # Run the action
491
+ execute_hook(action)
492
+
493
+ # Run the after hook
494
+ execute_hook(after)
495
+ else # Show the help
496
+ show_help
497
+ end
498
+ end
499
+
500
+ private
501
+ # Setups the application localization.
502
+ #
503
+ # @param options [Hash] The settings for this command.
504
+ def setup_i18n(options)
505
+ i18n_setup("bovem.application", ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/"))
506
+ self.i18n = (options[:locale]).ensure_string
507
+ end
508
+
509
+ # Assigns a hook to a command.
510
+ #
511
+ # @param method [String|Symbol|NilClass] The method of the application to hookup.
512
+ # @param block [Proc] The block to hookup if method is not provided.
513
+ def assign_hook(method, &hook)
514
+ assigned = nil
515
+ assigned = method if method.is_a?(::String) || method.is_a?(::Symbol)
516
+ assigned = hook if !assigned && hook && hook.arity == 1
517
+ assigned
518
+ end
519
+
520
+ # Executes a hook.
521
+ #
522
+ # @param hook [String|Symbol|Proc|NilClass] The hook to execute.
523
+ def execute_hook(hook)
524
+ if hook then
525
+ hook.is_a?(::String) || hook.is_a?(::Symbol) ? application.send(hook, self) : hook.call(self)
526
+ end
527
+ end
528
+ end
529
+ end