bovem 2.4.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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