mamertes 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +15 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +6 -0
  4. data/.travis-gemfile +15 -0
  5. data/.travis.yml +10 -0
  6. data/.yardopts +1 -0
  7. data/Gemfile +21 -0
  8. data/README.md +126 -0
  9. data/Rakefile +29 -0
  10. data/doc/Mamertes.html +155 -0
  11. data/doc/Mamertes/Application.html +3057 -0
  12. data/doc/Mamertes/Command.html +7031 -0
  13. data/doc/Mamertes/CommandMethods.html +125 -0
  14. data/doc/Mamertes/CommandMethods/Children.html +1286 -0
  15. data/doc/Mamertes/CommandMethods/Help.html +209 -0
  16. data/doc/Mamertes/Error.html +631 -0
  17. data/doc/Mamertes/Localizer.html +376 -0
  18. data/doc/Mamertes/Option.html +6671 -0
  19. data/doc/Mamertes/Parser.html +276 -0
  20. data/doc/Mamertes/ParserMethods.html +125 -0
  21. data/doc/Mamertes/ParserMethods/General.html +134 -0
  22. data/doc/Mamertes/ParserMethods/General/ClassMethods.html +574 -0
  23. data/doc/Mamertes/Version.html +189 -0
  24. data/doc/_index.html +276 -0
  25. data/doc/class_list.html +54 -0
  26. data/doc/css/common.css +1 -0
  27. data/doc/css/full_list.css +57 -0
  28. data/doc/css/style.css +338 -0
  29. data/doc/file.README.html +198 -0
  30. data/doc/file_list.html +56 -0
  31. data/doc/frames.html +28 -0
  32. data/doc/index.html +198 -0
  33. data/doc/js/app.js +214 -0
  34. data/doc/js/full_list.js +178 -0
  35. data/doc/js/jquery.js +4 -0
  36. data/doc/method_list.html +509 -0
  37. data/doc/top-level-namespace.html +112 -0
  38. data/lib/mamertes.rb +18 -0
  39. data/lib/mamertes/application.rb +206 -0
  40. data/lib/mamertes/command.rb +529 -0
  41. data/lib/mamertes/option.rb +236 -0
  42. data/lib/mamertes/parser.rb +317 -0
  43. data/lib/mamertes/version.rb +24 -0
  44. data/locales/en.yml +40 -0
  45. data/locales/it.yml +40 -0
  46. data/mamertes.gemspec +30 -0
  47. data/spec/coverage_helper.rb +20 -0
  48. data/spec/mamertes/application_spec.rb +181 -0
  49. data/spec/mamertes/command_spec.rb +526 -0
  50. data/spec/mamertes/option_spec.rb +274 -0
  51. data/spec/mamertes/parser_spec.rb +126 -0
  52. data/spec/spec_helper.rb +15 -0
  53. metadata +115 -0
