hammer_cli 0.0.8 → 0.0.9
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.
- checksums.yaml +7 -0
- data/doc/developer_docs.md +361 -41
- data/lib/hammer_cli/abstract.rb +31 -0
- data/lib/hammer_cli/apipie/command.rb +4 -2
- data/lib/hammer_cli/apipie/options.rb +3 -10
- data/lib/hammer_cli/apipie/read_command.rb +0 -17
- data/lib/hammer_cli/apipie/resource.rb +1 -1
- data/lib/hammer_cli/apipie/write_command.rb +0 -1
- data/lib/hammer_cli/options/normalizers.rb +76 -0
- data/lib/hammer_cli/options/option_definition.rb +68 -0
- data/lib/hammer_cli/output/adapter/table.rb +2 -0
- data/lib/hammer_cli/version.rb +1 -1
- data/lib/hammer_cli.rb +1 -0
- metadata +45 -59
- data/lib/hammer_cli/option_formatters.rb +0 -13
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f94cb081ad874612f32639ed07cdf5626fb69ba4
|
4
|
+
data.tar.gz: 479cf176ac7bb522702489eb0205286ea0a98bf4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5a52be94f2ad2519e8eff5737b4f1fafbefb247db4fc49a6be931f0129f4a12fae8641e086ab4f0b9cca8bef691db8a5a6035c43e94afb7cb1c6ef0da7504403
|
7
|
+
data.tar.gz: b97b952d02e88eb7541ea041d61b32cfe5374200a1d59ba6d24a38f8f3e58289b598606a075863ba58501f038c61547ce0b669e42befa81ea1b074d914c2ceec
|
data/doc/developer_docs.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
Hammer
|
2
|
-
|
1
|
+
Hammer Development Docs
|
2
|
+
=======================
|
3
3
|
|
4
4
|
Hammer is a generic clamp-based CLI framework. It uses existing clamp features and adds some extra utilities.
|
5
5
|
We recommend to get familiar with the [Clamp documentation](https://github.com/mdub/clamp/#quick-start)
|
@@ -253,29 +253,50 @@ end
|
|
253
253
|
|
254
254
|
```
|
255
255
|
|
256
|
-
#### Option
|
257
|
-
Another option-related feature is a set of
|
256
|
+
#### Option normalizers
|
257
|
+
Another option-related feature is a set of normalizers for specific option types. They validate and preprocess
|
258
|
+
option values. Each normalizer has a description of the format it accepts. This description is printed
|
259
|
+
in commands' help.
|
258
260
|
|
259
|
-
|
261
|
+
##### _List_
|
260
262
|
|
261
263
|
Parses comma separated strings to a list of values.
|
262
264
|
|
263
|
-
Usage:
|
264
265
|
```ruby
|
265
|
-
option "--users", "USER_NAMES", "List of user names",
|
266
|
+
option "--users", "USER_NAMES", "List of user names",
|
267
|
+
:format => HammerCLI::Options::Normalizers::List.new
|
266
268
|
```
|
267
269
|
`--users='J.R.,Gary,Bobby'` -> `['J.R.', 'Gary', 'Bobby']`
|
268
270
|
|
269
|
-
|
271
|
+
##### _File_
|
270
272
|
|
271
273
|
Loads contents of a file and returns it as a value of the option.
|
272
274
|
|
273
|
-
Usage:
|
274
275
|
```ruby
|
275
|
-
option "--poem", "PATH_TO_POEM", "File containing the text of your poem",
|
276
|
+
option "--poem", "PATH_TO_POEM", "File containing the text of your poem",
|
277
|
+
:format => HammerCLI::Options::Normalizers::File.new
|
276
278
|
```
|
277
279
|
`--poem=~/verlaine/les_poetes_maudits.txt` -> content of the file
|
278
280
|
|
281
|
+
##### _Bool_
|
282
|
+
|
283
|
+
Case insensitive true/false values. Translates _yes,y,true,t,1_ to `true` and _no,n,false,f,0_ to `false`.
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
option "--start", "START", "Start the action",
|
287
|
+
:format => HammerCLI::Options::Normalizers::Bool.new
|
288
|
+
```
|
289
|
+
`--start=yes` -> `true`
|
290
|
+
|
291
|
+
##### _KeyValueList_
|
292
|
+
|
293
|
+
Parses a comma separated list of key=value pairs. Can be used for naming attributes with vague structure.
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
option "--attributes", "ATTRIBUTES", "Values of various attributes",
|
297
|
+
:format => HammerCLI::Options::Normalizers::KeyValueList.new
|
298
|
+
```
|
299
|
+
`--attributes="material=unoptanium,thickness=3"` -> `{'material' => 'unoptanium', 'thickness' => '3'}`
|
279
300
|
|
280
301
|
### Adding subcommands
|
281
302
|
Commands in the cli can be structured into a tree of parent commands (nodes) and subcommands (leaves).
|
@@ -441,35 +462,40 @@ Imagine there's an API of some service that returns list of users:
|
|
441
462
|
|
442
463
|
We can create an output definition that selects and formats some of the fields:
|
443
464
|
```ruby
|
444
|
-
|
445
|
-
dsl.build do
|
465
|
+
class Command < HammerCLI::AbstractCommand
|
446
466
|
|
447
|
-
|
448
|
-
|
467
|
+
output do
|
468
|
+
# Simple field with a label. The first parameter is key in the printed hash.
|
469
|
+
field :id, 'ID'
|
449
470
|
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
471
|
+
# Fields can have types. The type determines how the field is printed.
|
472
|
+
# All available types are listed below.
|
473
|
+
# Here we want the roles to act as list.
|
474
|
+
field :roles, 'System Roles', Fields::List
|
454
475
|
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
476
|
+
# Label is used for grouping fields.
|
477
|
+
label 'Contacts' do
|
478
|
+
field :email, 'Email'
|
479
|
+
field :phone, 'Phone No.'
|
480
|
+
end
|
460
481
|
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
482
|
+
# From is used for accessing nested fields.
|
483
|
+
from :timestamps do
|
484
|
+
# See how date gets formatted in the output
|
485
|
+
field :created, 'Created At', Fields::Date
|
486
|
+
end
|
465
487
|
end
|
466
|
-
end
|
467
488
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
489
|
+
def execute
|
490
|
+
records = retrieve_data
|
491
|
+
print_records( # <- printing utility of AbstractCommand
|
492
|
+
output_definition, # <- method for accessing fields defined in the block 'output'
|
493
|
+
records # <- the data to print
|
494
|
+
)
|
495
|
+
return HammerCLI::EX_OK
|
496
|
+
end
|
472
497
|
|
498
|
+
end
|
473
499
|
```
|
474
500
|
|
475
501
|
Using the base adapter the output will look like:
|
@@ -491,6 +517,21 @@ Contacts:
|
|
491
517
|
Created At: 2012/12/18 15:25:00
|
492
518
|
```
|
493
519
|
|
520
|
+
You can optionally use output definition from another command as a base and extend it with
|
521
|
+
additional fields. This is helpful when there are two commands, one listing brief data and
|
522
|
+
another one showing details. Typically it's list and show.
|
523
|
+
```ruby
|
524
|
+
class ShowCommand < HammerCLI::AbstractCommand
|
525
|
+
|
526
|
+
output ListCommand.output_definition do
|
527
|
+
# additional fields
|
528
|
+
end
|
529
|
+
|
530
|
+
# ...
|
531
|
+
end
|
532
|
+
```
|
533
|
+
|
534
|
+
|
494
535
|
All Hammer field types are:
|
495
536
|
* __Date__
|
496
537
|
* __Id__ - Used to mark ID values, current print adapters have support for turning id printing on/off.
|
@@ -593,21 +634,300 @@ HammerCLI::Settings.get(:hello_world, :name) # get a nested value
|
|
593
634
|
There's more ways where to place your config file for hammer.
|
594
635
|
Read more in [the settings howto](https://github.com/theforeman/hammer-cli#configuration).
|
595
636
|
|
637
|
+
|
596
638
|
Creating commands for RESTful API with ApiPie
|
597
639
|
---------------------------------------------
|
598
|
-
|
640
|
+
|
641
|
+
CLIs binded to a rest api do simillar things for most of the resources. Typically it's
|
642
|
+
CRUD actions that appear for nearly every resource. Actions differ with parameters
|
643
|
+
accross resources but the operations remain the same.
|
644
|
+
|
645
|
+
Hammer is optimised for usage with [ApiPie](https://github.com/Pajk/apipie-rails)
|
646
|
+
and generated api bindings and tries to reduce the effort neccessary for a command creation.
|
599
647
|
|
600
648
|
|
601
|
-
|
602
|
-
- this part is valid for foreman
|
603
|
-
- what is apipie
|
604
|
-
- apipie bindings
|
605
|
-
- apipie, read and write commands
|
606
|
-
- define ids a resources
|
607
|
-
- apipie support, apipie_options
|
608
|
-
-->
|
649
|
+
### ApiPie and bindings
|
609
650
|
|
651
|
+
[ApiPie](https://github.com/Pajk/apipie-rails) is a documentation library for RESTful APIs.
|
652
|
+
Unlike traditional tools ApiPie uses DSL for api description. This brings many advantages. See its
|
653
|
+
documentation for details.
|
654
|
+
|
655
|
+
Foreman comes with [ruby bindings](https://github.com/theforeman/foreman_api) automatically generated
|
656
|
+
from the information provided by ApiPie. Every resource (eg. Architecture, User) has it's own
|
657
|
+
class with methods for each available action (eg. create, show, index, destroy).
|
658
|
+
Apart from that it contains also full api documentation with parameters for the actions.
|
659
|
+
This enables to reuse the documentation on client side for automatic option definition
|
660
|
+
and reduce the amount of custom code per CLI action.
|
661
|
+
|
662
|
+
|
663
|
+
### ApiPie commands in Hammer
|
664
|
+
|
665
|
+
Hammer identifies two basic types of ApiPie commands:
|
666
|
+
|
667
|
+
- __ReadCommand__
|
668
|
+
- should be used for actions that print records
|
669
|
+
- retrieves the data and prints them in given format (uses output definition)
|
670
|
+
- typical actions in rails terminology: _index, show_
|
671
|
+
|
672
|
+
- __WriteCommand__
|
673
|
+
- should used for actions that modify records
|
674
|
+
- sends modifying request and prints the result
|
675
|
+
- typical actions in rails terminology: _create, update, destroy_
|
676
|
+
|
677
|
+
Both command classes are single resource related and expect the resource and an action to be defined.
|
678
|
+
There's a simple DSL for that:
|
679
|
+
|
680
|
+
```ruby
|
681
|
+
class ListCommand < HammerCLI::Apipie::ReadCommand
|
682
|
+
# define resource and the action together
|
683
|
+
resource ForemanApi::Resources::Architecture, :index
|
684
|
+
end
|
610
685
|
|
686
|
+
# or
|
687
|
+
|
688
|
+
class ListCommand2 < HammerCLI::Apipie::ReadCommand
|
689
|
+
# define them separately
|
690
|
+
resource ForemanApi::Resources::Architecture
|
691
|
+
action :index
|
692
|
+
end
|
693
|
+
```
|
694
|
+
|
695
|
+
#### Options definition
|
696
|
+
|
697
|
+
When the resource-action pair is defined we can take the advantage of automatic option definition.
|
698
|
+
There's a class method `apipie_options` for this purpose.
|
699
|
+
|
700
|
+
```ruby
|
701
|
+
class ListCommand < HammerCLI::Apipie::ReadCommand
|
702
|
+
resource ForemanApi::Resources::Architecture, :index
|
703
|
+
|
704
|
+
apipie_options
|
705
|
+
end
|
706
|
+
```
|
707
|
+
|
708
|
+
If we plug the command into an existing command tree and check the help we will see there
|
709
|
+
are four parameters defined from the ApiPie docs. Compare the result with
|
710
|
+
[online api documentation](http://www.theforeman.org/api/apidoc/architectures/index.html).
|
711
|
+
```
|
712
|
+
$ hammer architecture list -h
|
713
|
+
Usage:
|
714
|
+
hammer architecture list [OPTIONS]
|
715
|
+
|
716
|
+
Options:
|
717
|
+
--search SEARCH filter results
|
718
|
+
--order ORDER sort results
|
719
|
+
--page PAGE paginate results
|
720
|
+
--per-page PER_PAGE number of entries per request
|
721
|
+
-h, --help print help
|
722
|
+
```
|
723
|
+
|
724
|
+
It is possible to combine apipie options with custom ones. If the generated options
|
725
|
+
doesn't suit your needs for any reason, you can always skip and redefine them by hand.
|
726
|
+
See following example.
|
727
|
+
```ruby
|
728
|
+
class ListCommand < HammerCLI::Apipie::ReadCommand
|
729
|
+
resource ForemanApi::Resources::Architecture, :index
|
730
|
+
|
731
|
+
apipie_options :without => [:search, :order]
|
732
|
+
option '--search', 'QUERY', "search query"
|
733
|
+
end
|
734
|
+
```
|
735
|
+
|
736
|
+
```
|
737
|
+
hammer architecture list -h
|
738
|
+
Usage:
|
739
|
+
hammer architecture list [OPTIONS]
|
740
|
+
|
741
|
+
Options:
|
742
|
+
--page PAGE paginate results
|
743
|
+
--per-page PER_PAGE number of entries per request
|
744
|
+
--search QUERY search query
|
745
|
+
-h, --help print help
|
746
|
+
```
|
747
|
+
Note that the `--search` description has changed and `--order` disappeared.
|
748
|
+
|
749
|
+
Automatic options reflect:
|
750
|
+
- parameter names and descriptions
|
751
|
+
- required parameters
|
752
|
+
- parameter types - the only supported type is array, which is translated to option normalizer `List`
|
753
|
+
|
754
|
+
#### Write commands
|
755
|
+
|
756
|
+
Write commands are expected to print result of the api action. There are
|
757
|
+
two class methods for setting success and failure messages. Messages are
|
758
|
+
printed according to the http status code the api returned.
|
759
|
+
|
760
|
+
```ruby
|
761
|
+
success_message "The user has been created"
|
762
|
+
failure_message "Could not create the user"
|
763
|
+
```
|
764
|
+
|
765
|
+
|
766
|
+
#### Example 1: Create an architecture
|
767
|
+
|
768
|
+
```ruby
|
769
|
+
class CreateCommand < HammerCLI::Apipie::WriteCommand
|
770
|
+
command_name "create"
|
771
|
+
resource ForemanApi::Resources::Architecture, :create
|
611
772
|
|
773
|
+
success_message "Architecture created"
|
774
|
+
failure_message "Could not create the architecture"
|
612
775
|
|
776
|
+
apipie_options
|
777
|
+
end
|
778
|
+
```
|
779
|
+
|
780
|
+
```
|
781
|
+
$ hammer architecture create -h
|
782
|
+
Usage:
|
783
|
+
hammer architecture create [OPTIONS]
|
784
|
+
|
785
|
+
Options:
|
786
|
+
--name NAME
|
787
|
+
--operatingsystem-ids OPERATINGSYSTEM_IDS Operatingsystem ID’s
|
788
|
+
Comma separated list of values.
|
789
|
+
-h, --help print help
|
790
|
+
```
|
791
|
+
|
792
|
+
```
|
793
|
+
$ hammer architecture create
|
794
|
+
ERROR: option '--name' is required
|
795
|
+
|
796
|
+
See: 'hammer architecture create --help'
|
797
|
+
```
|
798
|
+
|
799
|
+
```
|
800
|
+
$ hammer architecture create --name test --operatingsystem-ids=1,2
|
801
|
+
Architecture created
|
802
|
+
```
|
803
|
+
|
804
|
+
```
|
805
|
+
$ hammer architecture create --name test
|
806
|
+
Could not create the architecture:
|
807
|
+
Name has already been taken
|
808
|
+
```
|
809
|
+
|
810
|
+
|
811
|
+
#### Example 2: Show an architecture
|
812
|
+
|
813
|
+
```ruby
|
814
|
+
class InfoCommand < HammerCLI::Apipie::ReadCommand
|
815
|
+
command_name "info"
|
816
|
+
resource ForemanApi::Resources::Architecture, :show
|
817
|
+
|
818
|
+
# It's a good practice to reuse output definition from list commands
|
819
|
+
# and add more details. It helps avoiding duplicities.
|
820
|
+
output ListCommand.output_definition do
|
821
|
+
from "architecture" do
|
822
|
+
field :operatingsystem_ids, "OS ids", Fields::List
|
823
|
+
field :created_at, "Created at", Fields::Date
|
824
|
+
field :updated_at, "Updated at", Fields::Date
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
apipie_options
|
829
|
+
end
|
830
|
+
```
|
831
|
+
|
832
|
+
```
|
833
|
+
$ hammer architecture info -h
|
834
|
+
Usage:
|
835
|
+
hammer architecture info [OPTIONS]
|
836
|
+
|
837
|
+
Options:
|
838
|
+
--id ID
|
839
|
+
-h, --help print help
|
840
|
+
```
|
841
|
+
|
842
|
+
```
|
843
|
+
$ hammer architecture info --id 1
|
844
|
+
Id: 1
|
845
|
+
Name: x86_64
|
846
|
+
OS ids: 1, 3
|
847
|
+
Created at: 2013/06/08 18:53:56
|
848
|
+
Updated at: 2013/06/08 19:17:43
|
849
|
+
```
|
850
|
+
|
851
|
+
|
852
|
+
#### Tips
|
853
|
+
|
854
|
+
When you define more command like we've shown above you find yourself repeating
|
855
|
+
`resource ...` in every one of them. As the commands are usually grouped by
|
856
|
+
the resource it is handy to extract the resource definition one level up to
|
857
|
+
the encapsulating command.
|
858
|
+
|
859
|
+
```ruby
|
860
|
+
class Architecture < HammerCLI::Apipie::Command
|
861
|
+
|
862
|
+
resource ForemanApi::Resources::Architecture
|
863
|
+
|
864
|
+
class ListCommand < HammerCLI::Apipie::ReadCommand
|
865
|
+
action :index
|
866
|
+
# ...
|
867
|
+
end
|
868
|
+
|
869
|
+
|
870
|
+
class InfoCommand < HammerCLI::Apipie::ReadCommand
|
871
|
+
action :show
|
872
|
+
# ...
|
873
|
+
end
|
874
|
+
|
875
|
+
# ...
|
876
|
+
end
|
877
|
+
```
|
878
|
+
|
879
|
+
ApiPie resources are being looked up in the encapsulating classes and modules
|
880
|
+
when the definition is missing in the command class. If they are not found even there
|
881
|
+
the resource of the parent command is used at runtime. This is useful for context-aware
|
882
|
+
shared commands.
|
883
|
+
|
884
|
+
The following example shows a common subcommand that can be attached to
|
885
|
+
any parent of which resource implements method `add_tag`. Please note that this example
|
886
|
+
is fictitious. There's no tags in Foreman's architectures and users.
|
887
|
+
```ruby
|
888
|
+
module Tags
|
889
|
+
class AddTag < HammerCLI::Apipie::WriteCommand
|
890
|
+
option '--id', 'ID', 'ID of the resource'
|
891
|
+
option '--tag', 'TAG', 'Name of the tag to add'
|
892
|
+
action :add_tag
|
893
|
+
command_name 'add_tag'
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
class Architecture < HammerCLI::Apipie::Command
|
898
|
+
resource ForemanApi::Resources::Architecture
|
899
|
+
# ...
|
900
|
+
include Tags
|
901
|
+
autoload_subcommands
|
902
|
+
end
|
903
|
+
|
904
|
+
class User < HammerCLI::Apipie::Command
|
905
|
+
resource ForemanApi::Resources::User
|
906
|
+
# ...
|
907
|
+
include Tags
|
908
|
+
autoload_subcommands
|
909
|
+
end
|
910
|
+
```
|
911
|
+
|
912
|
+
```
|
913
|
+
$ hammer architecture add_tag -h
|
914
|
+
Usage:
|
915
|
+
hammer architecture add_tag [OPTIONS]
|
916
|
+
|
917
|
+
Options:
|
918
|
+
--id ID ID of the resource
|
919
|
+
--tag TAG Name of the tag to add
|
920
|
+
-h, --help print help
|
921
|
+
```
|
922
|
+
|
923
|
+
```
|
924
|
+
$ hammer user add_tag -h
|
925
|
+
Usage:
|
926
|
+
hammer user add_tag [OPTIONS]
|
927
|
+
|
928
|
+
Options:
|
929
|
+
--id ID ID of the resource
|
930
|
+
--tag TAG Name of the tag to add
|
931
|
+
-h, --help print help
|
932
|
+
```
|
613
933
|
|
data/lib/hammer_cli/abstract.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'hammer_cli/autocompletion'
|
2
2
|
require 'hammer_cli/exception_handler'
|
3
3
|
require 'hammer_cli/logger_watch'
|
4
|
+
require 'hammer_cli/options/option_definition'
|
4
5
|
require 'clamp'
|
5
6
|
require 'logging'
|
6
7
|
|
@@ -85,6 +86,23 @@ module HammerCLI
|
|
85
86
|
super
|
86
87
|
end
|
87
88
|
|
89
|
+
def self.output(definition=nil, &block)
|
90
|
+
dsl = HammerCLI::Output::Dsl.new
|
91
|
+
dsl.build &block if block_given?
|
92
|
+
|
93
|
+
output_definition.append definition.fields unless definition.nil?
|
94
|
+
output_definition.append dsl.fields
|
95
|
+
end
|
96
|
+
|
97
|
+
def output_definition
|
98
|
+
self.class.output_definition
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.output_definition
|
102
|
+
@output_definition ||= HammerCLI::Output::Definition.new
|
103
|
+
@output_definition
|
104
|
+
end
|
105
|
+
|
88
106
|
protected
|
89
107
|
|
90
108
|
def print_records(definition, records)
|
@@ -139,6 +157,19 @@ module HammerCLI
|
|
139
157
|
end
|
140
158
|
end
|
141
159
|
|
160
|
+
def self.option(switches, type, description, opts = {}, &block)
|
161
|
+
formatter = opts.delete(:format)
|
162
|
+
|
163
|
+
HammerCLI::Options::OptionDefinition.new(switches, type, description, opts).tap do |option|
|
164
|
+
declared_options << option
|
165
|
+
|
166
|
+
option.value_formatter = formatter
|
167
|
+
block ||= option.default_conversion_block
|
168
|
+
|
169
|
+
define_accessors_for(option, &block)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
142
173
|
def all_options
|
143
174
|
self.class.recognised_options.inject({}) do |h, opt|
|
144
175
|
h[opt.attribute_name] = send(opt.read_method)
|
@@ -9,8 +9,8 @@ module HammerCLI::Apipie
|
|
9
9
|
include HammerCLI::Apipie::Options
|
10
10
|
|
11
11
|
def initialize(*args)
|
12
|
+
super
|
12
13
|
setup_identifier_options
|
13
|
-
super(*args)
|
14
14
|
end
|
15
15
|
|
16
16
|
def setup_identifier_options
|
@@ -32,7 +32,9 @@ module HammerCLI::Apipie
|
|
32
32
|
|
33
33
|
def validate_options
|
34
34
|
super
|
35
|
-
|
35
|
+
if self.class.declared_identifiers
|
36
|
+
validator.any(*self.class.declared_identifiers.values).required
|
37
|
+
end
|
36
38
|
end
|
37
39
|
|
38
40
|
def self.desc(desc=nil)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'hammer_cli/option_formatters'
|
2
1
|
|
3
2
|
module HammerCLI::Apipie
|
4
3
|
module Options
|
@@ -59,8 +58,7 @@ module HammerCLI::Apipie
|
|
59
58
|
option_switches(param),
|
60
59
|
option_type(param),
|
61
60
|
option_desc(param),
|
62
|
-
option_opts(param)
|
63
|
-
&option_formatter(param)
|
61
|
+
option_opts(param)
|
64
62
|
)
|
65
63
|
end
|
66
64
|
|
@@ -81,15 +79,10 @@ module HammerCLI::Apipie
|
|
81
79
|
def option_opts(param)
|
82
80
|
opts = {}
|
83
81
|
opts[:required] = true if param["required"]
|
84
|
-
return opts
|
85
|
-
end
|
86
|
-
|
87
|
-
def option_formatter(param)
|
88
82
|
# FIXME: There is a bug in apipie, it does not produce correct expected type for Arrays
|
89
83
|
# When it's fixed, we should test param["expected_type"] == "array"
|
90
|
-
if param["validator"].include? "Array"
|
91
|
-
|
92
|
-
end
|
84
|
+
opts[:format] = HammerCLI::Options::Normalizers::List.new if param["validator"].include? "Array"
|
85
|
+
return opts
|
93
86
|
end
|
94
87
|
|
95
88
|
end
|
@@ -4,23 +4,6 @@ module HammerCLI::Apipie
|
|
4
4
|
|
5
5
|
class ReadCommand < Command
|
6
6
|
|
7
|
-
def self.output(definition=nil, &block)
|
8
|
-
dsl = HammerCLI::Output::Dsl.new
|
9
|
-
dsl.build &block if block_given?
|
10
|
-
|
11
|
-
output_definition.append definition.fields unless definition.nil?
|
12
|
-
output_definition.append dsl.fields
|
13
|
-
end
|
14
|
-
|
15
|
-
def output_definition
|
16
|
-
self.class.output_definition
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.output_definition
|
20
|
-
@output_definition ||= HammerCLI::Output::Definition.new
|
21
|
-
@output_definition
|
22
|
-
end
|
23
|
-
|
24
7
|
def execute
|
25
8
|
d = retrieve_data
|
26
9
|
logger.watch "Retrieved data: ", d
|
@@ -16,7 +16,7 @@ module HammerCLI::Apipie
|
|
16
16
|
resource_class.doc["methods"].each do |method|
|
17
17
|
return method if method["name"] == method_name.to_s
|
18
18
|
end
|
19
|
-
raise "No method documentation found for #{resource_class}##{
|
19
|
+
raise "No method documentation found for #{resource_class}##{method_name}"
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module HammerCLI
|
2
|
+
module Options
|
3
|
+
module Normalizers
|
4
|
+
|
5
|
+
|
6
|
+
class AbstractNormalizer
|
7
|
+
def description
|
8
|
+
""
|
9
|
+
end
|
10
|
+
|
11
|
+
def format(val)
|
12
|
+
raise NotImplementedError, "Class #{self.class.name} must implement method format."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
class KeyValueList < AbstractNormalizer
|
18
|
+
|
19
|
+
def description
|
20
|
+
"Comma-separated list of key=value."
|
21
|
+
end
|
22
|
+
|
23
|
+
def format(val)
|
24
|
+
return {} unless val.is_a?(String)
|
25
|
+
|
26
|
+
val.split(",").inject({}) do |result, item|
|
27
|
+
parts = item.split("=")
|
28
|
+
if parts.size != 2
|
29
|
+
raise ArgumentError, "value must be defined as a comma-separated list of key=value"
|
30
|
+
end
|
31
|
+
result.update(parts[0] => parts[1])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
class List < AbstractNormalizer
|
38
|
+
|
39
|
+
def description
|
40
|
+
"Comma separated list of values."
|
41
|
+
end
|
42
|
+
|
43
|
+
def format(val)
|
44
|
+
val.is_a?(String) ? val.split(",") : []
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
class Bool < AbstractNormalizer
|
50
|
+
|
51
|
+
def description
|
52
|
+
"One of true/false, yes/no, 1/0."
|
53
|
+
end
|
54
|
+
|
55
|
+
def format(bool)
|
56
|
+
if bool.downcase.match(/^(true|t|yes|y|1)$/i)
|
57
|
+
return true
|
58
|
+
elsif bool.downcase.match(/^(false|f|no|n|0)$/i)
|
59
|
+
return false
|
60
|
+
else
|
61
|
+
raise ArgumentError, "value must be one of true/false, yes/no, 1/0"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
class File < AbstractNormalizer
|
68
|
+
|
69
|
+
def format(path)
|
70
|
+
::File.read(::File.expand_path(path))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'clamp'
|
2
|
+
|
3
|
+
module HammerCLI
|
4
|
+
module Options
|
5
|
+
|
6
|
+
class OptionDefinition < Clamp::Option::Definition
|
7
|
+
|
8
|
+
attr_accessor :value_formatter
|
9
|
+
|
10
|
+
def help_lhs
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def help_rhs
|
15
|
+
lines = [
|
16
|
+
description.strip,
|
17
|
+
format_description.strip,
|
18
|
+
value_description.strip
|
19
|
+
]
|
20
|
+
|
21
|
+
rhs = lines.reject(&:empty?).join("\n")
|
22
|
+
rhs.empty? ? " " : rhs
|
23
|
+
end
|
24
|
+
|
25
|
+
def format_description
|
26
|
+
if value_formatter.nil?
|
27
|
+
""
|
28
|
+
else
|
29
|
+
value_formatter.description
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def value_description
|
34
|
+
default_sources = [
|
35
|
+
("$#{@environment_variable}" if defined?(@environment_variable)),
|
36
|
+
(@default_value.inspect if defined?(@default_value))
|
37
|
+
].compact
|
38
|
+
|
39
|
+
str = ""
|
40
|
+
str += "Can be specified multiple times. " if multivalued?
|
41
|
+
str += "Default: " + default_sources.join(", or ") unless default_sources.empty?
|
42
|
+
str
|
43
|
+
end
|
44
|
+
|
45
|
+
def default_conversion_block
|
46
|
+
if !value_formatter.nil?
|
47
|
+
value_formatter.method(:format)
|
48
|
+
elsif flag?
|
49
|
+
Clamp.method(:truthy?)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def default_value
|
54
|
+
if defined?(@default_value)
|
55
|
+
if value_formatter
|
56
|
+
value_formatter.format(@default_value)
|
57
|
+
else
|
58
|
+
@default_value
|
59
|
+
end
|
60
|
+
elsif multivalued?
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/hammer_cli/version.rb
CHANGED
data/lib/hammer_cli.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hammer_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.9
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Martin Bačovský
|
@@ -10,107 +9,94 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date: 2013-
|
12
|
+
date: 2013-11-08 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: clamp
|
17
16
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
17
|
requirements:
|
20
|
-
- -
|
18
|
+
- - '>='
|
21
19
|
- !ruby/object:Gem::Version
|
22
20
|
version: '0'
|
23
21
|
type: :runtime
|
24
22
|
prerelease: false
|
25
23
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
24
|
requirements:
|
28
|
-
- -
|
25
|
+
- - '>='
|
29
26
|
- !ruby/object:Gem::Version
|
30
27
|
version: '0'
|
31
28
|
- !ruby/object:Gem::Dependency
|
32
29
|
name: rest-client
|
33
30
|
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
31
|
requirements:
|
36
|
-
- -
|
32
|
+
- - '>='
|
37
33
|
- !ruby/object:Gem::Version
|
38
34
|
version: '0'
|
39
35
|
type: :runtime
|
40
36
|
prerelease: false
|
41
37
|
version_requirements: !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
38
|
requirements:
|
44
|
-
- -
|
39
|
+
- - '>='
|
45
40
|
- !ruby/object:Gem::Version
|
46
41
|
version: '0'
|
47
42
|
- !ruby/object:Gem::Dependency
|
48
43
|
name: logging
|
49
44
|
requirement: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
45
|
requirements:
|
52
|
-
- -
|
46
|
+
- - '>='
|
53
47
|
- !ruby/object:Gem::Version
|
54
48
|
version: '0'
|
55
49
|
type: :runtime
|
56
50
|
prerelease: false
|
57
51
|
version_requirements: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
52
|
requirements:
|
60
|
-
- -
|
53
|
+
- - '>='
|
61
54
|
- !ruby/object:Gem::Version
|
62
55
|
version: '0'
|
63
56
|
- !ruby/object:Gem::Dependency
|
64
57
|
name: awesome_print
|
65
58
|
requirement: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
59
|
requirements:
|
68
|
-
- -
|
60
|
+
- - '>='
|
69
61
|
- !ruby/object:Gem::Version
|
70
62
|
version: '0'
|
71
63
|
type: :runtime
|
72
64
|
prerelease: false
|
73
65
|
version_requirements: !ruby/object:Gem::Requirement
|
74
|
-
none: false
|
75
66
|
requirements:
|
76
|
-
- -
|
67
|
+
- - '>='
|
77
68
|
- !ruby/object:Gem::Version
|
78
69
|
version: '0'
|
79
70
|
- !ruby/object:Gem::Dependency
|
80
71
|
name: table_print
|
81
72
|
requirement: !ruby/object:Gem::Requirement
|
82
|
-
none: false
|
83
73
|
requirements:
|
84
|
-
- -
|
74
|
+
- - '>='
|
85
75
|
- !ruby/object:Gem::Version
|
86
76
|
version: '0'
|
87
77
|
type: :runtime
|
88
78
|
prerelease: false
|
89
79
|
version_requirements: !ruby/object:Gem::Requirement
|
90
|
-
none: false
|
91
80
|
requirements:
|
92
|
-
- -
|
81
|
+
- - '>='
|
93
82
|
- !ruby/object:Gem::Version
|
94
83
|
version: '0'
|
95
84
|
- !ruby/object:Gem::Dependency
|
96
85
|
name: highline
|
97
86
|
requirement: !ruby/object:Gem::Requirement
|
98
|
-
none: false
|
99
87
|
requirements:
|
100
|
-
- -
|
88
|
+
- - '>='
|
101
89
|
- !ruby/object:Gem::Version
|
102
90
|
version: '0'
|
103
91
|
type: :runtime
|
104
92
|
prerelease: false
|
105
93
|
version_requirements: !ruby/object:Gem::Requirement
|
106
|
-
none: false
|
107
94
|
requirements:
|
108
|
-
- -
|
95
|
+
- - '>='
|
109
96
|
- !ruby/object:Gem::Version
|
110
97
|
version: '0'
|
111
|
-
description:
|
112
|
-
|
113
|
-
'
|
98
|
+
description: |
|
99
|
+
Hammer cli provides universal extendable CLI interface for ruby apps
|
114
100
|
email: mbacovsk@redhat.com
|
115
101
|
executables:
|
116
102
|
- hammer
|
@@ -120,74 +106,74 @@ extra_rdoc_files:
|
|
120
106
|
- LICENSE
|
121
107
|
- hammer_cli_complete
|
122
108
|
- config/cli_config.template.yml
|
109
|
+
- doc/developer_docs.md
|
123
110
|
- doc/design.png
|
124
111
|
- doc/design.uml
|
125
|
-
- doc/developer_docs.md
|
126
112
|
files:
|
127
|
-
- lib/hammer_cli.rb
|
113
|
+
- lib/hammer_cli/logger_watch.rb
|
114
|
+
- lib/hammer_cli/apipie/write_command.rb
|
115
|
+
- lib/hammer_cli/apipie/resource.rb
|
116
|
+
- lib/hammer_cli/apipie/read_command.rb
|
117
|
+
- lib/hammer_cli/apipie/options.rb
|
118
|
+
- lib/hammer_cli/apipie/command.rb
|
119
|
+
- lib/hammer_cli/logger.rb
|
128
120
|
- lib/hammer_cli/messages.rb
|
129
|
-
- lib/hammer_cli/
|
130
|
-
- lib/hammer_cli/
|
121
|
+
- lib/hammer_cli/abstract.rb
|
122
|
+
- lib/hammer_cli/apipie.rb
|
123
|
+
- lib/hammer_cli/version.rb
|
124
|
+
- lib/hammer_cli/exception_handler.rb
|
125
|
+
- lib/hammer_cli/output.rb
|
126
|
+
- lib/hammer_cli/autocompletion.rb
|
127
|
+
- lib/hammer_cli/shell.rb
|
131
128
|
- lib/hammer_cli/output/adapter/table.rb
|
132
129
|
- lib/hammer_cli/output/adapter/base.rb
|
133
130
|
- lib/hammer_cli/output/adapter/abstract.rb
|
134
131
|
- lib/hammer_cli/output/adapter/silent.rb
|
135
132
|
- lib/hammer_cli/output/adapter/csv.rb
|
133
|
+
- lib/hammer_cli/output/definition.rb
|
136
134
|
- lib/hammer_cli/output/adapter.rb
|
137
|
-
- lib/hammer_cli/output/output.rb
|
138
135
|
- lib/hammer_cli/output/dsl.rb
|
139
|
-
- lib/hammer_cli/output/
|
140
|
-
- lib/hammer_cli/
|
141
|
-
- lib/hammer_cli/
|
142
|
-
- lib/hammer_cli/
|
143
|
-
- lib/hammer_cli/
|
144
|
-
- lib/hammer_cli/
|
145
|
-
- lib/hammer_cli/apipie.rb
|
146
|
-
- lib/hammer_cli/apipie/command.rb
|
147
|
-
- lib/hammer_cli/apipie/options.rb
|
148
|
-
- lib/hammer_cli/apipie/resource.rb
|
149
|
-
- lib/hammer_cli/apipie/write_command.rb
|
150
|
-
- lib/hammer_cli/apipie/read_command.rb
|
136
|
+
- lib/hammer_cli/output/formatters.rb
|
137
|
+
- lib/hammer_cli/output/output.rb
|
138
|
+
- lib/hammer_cli/output/fields.rb
|
139
|
+
- lib/hammer_cli/options/option_definition.rb
|
140
|
+
- lib/hammer_cli/options/normalizers.rb
|
141
|
+
- lib/hammer_cli/settings.rb
|
151
142
|
- lib/hammer_cli/validator.rb
|
152
|
-
- lib/hammer_cli/version.rb
|
153
143
|
- lib/hammer_cli/exit_codes.rb
|
154
|
-
- lib/hammer_cli/output.rb
|
155
|
-
- lib/hammer_cli/exception_handler.rb
|
156
144
|
- lib/hammer_cli/main.rb
|
157
|
-
- lib/hammer_cli
|
158
|
-
- lib/hammer_cli/settings.rb
|
145
|
+
- lib/hammer_cli.rb
|
159
146
|
- bin/hammer
|
160
147
|
- README.md
|
161
148
|
- LICENSE
|
162
149
|
- hammer_cli_complete
|
163
150
|
- config/cli_config.template.yml
|
151
|
+
- doc/developer_docs.md
|
164
152
|
- doc/design.png
|
165
153
|
- doc/design.uml
|
166
|
-
- doc/developer_docs.md
|
167
154
|
homepage: http://github.com/theforeman/hammer-cli
|
168
155
|
licenses:
|
169
156
|
- GPL-3
|
157
|
+
metadata: {}
|
170
158
|
post_install_message:
|
171
159
|
rdoc_options: []
|
172
160
|
require_paths:
|
173
161
|
- lib
|
174
162
|
required_ruby_version: !ruby/object:Gem::Requirement
|
175
|
-
none: false
|
176
163
|
requirements:
|
177
|
-
- -
|
164
|
+
- - '>='
|
178
165
|
- !ruby/object:Gem::Version
|
179
166
|
version: '0'
|
180
167
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
-
none: false
|
182
168
|
requirements:
|
183
|
-
- -
|
169
|
+
- - '>='
|
184
170
|
- !ruby/object:Gem::Version
|
185
171
|
version: '0'
|
186
172
|
requirements: []
|
187
173
|
rubyforge_project:
|
188
|
-
rubygems_version:
|
174
|
+
rubygems_version: 2.0.8
|
189
175
|
signing_key:
|
190
|
-
specification_version:
|
176
|
+
specification_version: 4
|
191
177
|
summary: Universal command-line interface
|
192
178
|
test_files: []
|
193
179
|
has_rdoc:
|