command_mapper 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/spec/commnad_spec.rb CHANGED
@@ -7,6 +7,9 @@ describe CommandMapper::Command do
7
7
  command 'foo'
8
8
  end
9
9
 
10
+ class InheritedCommandName < WithCommandName
11
+ end
12
+
10
13
  class NoCommandName < CommandMapper::Command
11
14
  end
12
15
  end
@@ -51,6 +54,14 @@ describe CommandMapper::Command do
51
54
  expect(subject.command_name).to eq('foo')
52
55
  end
53
56
  end
57
+
58
+ context "when the command class inherits from another command class" do
59
+ let(:command_class) { TestCommand::InheritedCommandName }
60
+
61
+ it "must check the superclass" do
62
+ expect(subject.command_name).to eq("foo")
63
+ end
64
+ end
54
65
  end
55
66
 
56
67
  module TestCommand
@@ -58,6 +69,20 @@ describe CommandMapper::Command do
58
69
  end
59
70
  end
60
71
 
72
+ module TestCommand
73
+ class BaseClassWithOptions < CommandMapper::Command
74
+ option "--foo"
75
+ option "--bar"
76
+ end
77
+
78
+ class InheritedOptions < BaseClassWithOptions
79
+ end
80
+
81
+ class InheritsAndDefinesOptions < BaseClassWithOptions
82
+ option "--baz"
83
+ end
84
+ end
85
+
61
86
  describe ".options" do
62
87
  subject { command_class }
63
88
 
@@ -68,16 +93,6 @@ describe CommandMapper::Command do
68
93
  end
69
94
 
70
95
  context "and when the command inherits from another command class" do
71
- module TestCommand
72
- class BaseClassWithOptions < CommandMapper::Command
73
- option "--foo"
74
- option "--bar"
75
- end
76
-
77
- class InheritedOptions < BaseClassWithOptions
78
- end
79
- end
80
-
81
96
  let(:command_class) { TestCommand::InheritedOptions }
82
97
  let(:command_superclass) { TestCommand::BaseClassWithOptions }
83
98
 
@@ -86,12 +101,6 @@ describe CommandMapper::Command do
86
101
  end
87
102
 
88
103
  context "and when the class defines options of it's own" do
89
- module TestCommand
90
- class InheritsAndDefinesOptions < BaseClassWithOptions
91
- option "--baz"
92
- end
93
- end
94
-
95
104
  let(:command_class) { TestCommand::InheritsAndDefinesOptions }
96
105
 
97
106
  it "must copy the options defined in the superclass" do
@@ -109,6 +118,38 @@ describe CommandMapper::Command do
109
118
  end
110
119
  end
111
120
 
121
+ describe ".has_option?" do
122
+ subject { command_class }
123
+
124
+ let(:name) { :bar }
125
+
126
+ context "when the command has no defined options" do
127
+ let(:command_class) { TestCommand::EmptyCommand }
128
+
129
+ it "must return false" do
130
+ expect(subject.has_option?(name)).to be(false)
131
+ end
132
+ end
133
+
134
+ context "when the command does have defined options" do
135
+ let(:command_class) { TestCommand::BaseClassWithOptions }
136
+
137
+ context "and has the option with the given name" do
138
+ it "must return true" do
139
+ expect(subject.has_option?(name)).to be(true)
140
+ end
141
+ end
142
+
143
+ context "but does not have the option with the given name" do
144
+ let(:name) { :xxx }
145
+
146
+ it "must return false" do
147
+ expect(subject.has_option?(name)).to be(false)
148
+ end
149
+ end
150
+ end
151
+ end
152
+
112
153
  describe ".option" do
113
154
  module TestCommand
114
155
  class DefinesItsOwnOptions < CommandMapper::Command
@@ -231,6 +272,55 @@ describe CommandMapper::Command do
231
272
  }.to raise_error(ArgumentError,"option #{flag.inspect} maps to method name ##{name} and cannot override the internal method with same name: ##{name}")
232
273
  end
233
274
  end