@@ -0,0 +1,112 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.8.6.2
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ hasFrames = window.top.frames.main ? true : false;
19
+ relpath = '';
20
+ framesUrl = "frames.html#!" + escape(window.location.href);
21
+ </script>
22
+
23
+
24
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
25
+
26
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
27
+
28
+
29
+ </head>
30
+ <body>
31
+ <div id="header">
32
+ <div id="menu">
33
+
34
+ <a href="_index.html">Index</a> &raquo;
35
+
36
+
37
+ <span class="title">Top Level Namespace</span>
38
+
39
+
40
+ <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
41
+ </div>
42
+
43
+ <div id="search">
44
+
45
+ <a class="full_list_link" id="class_list_link"
46
+ href="class_list.html">
47
+ Class List
48
+ </a>
49
+
50
+ <a class="full_list_link" id="method_list_link"
51
+ href="method_list.html">
52
+ Method List
53
+ </a>
54
+
55
+ <a class="full_list_link" id="file_list_link"
56
+ href="file_list.html">
57
+ File List
58
+ </a>
59
+
60
+ </div>
61
+ <div class="clear"></div>
62
+ </div>
63
+
64
+ <iframe id="search_frame"></iframe>
65
+
66
+ <div id="content"><h1>Top Level Namespace
67
+
68
+
69
+
70
+ </h1>
71
+
72
+ <dl class="box">
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+ </dl>
82
+ <div class="clear"></div>
83
+
84
+ <h2>Defined Under Namespace</h2>
85
+ <p class="children">
86
+
87
+
88
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="Mamertes.html" title="Mamertes (module)">Mamertes</a></span>
89
+
90
+
91
+
92
+
93
+ </p>
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ </div>
104
+
105
+ <div id="footer">
106
+ Generated on Sat Aug 10 16:23:08 2013 by
107
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
+ 0.8.6.2 (ruby-1.9.3).
109
+ </div>
110
+
111
+ </body>
112
+ </html>
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the mamertes 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
+ require "rubygems"
8
+ require "optparse"
9
+ require "prettyprint"
10
+ require "bovem"
11
+
12
+ Lazier.load!(:object)
13
+
14
+ require "mamertes/version" if !defined?(Mamertes::Version)
15
+ require "mamertes/command"
16
+ require "mamertes/option"
17
+ require "mamertes/application"
18
+ require "mamertes/parser"
@@ -0,0 +1,206 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the mamertes 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
+ # Yet another command line manager.
8
+ module Mamertes
9
+ # This exception is raised when something goes wrong.
10
+ #
11
+ # @attribute [r] target
12
+ # @return [Object] The target of this error.
13
+ # @attribute [r] reason
14
+ # @return [Symbol] The reason of failure.
15
+ # @attribute [r] message
16
+ # @return [String] A human readable message.
17
+ class Error < ArgumentError
18
+ attr_reader :target
19
+ attr_reader :reason
20
+ attr_reader :message
21
+
22
+ # Initializes a new error
23
+ #
24
+ # @param target [Object] The target of this error.
25
+ # @param reason [Symbol] The reason of failure.
26
+ # @param message [String] A human readable message.
27
+ def initialize(target, reason, message)
28
+ super(message)
29
+
30
+ @target = target
31
+ @reason = reason
32
+ @message = message
33
+ end
34
+ end
35
+
36
+ # This class is used to localize strings inside classes methods.
37
+ class Localizer < ::Lazier::Localizer
38
+ # Initialize a new localizer.
39
+ #
40
+ # @param locale [String|Symbol] The locale to use for localization.
41
+ def initialize(locale)
42
+ super(:mamertes, ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/"), locale)
43
+ end
44
+
45
+ # Localize a message in a specified locale.
46
+ #
47
+ # @param locale [String|Symbol] The locale to use for localization.
48
+ # @param message [String|Symbol] The message to localize.
49
+ # @param args [Array] Optional arguments to localize the message.
50
+ # @return [String|R18n::Untranslated] The localized message.
51
+ def self.localize_on_locale(locale, message, *args)
52
+ new(locale).i18n.send(message, *args)
53
+ end
54
+ end
55
+
56
+ # This is the main class for a Mamertes application.
57
+ #
58
+ # Basically is the same of a command, but it adds support for application version.
59
+ #
60
+ # @attribute version
61
+ # @return [String] The version of the application.
62
+ # @attribute shell
63
+ # @return [::Bovem::Shell] A shell helper.
64
+ # @attribute console
65
+ # @return [::Bovem::Console] A console helper.
66
+ # @attribute skip_commands
67
+ # @return [Boolean] If to skip commands run via {#run}.
68
+ # @attribute show_commands
69
+ # @return [Boolean] If to show command lines run via {#run}.
70
+ # @attribute output_commands
71
+ # @return [Boolean] If to show the output of the commands run via {#run}.
72
+ class Application < ::Mamertes::Command
73
+ attr_accessor :version
74
+ attr_accessor :shell
75
+ attr_accessor :console
76
+ attr_accessor :skip_commands
77
+ attr_accessor :show_commands
78
+ attr_accessor :output_commands
79
+
80
+ # Initializes a new Mamertes application.
81
+ #
82
+ # In options, you can override the command line arguments with `:__args__`, and you can skip execution by specifying `run: false`.
83
+ #
84
+ # @see Command#setup_with
85
+ #
86
+ # @param options [Hash] The settings to initialize the application with.
87
+ # @return [Application] The created application.
88
+ def self.create(options = {}, &block)
89
+ raise Mamertes::Error.new(Mamertes::Application, :missing_block, ::Mamertes::Localizer.localize_on_locale(options[:locale], :missing_app_block)) if !block_given?
90
+ run, args, options = setup_application_option(options)
91
+
92
+ begin
93
+ create_application(run, args, options, &block)
94
+ rescue => e
95
+ Kernel.puts(e.to_s)
96
+ Kernel.exit(1)
97
+ end
98
+ end
99
+
100
+ # Creates a new application.
101
+ #
102
+ # @param options [Hash] The settings to initialize the application with.
103
+ def initialize(options = {}, &block)
104
+ super(options, &block)
105
+
106
+ @shell = ::Bovem::Shell.instance
107
+ @console = @shell.console
108
+ @skip_commands = false
109
+ @show_commands = false
110
+ @output_commands = false
111
+
112
+ help_option
113
+ end
114
+
115
+ # Reads and optionally sets the version of this application.
116
+ #
117
+ # @param value [String|nil] The new version of this application.
118
+ # @return [String|nil] The version of this application.
119
+ def version(value = nil)
120
+ @version = value.ensure_string if !value.nil?
121
+ @version
122
+ end
123
+
124
+ # Executes this application.
125
+ #
126
+ # @param args [Array] The command line to pass to this application. Defaults to `ARGV`.
127
+ def execute(args = nil)
128
+ super(args || ARGV)
129
+ end
130
+
131
+ # Adds a help command and a help option to this application.
132
+ def help_option
133
+ command(:help, description: i18n.help_command_description) do
134
+ action { |command| application.command_help(command) }
135
+ end
136
+
137
+ option(:help, [i18n.help_option_short_form, i18n.help_option_long_form], help: i18n.help_message){ |application, _| application.show_help }
138
+ end
139
+
140
+ # The name of the current executable.
141
+ #
142
+ # @return [String] The name of the current executable.
143
+ def executable_name
144
+ $0
145
+ end
146
+
147
+ # Shows a help about a command.
148
+ #
149
+ # @param command [Command] The command to show help for.
150
+ def command_help(command)
151
+ fetch_commands_for_help(command).each do |arg|
152
+ # Find the command across
153
+ next_command = ::Mamertes::Parser.find_command(arg, command, [])
154
+
155
+ if next_command then
156
+ command = command.commands[next_command[:name]]
157
+ else
158
+ break
159
+ end
160
+ end
161
+
162
+ command.show_help
163
+ end
164
+
165
+ # Runs a command into the shell.
166
+ #
167
+ # @param command [String] The string to run.
168
+ # @param message [String] A message to show before running.
169
+ # @param show_exit [Boolean] If show the exit status.
170
+ # @param fatal [Boolean] If quit in case of fatal errors.
171
+ # @return [Hash] An hash with `status` and `output` keys.
172
+ def run(command, message = nil, show_exit = true, fatal = true)
173
+ @shell.run(command, message, !@skip_commands, show_exit, @output_commands, @show_commands, fatal)
174
+ end
175
+
176
+ private
177
+ # Setup options for application creation.
178
+ #
179
+ # @param options [Hash] The options to setups.
180
+ # @return [Array] If to run the application, the arguments and the specified options.
181
+ def self.setup_application_option(options)
182
+ options = {name: ::Mamertes::Localizer.localize_on_locale(options[:locale], :default_application_name), parent: nil, application: nil}.merge(options.ensure_hash)
183
+ run = options.delete(:run)
184
+ [(!run.nil? ? run : true).to_boolean, options.delete(:__args__), options]
185
+ end
186
+
187
+ # Create the application.
188
+ #
189
+ # @param run [Boolean ]If to run the application.
190
+ # @param args [Hash] The arguments to use for running.
191
+ # @param options [Hash] The options of the application.
192
+ # @return [Application] The new application.
193
+ def self.create_application(run, args, options, &block)
194
+ application = new(options, &block)
195
+ application.execute(args) if application && run
196
+ application
197
+ end
198
+
199
+ # Fetch a command list for showing help.
200
+ #
201
+ # @param command [Command] The command to show help for.
202
+ def fetch_commands_for_help(command)
203
+ command.arguments.collect {|c| c.split(":") }.flatten.collect(&:strip).select(&:present?)
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,529 @@
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the mamertes 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 Mamertes
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 Mamertes::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 Mamertes::Error.new(self, :duplicate_option, i18n.existing_option_global(name))
167
+ else
168
+ raise Mamertes::Error.new(self, :duplicate_option, i18n.existing_option(name, full_name))
169
+ end
170
+ end
171
+
172
+ option = ::Mamertes::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 = ::Mamertes::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 Mamertes.
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 Mamertes::CommandMethods::Help
334
+ include Mamertes::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?(Mamertes::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 = Mamertes::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(:mamertes, ::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