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
data/lib/bovem/shell.rb CHANGED
@@ -561,12 +561,12 @@ module Bovem
561
561
  #
562
562
  # @return [Shell] A new instance.
563
563
  def self.instance
564
- @instance ||= ::Bovem::Shell.new
564
+ @instance ||= Bovem::Shell.new
565
565
  end
566
566
 
567
567
  # Initializes a new Shell.
568
568
  def initialize
569
- @console = ::Bovem::Console.instance
569
+ @console = Bovem::Console.instance
570
570
  i18n_setup(:bovem, ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/"))
571
571
  end
572
572
  end
data/lib/bovem/version.rb CHANGED
@@ -11,13 +11,13 @@ module Bovem
11
11
  # @see http://semver.org
12
12
  module Version
13
13
  # The major version.
14
- MAJOR = 2
14
+ MAJOR = 3
15
15
 
16
16
  # The minor version.
17
- MINOR = 4
17
+ MINOR = 0
18
18
 
19
19
  # The patch version.
20
- PATCH = 1
20
+ PATCH = 0
21
21
 
22
22
  # The current version number of Bovem.
23
23
  STRING = [MAJOR, MINOR, PATCH].compact.join(".")
data/locales/en.yml CHANGED
@@ -41,3 +41,36 @@
41
41
  mkdir_file: "Path {mark=bright}%1{/mark} is currently a file."
42
42
  mkdir_denied: "Cannot create following directory due to permission denied: {mark=bright}%1{/mark}."
43
43
  mkdir_error: "Cannot create following directories:"
44
+ application:
45
+ default_application_name: "__APPLICATION__"
46
+ ambigous_command: "Command shortcut \"%1\" is ambiguous across commands %2. Please add some other characters."
47
+ needless_argument: "Option %1 does not expects an argument."
48
+ missing_argument: "Option %1 expects an argument."
49
+ invalid_option: "Invalid option %1."
50
+ invalid_integer: "Option %1 expects a valid integer as argument."
51
+ invalid_float: "Option %1 expects a valid floating number as argument."
52
+ conflicting_options: "Options %1 and %2 have conflicting forms."
53
+ missing_option: "Required option %1 is missing."
54
+ invalid_value: "Value of option %1 must be one of these values: %2."
55
+ invalid_for_regexp: "Value of option %1 must match the regular expression: %2."
56
+ help_option_short_form: "-h"
57
+ help_option_long_form: "--help"
58
+ help_message: "Shows this message."
59
+ help_arg: "ARG"
60
+ help_name: "[NAME]"
61
+ help_application_synopsis: "%s [options] %s[command-options] [arguments] "
62
+ help_command_synopsis: "%s [options] %s %s[command-options] [arguments] "
63
+ help_synopsis: "[SYNOPSIS]"
64
+ help_description: "[DESCRIPTION]"
65
+ help_no_description: "*NO DESCRIPTION PROVIDED*"
66
+ help_options: "[OPTIONS]"
67
+ help_global_options: "[GLOBAL OPTIONS]"
68
+ help_commands: "[COMMANDS]"
69
+ help_subcommands: "[SUBCOMMANDS]"
70
+ help_subcommand_invocation: "[command [sub-command ...]] "
71
+ help_subsubcommand_invocation: "[sub-command [sub-sub-command ...]] "
72
+ help_command_description: "Shows a help about a command."
73
+ existing_command: "The command \"%1\" already exists."
74
+ existing_option_global: "The global option \"%1\" already exists."
75
+ existing_option: "The option \"%1\" already exists for the command \"%2\"."
76
+ missing_app_block: "You have to provide a block to the Bovem::Application constructor!"
data/locales/it.yml CHANGED
@@ -41,3 +41,36 @@
41
41
  mkdir_file: "Il percorso {mark=bright}%1{/mark} è attualmente un file."
42
42
  mkdir_denied: "Impossible creare la seguente cartella a causa di permessi negati: {mark=bright}%1{/mark}."
43
43
  mkdir_error: "Impossibile creare le seguenti cartelle:"
44
+ application:
45
+ default_application_name: "__APPLICAZIONE__"
46
+ ambigous_command: "La scorciatoia di comando \"%1\" è ambigua tra i seguenti comandi %2. Per favore aggiungi qualche altro carattere."
47
+ needless_argument: "L'opzione %1 non richiede un argomento."
48
+ missing_argument: "L'opzione %1 richiede un argomento."
49
+ invalid_option: "L'opzione %1 non è valida."
50
+ invalid_integer: "L'opzione %1 richiede un numero intero valido come argomento."
51
+ invalid_float: "L'opzione %1 richiede un numero decimale valido come argomento."
52
+ conflicting_options: "Le opzioni %1 e %2 hanno forme in conflitto."
53
+ missing_option: "L'opzione richiesta %1 è mancante."
54
+ invalid_value: "Il valore dell'opzione %1 deve essere uno dei seguenti valori: %2."
55
+ invalid_for_regexp: "Il valore dell'opzione %1 deve soddisfare questa espressione regolare: %2."
56
+ help_option_short_form: "-h"
57
+ help_option_long_form: "--help"
58
+ help_message: "Mostra questo messaggio."
59
+ help_arg: "ARG"
60
+ help_name: "[NOME]"
61
+ help_application_synopsis: "%s [opzioni] %s[opzioni-comando] [argomenti] "
62
+ help_command_synopsis: "%s [opzioni] %s %s[opzioni-comando] [argomenti] "
63
+ help_synopsis: "[UTILIZZO]"
64
+ help_description: "[DESCRIZIONE]"
65
+ help_no_description: "*NESSUNA DESCRIZIONE FORNITA*"
66
+ help_options: "[OPZIONI]"
67
+ help_global_options: "[OPZIONI GLOBALI]"
68
+ help_commands: "[COMANDI]"
69
+ help_subcommands: "[SOTTOCOMANDI]"
70
+ help_subcommand_invocation: "[comando [sotto-comando ...]] "
71
+ help_subsubcommand_invocation: "[sotto-comando [sotto-sotto-comando ...]] "
72
+ help_command_description: "Mostra aiuto riguardo un comando."
73
+ existing_command: "Il comando \"%1\" è già esistente."
74
+ existing_option_global: "L'opzione globale \"%1\" è già esistente."
75
+ existing_option: "L'opzione \"%1\" è già esistente per il comando \"%2\"."
76
+ missing_app_block: "Devi fornire un blocco per il costruttore di Bovem::Application!"
@@ -0,0 +1,170 @@
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
+ require "spec_helper"
8
+
9
+ describe Bovem::Application do
10
+ let(:application) { Bovem::Application.new(locale: :en) }
11
+
12
+ describe "#initialize" do
13
+ it "should call the parent constructor" do
14
+ options = {a: :b}
15
+ block = Proc.new {}
16
+
17
+ expect(Bovem::Command).to receive(:new).with(options, &block)
18
+ Bovem::Application.new(options, &block)
19
+ end
20
+
21
+ it "should set good defaults" do
22
+ expect(application.shell).to eq(Bovem::Shell.instance)
23
+ expect(application.console).to eq(application.shell.console)
24
+ expect(application.skip_commands).to be_false
25
+ expect(application.show_commands).to be_false
26
+ expect(application.output_commands).to be_false
27
+ end
28
+ end
29
+
30
+ describe "#version" do
31
+ it "should set and return the version" do
32
+ expect(application.version).to be_nil
33
+ expect(application.version("another")).to eq("another")
34
+ expect(application.version(nil)).to eq("another")
35
+ end
36
+ end
37
+
38
+ describe "#help_option" do
39
+ it "should add a command and a option" do
40
+ expect(application).to receive(:command).with(:help, {description: "Shows a help about a command."})
41
+ expect(application).to receive(:option).with(:help, ["-h", "--help"], {help: "Shows this message."})
42
+ application.help_option
43
+ end
44
+
45
+ it "should execute associated actions" do
46
+ expect(application).to receive(:show_help).exactly(2)
47
+ expect(application).to receive(:command_help)
48
+
49
+ application.execute(["help", "command"])
50
+ application.execute("-h")
51
+ end
52
+ end
53
+
54
+ describe "#executable_name" do
55
+ it "should return executable name" do
56
+ expect(application.executable_name).to eq($0)
57
+ end
58
+ end
59
+
60
+ describe "#command_help" do
61
+ it "should show the help for the command" do
62
+ command = application.command "command"
63
+ subcommand = command.command "subcommand"
64
+
65
+ expect(application).to receive(:show_help)
66
+ application.command_help(application)
67
+
68
+ expect(command).to receive(:show_help)
69
+ application.argument(command.name)
70
+ application.command_help(application)
71
+
72
+ expect(subcommand).to receive(:show_help)
73
+ application.argument(subcommand.name)
74
+ application.command_help(application)
75
+
76
+ expect(subcommand).to receive(:show_help)
77
+ application.argument("foo")
78
+ application.command_help(application)
79
+ end
80
+ end
81
+
82
+ describe "#run" do
83
+ it "should forward to the shell" do
84
+ expect(application.shell).to receive(:run).with("COMMAND", "MESSAGE", true, "A", false, false, "B")
85
+ application.run("COMMAND", "MESSAGE", "A", "B")
86
+
87
+ application.skip_commands = true
88
+ application.output_commands = true
89
+ application.show_commands = true
90
+ expect(application.shell).to receive(:run).with("COMMAND", "MESSAGE", false, "C", true, true, "D")
91
+ application.run("COMMAND", "MESSAGE", "C", "D")
92
+ end
93
+ end
94
+
95
+ describe ".create" do
96
+ it "should complain about a missing block" do
97
+ expect { Bovem::Application.create }.to raise_error(Bovem::Errors::Error)
98
+ end
99
+
100
+ it "should print errors" do
101
+ allow(Bovem::Application).to receive(:create_application).and_raise(ArgumentError.new("ERROR"))
102
+ expect(Kernel).to receive(:puts).with("ERROR")
103
+ expect(Kernel).to receive(:exit).with(1)
104
+ Bovem::Application.create(__args__: []) {}
105
+ end
106
+
107
+ it "should create a default application" do
108
+ expect(Bovem::Application).to receive(:new).with({name: "__APPLICATION__", parent: nil, application: nil, locale: :en})
109
+ Bovem::Application.create({locale: :en}) {}
110
+ end
111
+
112
+ it "should create an application with given options and block" do
113
+ options = {name: "OK"}
114
+
115
+ expect(Bovem::Application).to receive(:new).with({name: "OK", parent: nil, application: nil})
116
+ application = Bovem::Application.create(options) {}
117
+ end
118
+
119
+ it "should execute the block" do
120
+ allow_any_instance_of(Bovem::Console).to receive(:write)
121
+ allow(Kernel).to receive(:exit)
122
+ options = {name: "OK", __args__: []}
123
+ check = false
124
+
125
+ application = Bovem::Application.create(options) { check = true }
126
+ expect(check).to be_true
127
+ expect(application.name).to eq("OK")
128
+ end
129
+
130
+ it "should execute the new application" do
131
+ args = []
132
+
133
+ application = Bovem::Application.create do
134
+ option("require", [], {})
135
+ option("format", [], {})
136
+ option("example", [], {})
137
+
138
+ action do |command|
139
+ args = command.arguments.join("-")
140
+ end
141
+ end
142
+
143
+ expect(args).to eq(ARGV.reject {|a| a =~ /^--/ }.join("-"))
144
+ end
145
+
146
+ it "can override arguments" do
147
+ args = []
148
+
149
+ application = Bovem::Application.create({__args__: ["C", "D"]}) do
150
+ action do |command|
151
+ args = command.arguments.join("-")
152
+ end
153
+ end
154
+
155
+ expect(args).to eq("C-D")
156
+ end
157
+
158
+ it "should not execute the application if requested to" do
159
+ args = []
160
+
161
+ application = Bovem::Application.create(run: false) do
162
+ action do |command|
163
+ args = command.arguments.join("-")
164
+ end
165
+ end
166
+
167
+ expect(args).to eq([])
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,526 @@
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
+ require "spec_helper"
8
+
9
+ describe Bovem::Command do
10
+ let(:application) {
11
+ Bovem::Application.new(locale: :en) {
12
+ action {}
13
+ }
14
+ }
15
+
16
+ let(:command) {
17
+ c = Bovem::Command.new(locale: :en)
18
+ c.application = application
19
+ c
20
+ }
21
+
22
+ describe "#initialize" do
23
+ it "should forward to #setup_with" do
24
+ expect(Bovem::Command.new(name: "command").name).to eq("command")
25
+ end
26
+
27
+ it "should call the block" do
28
+ count = 0
29
+ Bovem::Command.new(name: "command") { count += 1 }
30
+ expect(count).to eq(1)
31
+ end
32
+ end
33
+
34
+ describe "#name" do
35
+ it "should set and return the name" do
36
+ expect(command.name).to be_nil
37
+ expect(command.name("another")).to eq("another")
38
+ expect(command.name(nil)).to eq("another")
39
+ end
40
+ end
41
+
42
+ describe "#full_name" do
43
+ it "should retrieve the full hierarchy of the name" do
44
+ command.name = "root"
45
+ expect(command.full_name).to eq("root")
46
+
47
+ subcommand = Bovem::Command.new(name: "child")
48
+ subcommand.parent = command
49
+ expect(subcommand.full_name).to eq("root:child")
50
+ expect(subcommand.full_name(nil, " ")).to eq("root child")
51
+ expect(subcommand.full_name("A", " ")).to eq("root child A")
52
+ end
53
+ end
54
+
55
+ describe "#description" do
56
+ it "should set and return the description" do
57
+ expect(command.description).to be_nil
58
+ expect(command.description("another")).to eq("another")
59
+ expect(command.description(nil)).to eq("another")
60
+ end
61
+ end
62
+
63
+ describe "#banner" do
64
+ it "should set and return the banner" do
65
+ expect(command.banner).to be_nil
66
+ expect(command.banner("another")).to eq("another")
67
+ expect(command.banner(nil)).to eq("another")
68
+ end
69
+
70
+ end
71
+
72
+ describe "#synopsis" do
73
+ it "should set and return the synopsis" do
74
+ expect(command.synopsis).to be_nil
75
+ expect(command.synopsis("another")).to eq("another")
76
+ expect(command.synopsis(nil)).to eq("another")
77
+ end
78
+ end
79
+
80
+ describe "#before" do
81
+ it "should set and return the before hook" do
82
+ valid = Proc.new{|a| puts "OK" }
83
+
84
+ expect(command.before).to be_nil
85
+ expect(command.before(1)).to be_nil
86
+ expect(command.before { puts "OK" }).to be_nil
87
+ expect(command.before {|a, b| puts "OK" }).to be_nil
88
+ expect(command.before(:method)).to eq(:method)
89
+ expect(command.action("METHOD")).to eq("METHOD")
90
+ expect(command.before(&valid)).to eq(valid)
91
+ expect(command.before("METHOD", &valid)).to eq("METHOD")
92
+ end
93
+ end
94
+
95
+ describe "#action" do
96
+ it "should set and return the action" do
97
+ valid = Proc.new{|a| puts "OK" }
98
+
99
+ expect(command.action).to be_nil
100
+ expect(command.action(1)).to be_nil
101
+ expect(command.action { puts "OK" }).to be_nil
102
+ expect(command.action {|a, b| puts "OK" }).to be_nil
103
+ expect(command.action(:method)).to eq(:method)
104
+ expect(command.action("METHOD")).to eq("METHOD")
105
+ expect(command.action(&valid)).to eq(valid)
106
+ expect(command.action("METHOD", &valid)).to eq("METHOD")
107
+ end
108
+ end
109
+
110
+ describe "#after" do
111
+ it "should set and return the after hook" do
112
+ valid = Proc.new{|a| puts "OK" }
113
+
114
+ expect(command.after).to be_nil
115
+ expect(command.after(1)).to be_nil
116
+ expect(command.after { puts "OK" }).to be_nil
117
+ expect(command.after {|a, b| puts "OK" }).to be_nil
118
+ expect(command.after(:method)).to eq(:method)
119
+ expect(command.after("METHOD")).to eq("METHOD")
120
+ expect(command.after(&valid)).to eq(valid)
121
+ expect(command.after("METHOD", &valid)).to eq("METHOD")
122
+ end
123
+ end
124
+
125
+ describe "#has_description?" do
126
+ it "should check if the command has a description" do
127
+ expect(Bovem::Command.new.has_description?).to be_false
128
+ expect(Bovem::Command.new({description: "DESCRIPTION"}).has_description?).to be_true
129
+ end
130
+ end
131
+
132
+ describe "#has_banner?" do
133
+ it "should check if the command has a banner" do
134
+ expect(Bovem::Command.new.has_banner?).to be_false
135
+ expect(Bovem::Command.new({banner: "BANNER"}).has_banner?).to be_true
136
+ end
137
+ end
138
+
139
+ describe "#command" do
140
+ it "should add a subcommand" do
141
+ command.command("subcommand", {banner: "BANNER"}) do |option|
142
+ description("DESCRIPTION")
143
+ end
144
+
145
+ subcommand = command.commands["subcommand"]
146
+
147
+ expect(subcommand.name).to eq("subcommand")
148
+ expect(subcommand.parent).to be(command)
149
+ expect(subcommand.application).to be(application)
150
+ expect(subcommand.banner).to eq("BANNER")
151
+ expect(subcommand.description).to eq("DESCRIPTION")
152
+ end
153
+
154
+ it "should check for duplicates" do
155
+ command.command("subcommand")
156
+ expect {command.command("subcommand")}.to raise_error(Bovem::Errors::Error)
157
+ end
158
+ end
159
+
160
+ describe "#option" do
161
+ it "should add a subcommand" do
162
+ command.option("option", ["short", "long"], {type: String, help: "HELP"})
163
+
164
+ option = command.options["option"]
165
+
166
+ expect(option.name).to eq("option")
167
+ expect(option.short).to eq("s")
168
+ expect(option.long).to eq("long")
169
+ expect(option.help).to eq("HELP")
170
+ end
171
+
172
+ it "should check for duplicates" do
173
+ application.option("option")
174
+ command.option("option")
175
+ expect {command.option("option")}.to raise_error(Bovem::Errors::Error)
176
+ expect {application.option("option")}.to raise_error(Bovem::Errors::Error)
177
+ end
178
+ end
179
+
180
+ describe "#commands" do
181
+ it "should return the list of commands" do
182
+ expect(command.commands).to eq({})
183
+ command.command("subcommand1")
184
+ command.command("subcommand2")
185
+ expect(command.commands.values.collect(&:name).sort).to eq(["subcommand1", "subcommand2"])
186
+ end
187
+
188
+ it "should let access both with Symbol or String" do
189
+ command.command("subcommand1")
190
+ expect(command.commands).to be_a(HashWithIndifferentAccess)
191
+ expect(command.commands[:subcommand1]).to eq(command.commands["subcommand1"])
192
+ end
193
+ end
194
+
195
+ describe "#clear_commands" do
196
+ it "should remove commands" do
197
+ command.command("subcommand")
198
+ expect(command.commands.length == 1)
199
+ command.clear_commands
200
+ expect(command.commands.length == 0)
201
+ end
202
+ end
203
+
204
+ describe "#has_commands?" do
205
+ it "should check if the command has subcommands" do
206
+ expect(command.has_commands?).to be_false
207
+ command.command("subcommand")
208
+ expect(command.has_commands?).to be_true
209
+ end
210
+ end
211
+
212
+ describe "#clear_options" do
213
+ it "should remove options" do
214
+ command.option("option")
215
+ expect(command.options.length == 1)
216
+ command.clear_options
217
+ expect(command.options.length == 0)
218
+ end
219
+ end
220
+
221
+ describe "#options" do
222
+ it "should return the list of options" do
223
+ expect(command.options).to eq({})
224
+ command.option("option1")
225
+ command.option("option2")
226
+ expect(command.options.values.collect(&:name).sort).to eq(["option1", "option2"])
227
+ end
228
+
229
+ it "should let access both with Symbol or String" do
230
+ command.option("option1")
231
+ expect(command.options).to be_a(HashWithIndifferentAccess)
232
+ expect(command.options[:option1]).to eq(command.options["option1"])
233
+ end
234
+ end
235
+
236
+ describe "#has_options?" do
237
+ it "should check if the command has options" do
238
+ expect(command.has_options?).to be_false
239
+ command.option("option")
240
+ expect(command.has_options?).to be_true
241
+ end
242
+ end
243
+
244
+ describe "#argument" do
245
+ it "should add an argument to the command" do
246
+ expect(command.arguments).to eq([])
247
+ command.argument("A")
248
+ expect(command.arguments).to eq(["A"])
249
+ command.argument("B")
250
+ expect(command.arguments).to eq(["A", "B"])
251
+ end
252
+ end
253
+
254
+ describe "#arguments" do
255
+ it "should return arguments" do
256
+ expect(command.arguments).to eq([])
257
+ command.argument("A")
258
+ expect(command.arguments).to eq(["A"])
259
+ command.argument("B")
260
+ expect(command.arguments).to eq(["A", "B"])
261
+ end
262
+ end
263
+
264
+ describe "#application" do
265
+ it "should return the application" do
266
+ expect(command.application).to be(application)
267
+ expect(application.application).to be(application)
268
+ end
269
+ end
270
+
271
+ describe "#is_application?" do
272
+ it "should check if the command is an application" do
273
+ expect(command.is_application?).to be_false
274
+ expect(application.is_application?).to be_true
275
+ end
276
+ end
277
+
278
+ describe "#setup_with" do
279
+ it "should setup required option by calling proper methods" do
280
+ expect(command).to receive("name").with("new-command")
281
+ expect(command).to receive("application=").with(nil)
282
+ command.setup_with({name: "new-command", application: nil, invalid: false})
283
+ end
284
+ end
285
+
286
+ describe "#execute" do
287
+ it "should parse command line" do
288
+ allow(Kernel).to receive(:exit)
289
+ allow_any_instance_of(Bovem::Console).to receive(:write)
290
+
291
+ args = ["command"]
292
+ expect(Bovem::Parser).to receive(:parse).with(command, args)
293
+ command.execute(args)
294
+ end
295
+
296
+ it "should execute hooks and actions in sequence" do
297
+ check = []
298
+ child = []
299
+ args = ["command"]
300
+
301
+ command.before do |command|
302
+ check << "A"
303
+ end
304
+
305
+ command.action do |command|
306
+ check << "B"
307
+ end
308
+
309
+ command.after do |command|
310
+ check << "C"
311
+ end
312
+
313
+ command.command("subcommand") do
314
+ before do |command|
315
+ check << "D"
316
+ end
317
+
318
+ action do |command|
319
+ check << "E"
320
+ end
321
+
322
+ after do |command|
323
+ check << "F"
324
+ end
325
+ end
326
+
327
+ allow(Bovem::Parser).to receive(:parse).and_return(nil)
328
+ command.execute(args)
329
+ expect(check).to eq(["A", "B", "C"])
330
+ end
331
+
332
+ it "should execute the hooks even they are methods" do
333
+ check = []
334
+ child = []
335
+ args = ["command"]
336
+
337
+ allow(command).to receive(:application).and_return(Object.new)
338
+ allow(command.application).to receive(:action_before) { check << "A" }
339
+ allow(command.application).to receive("action_perform") { check << "B" }
340
+ allow(command.application).to receive(:action_after) { check << "C" }
341
+
342
+ command.before(:action_before)
343
+ command.action("action_perform")
344
+ command.after(:action_after)
345
+
346
+ allow(Bovem::Parser).to receive(:parse).and_return(nil)
347
+ command.execute(args)
348
+ expect(check).to eq(["A", "B", "C"])
349
+ end
350
+
351
+ it "should skip its actions and hooks and pass control to the subcommand" do
352
+ check = []
353
+ child = []
354
+ args = ["command"]
355
+
356
+ command.before do |command|
357
+ check << "A"
358
+ end
359
+
360
+ command.action do |command|
361
+ check << "B"
362
+ end
363
+
364
+ command.after do |command|
365
+ check << "C"
366
+ end
367
+
368
+ command.command("subcommand") do
369
+ before do |command|
370
+ check << "D"
371
+ end
372
+
373
+ action do |command|
374
+ check << "E"
375
+ end
376
+
377
+ after do |command|
378
+ check << "F"
379
+ end
380
+ end
381
+
382
+ allow(Bovem::Parser).to receive(:parse) do |cmd, args|
383
+ cmd == command ? {name: "subcommand", args: args} : nil
384
+ end
385
+ command.execute(args)
386
+ expect(check).to eq(["D", "E", "F"])
387
+ end
388
+
389
+ it "should show help if action is not defined and no subcommand is found" do
390
+ check = []
391
+ child = []
392
+ args = ["command"]
393
+
394
+ command.command("subcommand") do
395
+ before do |command|
396
+ check << "D"
397
+ end
398
+
399
+ action do |command|
400
+ check << "E"
401
+ end
402
+
403
+ after do |command|
404
+ check << "F"
405
+ end
406
+ end
407
+
408
+ allow(Bovem::Parser).to receive(:parse).and_return(nil)
409
+ expect(command).to receive(:show_help)
410
+ command.execute(args)
411
+ expect(check).to eq([])
412
+ end
413
+ end
414
+
415
+ describe "#get_options" do
416
+ let(:reference){
417
+ c = Bovem::Command.new("command") do
418
+ option("aaa", [], {type: Integer, default: 456})
419
+ option("bbb", [], {type: String})
420
+ option("ccc", [], {type: Array, default: ["1", "2"]})
421
+ end
422
+
423
+ c.application = Bovem::Application.new do |a|
424
+ option("aaa", [], {type: Integer, default: 123})
425
+ option("ddd", [], {type: Float})
426
+ action {}
427
+ end
428
+
429
+ c
430
+ }
431
+
432
+ it "should return the full list of options" do
433
+ Bovem::Parser.parse(reference.application, ["--aaa", "111", "--ddd", "2.0"])
434
+ Bovem::Parser.parse(reference, ["--bbb", "2.0", "--ccc", "A,B,C"])
435
+ expect(reference.get_options.symbolize_keys).to eq({application_aaa: 111, application_ddd: 2.0, aaa: 456, bbb: "2.0", ccc: ["A", "B", "C"]})
436
+ end
437
+
438
+ it "should only return provided options if required to" do
439
+ Bovem::Parser.parse(reference.application, ["--aaa", "111"])
440
+ Bovem::Parser.parse(reference, ["--ccc", "2.0"])
441
+ expect(reference.get_options(false).symbolize_keys).to eq({application_aaa: 111, aaa: 456, ccc: ["2.0"]})
442
+ end
443
+
444
+ it "should skip application options if required to" do
445
+ Bovem::Parser.parse(reference.application, ["--aaa", "111", "--ddd", "2.0"])
446
+ Bovem::Parser.parse(reference, ["--bbb", "2.0", "--ccc", "A,B,C"])
447
+ expect(reference.get_options(true, false).symbolize_keys).to eq({aaa: 456, bbb: "2.0", ccc: ["A", "B", "C"]})
448
+ expect(reference.get_options(true, nil).symbolize_keys).to eq({aaa: 456, bbb: "2.0", ccc: ["A", "B", "C"]})
449
+ end
450
+
451
+ it "should apply the requested prefix for command options" do
452
+ Bovem::Parser.parse(reference.application, ["--aaa", "111", "--ddd", "2.0"])
453
+ Bovem::Parser.parse(reference, ["--bbb", "2.0", "--ccc", "A,B,C"])
454
+ expect(reference.get_options(true, false, "PREFIX").symbolize_keys).to eq({PREFIXaaa: 456, PREFIXbbb: "2.0", PREFIXccc: ["A", "B", "C"]})
455
+ end
456
+
457
+ it "should apply the requested prefix for application options" do
458
+ Bovem::Parser.parse(reference.application, ["--aaa", "111", "--ddd", "2.0"])
459
+ Bovem::Parser.parse(reference, ["--bbb", "2.0", "--ccc", "A,B,C"])
460
+ expect(reference.get_options(true, "APP").symbolize_keys).to eq({APPaaa: 111, APPddd: 2.0, aaa: 456, bbb: "2.0", ccc: ["A", "B", "C"]})
461
+ end
462
+
463
+ it "should only return requested options" do
464
+ Bovem::Parser.parse(reference.application, ["--aaa", "111", "--ddd", "2.0"])
465
+ Bovem::Parser.parse(reference, ["--bbb", "2.0", "--ccc", "A,B,C"])
466
+ expect(reference.get_options(true, "application_", "", :aaa, :bbb).symbolize_keys).to eq({application_aaa: 111, aaa: 456, bbb: "2.0"})
467
+ end
468
+
469
+ it "should apply higher precedence to command options in case of conflicts" do
470
+ Bovem::Parser.parse(reference.application, ["--aaa", "111", "--ddd", "2.0"])
471
+ Bovem::Parser.parse(reference, ["--bbb", "2.0", "--ccc", "A,B,C"])
472
+ expect(reference.get_options(true, "", "").symbolize_keys).to eq({ddd: 2.0, aaa: 456, bbb: "2.0", ccc: ["A", "B", "C"]})
473
+ end
474
+ end
475
+
476
+ describe "#show_help" do
477
+ it "should behave differently for application" do
478
+ allow(Kernel).to receive(:exit).and_return(0)
479
+
480
+ expect(application.console).to receive(:write).with("[NAME]")
481
+ expect(application.console).to receive(:write).at_least(1)
482
+ application.show_help
483
+ end
484
+
485
+ it "should print a banner" do
486
+ allow(Kernel).to receive(:exit).and_return(0)
487
+
488
+ command.banner = "BANNER"
489
+ expect(application.console).to receive(:write).with("[DESCRIPTION]")
490
+ expect(application.console).to receive(:write).at_least(1)
491
+ command.show_help
492
+ end
493
+
494
+ it "should print options" do
495
+ allow(Kernel).to receive(:exit).and_return(0)
496
+
497
+ application.option("global", [], {type: String})
498
+ command.option("local")
499
+
500
+ expect(application.console).to receive(:write).with("[GLOBAL OPTIONS]")
501
+ expect(application.console).to receive(:write).with("[OPTIONS]")
502
+ expect(application.console).to receive(:write).at_least(1)
503
+ application.show_help
504
+ command.show_help
505
+ end
506
+
507
+ it "should print subcommands" do
508
+ allow(Kernel).to receive(:exit).and_return(0)
509
+
510
+ command.command("subcommand")
511
+ expect(application.console).to receive(:write).with("[COMMANDS]")
512
+ expect(application.console).to receive(:write).with("[SUBCOMMANDS]")
513
+ expect(application.console).to receive(:write).at_least(1)
514
+ application.show_help
515
+ command.show_help
516
+ end
517
+
518
+ it "should exit" do
519
+ allow(Kernel).to receive(:puts)
520
+ allow(Bovem::Console.any_instance).to receive(:write)
521
+
522
+ expect(Kernel).to receive(:exit).with(0).exactly(1)
523
+ application.show_help
524
+ end
525
+ end
526
+ end