275
+
276
+ context "when the option name conflicts with another defined argument" do
277
+ let(:command_class) do
278
+ Class.new(described_class) do
279
+ argument :foo
280
+ end
281
+ end
282
+
283
+ let(:flag) { "--foo" }
284
+ let(:name) { :foo }
285
+
286
+ it do
287
+ expect {
288
+ subject.option(flag)
289
+ }.to raise_error(ArgumentError,"option #{flag.inspect} with name #{name.inspect} conflicts with another argument with the same name")
290
+ end
291
+ end
292
+
293
+ context "when the option name conflicts with another defined subcommand" do
294
+ let(:command_class) do
295
+ Class.new(described_class) do
296
+ subcommand 'foo' do
297
+ end
298
+ end
299
+ end
300
+
301
+ let(:flag) { "--foo" }
302
+ let(:name) { :foo }
303
+
304
+ it do
305
+ expect {
306
+ subject.option(flag)
307
+ }.to raise_error(ArgumentError,"option #{flag.inspect} with name #{name.inspect} conflicts with another subcommand with the same name")
308
+ end
309
+ end
310
+ end
311
+
312
+ module TestCommand
313
+ class BaseClassWithArguments < CommandMapper::Command
314
+ argument :foo
315
+ argument :bar
316
+ end
317
+
318
+ class InheritedArguments < BaseClassWithArguments
319
+ end
320
+
321
+ class InheritsAndDefinesArguments < BaseClassWithArguments
322
+ argument :baz
323
+ end
234
324
  end
235
325
 
236
326
  describe ".arguments" do
@@ -243,31 +333,15 @@ describe CommandMapper::Command do
243
333
  end
244
334
 
245
335
  context "when the comand does have defined arguments" do
246
- module TestCommand
247
- class BaseClassWithOptions < CommandMapper::Command
248
- argument :foo
249
- argument :bar
250
- end
251
-
252
- class InheritedOptions < BaseClassWithOptions
253
- end
254
- end
255
-
256
- let(:command_class) { TestCommand::InheritedOptions }
257
- let(:command_superclass) { TestCommand::BaseClassWithOptions }
336
+ let(:command_class) { TestCommand::InheritedArguments }
337
+ let(:command_superclass) { TestCommand::BaseClassWithArguments }
258
338
 
259
339
  it "must copy the arguments defined in the superclass" do
260
340
  expect(subject.arguments).to eq(command_superclass.arguments)
261
341
  end
262
342
 
263
343
  context "and when the class defines arguments of it's own" do
264
- module TestCommand
265
- class InheritsAndDefinesOptions < BaseClassWithOptions
266
- argument :baz
267
- end
268
- end
269
-
270
- let(:command_class) { TestCommand::InheritsAndDefinesOptions }
344
+ let(:command_class) { TestCommand::InheritsAndDefinesArguments }
271
345
 
272
346
  it "must copy the arguments defined in the superclass" do
273
347
  expect(subject.arguments).to include(command_superclass.arguments)
@@ -284,6 +358,38 @@ describe CommandMapper::Command do
284
358
  end
285
359
  end
286
360
 
361
+ describe ".has_argument?" do
362
+ subject { command_class }
363
+
364
+ let(:name) { :bar }
365
+
366
+ context "when the command has no defined arguments" do
367
+ let(:command_class) { TestCommand::EmptyCommand }
368
+
369
+ it "must return false" do
370
+ expect(subject.has_argument?(name)).to be(false)
371
+ end
372
+ end
373
+
374
+ context "when the command does have defined arguments" do
375
+ let(:command_class) { TestCommand::BaseClassWithArguments }
376
+
377
+ context "and has the argument with the given name" do
378
+ it "must return true" do
379
+ expect(subject.has_argument?(name)).to be(true)
380
+ end
381
+ end
382
+
383
+ context "but does not have the argument with the given name" do
384
+ let(:name) { :xxx }
385
+
386
+ it "must return false" do
387
+ expect(subject.has_argument?(name)).to be(false)
388
+ end
389
+ end
390
+ end
391
+ end
392
+
287
393
  describe ".argument" do
288
394
  module TestCommand
289
395
  class DefinesArgument < CommandMapper::Command
@@ -346,6 +452,59 @@ describe CommandMapper::Command do
346
452
  }.to raise_error(ArgumentError,"argument #{name.inspect} cannot override internal method with same name: ##{name}")
347
453
  end
348
454
  end
