command_mapper 0.1.1 → 0.2.1

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.
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