hammer_cli 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|