455
+
456
+ context "when the argument name conflicts with another defined option" do
457
+ let(:command_class) do
458
+ Class.new(described_class) do
459
+ option '--foo'
460
+ end
461
+ end
462
+
463
+ let(:name) { :foo }
464
+
465
+ it do
466
+ expect {
467
+ subject.argument(name)
468
+ }.to raise_error(ArgumentError,"argument #{name.inspect} conflicts with another option with the same name")
469
+ end
470
+ end
471
+
472
+ context "when the argument name conflicts with another defined subcommand" do
473
+ let(:command_class) do
474
+ Class.new(described_class) do
475
+ subcommand 'foo' do
476
+ end
477
+ end
478
+ end
479
+
480
+ let(:name) { :foo }
481
+
482
+ it do
483
+ expect {
484
+ subject.argument(name)
485
+ }.to raise_error(ArgumentError,"argument #{name.inspect} conflicts with another subcommand with the same name")
486
+ end
487
+ end
488
+ end
489
+
490
+ module TestCommand
491
+ class BaseClassWithSubcommands < CommandMapper::Command
492
+ subcommand :foo do
493
+ end
494
+
495
+ subcommand :bar do
496
+ end
497
+ end
498
+
499
+ class InheritedSubcommands < BaseClassWithSubcommands
500
+ end
501
+
502
+ class InheritsAndDefinesSubcommands < BaseClassWithSubcommands
503
+
504
+ subcommand :baz do
505
+ end
506
+
507
+ end
349
508
  end
350
509
 
351
510
  describe ".subcommands" do
@@ -358,37 +517,15 @@ describe CommandMapper::Command do
358
517
  end
359
518
 
360
519
  context "when the comand does have defined subcommands" do
361
- module TestCommand
362
- class BaseClassWithOptions < CommandMapper::Command
363
- subcommand :foo do
364
- end
365
-
366
- subcommand :bar do
367
- end
368
- end
369
-
370
- class InheritedOptions < BaseClassWithOptions
371
- end
372
- end
373
-
374
- let(:command_class) { TestCommand::InheritedOptions }
375
- let(:command_superclass) { TestCommand::BaseClassWithOptions }
520
+ let(:command_class) { TestCommand::InheritedSubcommands }
521
+ let(:command_superclass) { TestCommand::BaseClassWithSubcommands }
376
522
 
377
523
  it "must copy the subcommands defined in the superclass" do
378
524
  expect(subject.subcommands).to eq(command_superclass.subcommands)
379
525
  end
380
526
 
381
527
  context "and when the class defines subcommands of it's own" do
382
- module TestCommand
383
- class InheritsAndDefinesOptions < BaseClassWithOptions
384
-
385
- subcommand :baz do
386
- end
387
-
388
- end
389
- end
390
-
391
- let(:command_class) { TestCommand::InheritsAndDefinesOptions }
528
+ let(:command_class) { TestCommand::InheritsAndDefinesSubcommands }
392
529
 
393
530
  it "must copy the subcommands defined in the superclass" do
394
531
  expect(subject.subcommands).to include(command_superclass.subcommands)
@@ -405,6 +542,38 @@ describe CommandMapper::Command do
405
542
  end
406
543
  end
407
544
 
545
+ describe ".has_subcommand?" do
546
+ subject { command_class }
547
+
548
+ let(:name) { :bar }
549
+
550
+ context "when the command has no defined subcommands" do
551
+ let(:command_class) { TestCommand::EmptyCommand }
552
+
553
+ it "must return false" do
554
+ expect(subject.has_subcommand?(name)).to be(false)
555
+ end
556
+ end
557
+
558
+ context "when the command does have defined subcommands" do
559
+ let(:command_class) { TestCommand::BaseClassWithSubcommands }
560
+
561
+ context "and has the subcommand with the given name" do
562
+ it "must return true" do
563
+ expect(subject.has_subcommand?(name)).to be(true)
564
+ end
565
+ end
566
+
567
+ context "but does not have the subcommand with the given name" do
568
+ let(:name) { :xxx }
569
+
570
+ it "must return false" do
571
+ expect(subject.has_subcommand?(name)).to be(false)
572
+ end
573
+ end
574
+ end
575
+ end
576
+
408
577
  describe ".subcommand" do
409
578
  module TestCommand
410
579
  class DefinesSubcommand < CommandMapper::Command
@@ -556,11 +725,45 @@ describe CommandMapper::Command do
556
725
 
557
726
  it do
558
727
  expect {
559
- command_class.subcommand(name) do
728
+ subject.subcommand(name) do
560
729
  end
561
730
  }.to raise_error(ArgumentError,"subcommand #{name.inspect} maps to method name ##{method_name} and cannot override the internal method with same name: ##{method_name}")
562
731
  end
563
732
  end
733
+
734
+ context "when the subcommand name conflicts with another defined option" do
735
+ let(:command_class) do
736
+ Class.new(described_class) do
737
+ option '--foo'
738
+ end
739
+ end
740
+
741
+ let(:name) { 'foo' }
742
+
743
+ it do
744
+ expect {
745
+ subject.subcommand(name) do
746
+ end
747
+ }.to raise_error(ArgumentError,"subcommand #{name.inspect} conflicts with another option with the same name")
748
+ end
749
+ end
750
+
751
+ context "when the subcommand name conflicts with another defined argument" do
752
+ let(:command_class) do
753
+ Class.new(described_class) do
754
+ argument :foo
755
+ end
756
+ end
757
+
758
+ let(:name) { 'foo' }
759
+
760
+ it do
761
+ expect {
762
+ subject.subcommand(name) do
763
+ end
764
+ }.to raise_error(ArgumentError,"subcommand #{name.inspect} conflicts with another argument with the same name")
765
+ end
766
+ end
564
767
  end
565
768
 
566
769
  module TestCommand
@@ -604,8 +807,8 @@ describe CommandMapper::Command do
604
807
  {opt1: opt1, arg1: arg1}
605
808
  end
606
809
 
607
- it "must initialize a new command with the Hash of params and call #run" do
608
- if RUBY_VERSION < '3.'
810
+ it "must initialize a new command with the Hash of params and call #run_command" do
811
+ if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
609
812
  expect(subject).to receive(:new).with({},params).and_return(command_instance)
610
813
  else
611
814
  expect(subject).to receive(:new).with(params).and_return(command_instance)
@@ -622,7 +825,7 @@ describe CommandMapper::Command do
622
825
  {opt1: opt1, arg1: arg1}
623
826
  end
624
827
 
625
- it "must initialize a new command with the keyword arguments and call #run" do
828
+ it "must initialize a new command with the keyword arguments and call #run_command" do
626
829
  expect(subject).to receive(:new).with({},**kwargs).and_return(command_instance)
627
830
  expect(command_instance).to receive(:run_command).and_return(return_value)
628
831
 
@@ -631,6 +834,44 @@ describe CommandMapper::Command do
631
834
  end
632
835
  end
633
836
 
837
+ describe ".spawn" do
838
+ let(:command_instance) { double(:command_instance) }
839
+ let(:return_value) { double(:boolean) }
840
+
841
+ subject { command_class }
842
+
843
+ context "when called with a Hash of params" do
844
+ let(:params) do
845
+ {opt1: opt1, arg1: arg1}
846
+ end
847
+
848
+ it "must initialize a new command with the Hash of params and call #spawn_command" do
849
+ if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
850
+ expect(subject).to receive(:new).with({},params).and_return(command_instance)
851
+ else
852
+ expect(subject).to receive(:new).with(params).and_return(command_instance)
853
+ end
854
+
855
+ expect(command_instance).to receive(:spawn_command).and_return(return_value)
856
+
857
+ expect(subject.spawn(params)).to be(return_value)
858
+ end
859
+ end
860
+
861
+ context "when called with keyword aguments" do
862
+ let(:kwargs) do
863
+ {opt1: opt1, arg1: arg1}
864
+ end
865
+
866
+ it "must initialize a new command with the keyword arguments and call #spawn_command" do
867
+ expect(subject).to receive(:new).with({},**kwargs).and_return(command_instance)
868
+ expect(command_instance).to receive(:spawn_command).and_return(return_value)
869
+
870
+ expect(subject.spawn(**kwargs)).to be(return_value)
871
+ end
872
+ end
873
+ end
874
+
634
875
  describe ".capture" do
635
876
  let(:command_instance) { double(:command_instance) }
636
877
  let(:return_value) { double(:string) }
@@ -642,8 +883,8 @@ describe CommandMapper::Command do
642
883
  {opt1: opt1, arg1: arg1}
643
884
  end
644
885
 
645
- it "must initialize a new command with the Hash of params and call #capture" do
646
- if RUBY_VERSION < '3.'
886
+ it "must initialize a new command with the Hash of params and call #capture_command" do
887
+ if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
647
888
  expect(subject).to receive(:new).with({},params).and_return(command_instance)
648
889
  else
649
890
  expect(subject).to receive(:new).with(params).and_return(command_instance)
@@ -660,7 +901,7 @@ describe CommandMapper::Command do
660
901
  {opt1: opt1, arg1: arg1}
661
902
  end
662
903
 
663
- it "must initialize a new command with the keyword arguments and call #capture" do
904
+ it "must initialize a new command with the keyword arguments and call #capture_command" do
664
905
  expect(subject).to receive(:new).with({},**kwargs).and_return(command_instance)
665
906
  expect(command_instance).to receive(:capture_command).and_return(return_value)
666
907
 
@@ -680,8 +921,8 @@ describe CommandMapper::Command do
680
921
  {opt1: opt1, arg1: arg1}
681
922
  end
682
923
 
683
- it "must initialize a new command with the Hash of params and call #popen" do
684
- if RUBY_VERSION < '3.'
924
+ it "must initialize a new command with the Hash of params and call #popen_command" do
925
+ if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
685
926
  expect(subject).to receive(:new).with({},params).and_return(command_instance)
686
927
  else
687
928
  expect(subject).to receive(:new).with(params).and_return(command_instance)
@@ -698,7 +939,7 @@ describe CommandMapper::Command do
698
939
  {opt1: opt1, arg1: arg1}
699
940
  end
700
941
 
701
- it "must initialize a new command with the keyword arguments and call #popen" do
942
+ it "must initialize a new command with the keyword arguments and call #popen_command" do
702
943
  expect(subject).to receive(:new).with({},**kwargs).and_return(command_instance)
703
944
  expect(command_instance).to receive(:popen_command).and_return(return_value)
704
945
 
@@ -718,8 +959,8 @@ describe CommandMapper::Command do
718
959
  {opt1: opt1, arg1: arg1}
719
960
  end
720
961
 
721
- it "must initialize a new command with the Hash of params and call #sudo" do
722
- if RUBY_VERSION < '3.'
962
+ it "must initialize a new command with the Hash of params and call #sudo_command" do
963
+ if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
723
964
  expect(subject).to receive(:new).with({},params).and_return(command_instance)
724
965
  else
725
966
  expect(subject).to receive(:new).with(params).and_return(command_instance)
@@ -736,7 +977,7 @@ describe CommandMapper::Command do
736
977
  {opt1: opt1, arg1: arg1}
737
978
  end
738
979
 
739
- it "must initialize a new command with the keyword arguments and call #sudo" do
980
+ it "must initialize a new command with the keyword arguments and call #sudo_command" do
740
981
  expect(subject).to receive(:new).with({},**kwargs).and_return(command_instance)
741
982
  expect(command_instance).to receive(:sudo_command).and_return(return_value)
742
983
 
@@ -922,6 +1163,26 @@ describe CommandMapper::Command do
922
1163
  }.to raise_error(ArgumentRequired,"argument arg2 is required")
923
1164
  end
924
1165
  end
1166
+
1167
+ context "but the command has un-required arguments that repeat" do
1168
+ module TestCommand
1169
+ class CommandWithUnRequiredRepeatingArguments < CommandMapper::Command
1170
+ command "test" do
1171
+ argument :arg1, required: false
1172
+ argument :arg2, required: false, repeats: true
1173
+ argument :arg3, required: false
1174
+ end
1175
+ end
1176
+ end
1177
+
1178
+ let(:command_class) { TestCommand::CommandWithUnRequiredRepeatingArguments }
1179
+
1180
+ subject { command_class.new(arg1: nil, arg2: nil, arg3: nil) }
1181
+
1182
+ it "must omit the un-required repeating arguments that are not set" do
1183
+ expect(subject.command_argv).to eq([subject.class.command_name])
1184
+ end
1185
+ end
925
1186
  end
926
1187
 
927
1188
  context "when the command is initialized with the command_path: keyword" do
@@ -1109,12 +1370,22 @@ describe CommandMapper::Command do
1109
1370
  subject { command_class.new({opt1: opt1, arg1: arg1}, command_env: env) }
1110
1371
 
1111
1372
  it "must pass the command's env and argv to Kenrel.system" do
1112
- expect(subject).to receive(:system).with(env,*subject.command_argv)
1373
+ expect(Kernel).to receive(:system).with(env,*subject.command_argv)
1113
1374
 
1114
1375
  subject.run_command
1115
1376
  end
1116
1377
  end
1117
1378
 
1379
+ describe "#spawn_command" do
1380
+ subject { command_class.new({opt1: opt1, arg1: arg1}, command_env: env) }
1381
+
1382
+ it "must pass the command's env and argv to Kenrel.system" do
1383
+ expect(Process).to receive(:spawn).with(env,*subject.command_argv)
1384
+
1385
+ subject.spawn_command
1386
+ end
1387
+ end
1388
+
1118
1389
  describe "#capture_command" do
1119
1390
  subject { command_class.new({opt1: opt1, arg1: arg1}, command_env: env) }
1120
1391