hammer_cli 0.0.7 → 0.0.8

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/README.md CHANGED
@@ -1,129 +1,340 @@
1
- Hammer - the CLI tool for Foreman
2
- =================================
1
+ Hammer - the CLI tool (not only) for Foreman
2
+ ============================================
3
3
 
4
- Hammer is a generic [clamp-based](https://github.com/mdub/clamp) CLI framework. Hammer-cli is just a core without any commands.
5
4
 
6
- The core can be extended with plugins and customized according to your application setup. Any Ruby script can be easily turned into a command so possibilities are wide.
5
+ Hammer is a generic [clamp-based](https://github.com/mdub/clamp) CLI framework.
6
+ Hammer-cli provides just the core functionality. The core is extensible using plugins that contain application-specific commands.
7
7
 
8
- Currently available plugins are:
8
+ This architecture allows for easy customization according to your application. Nearly any Ruby script can be turned into a Hammer command, so the possibilities are endless.
9
+
10
+ Available plugins are currently:
9
11
  - [hammer-cli-foreman](https://github.com/theforeman/hammer-cli-foreman) - commands corresponding to Foreman API
10
12
  - [hammer-cli-katello-bridge](https://github.com/theforeman/hammer-cli-katello-bridge) - set of commands provided by Katello CLI
11
13
 
12
- You also can easily add custom commands specific for your use, such as various bulk actions or admin tasks.
13
-
14
+ You also can easily add custom commands for your specific use, such as bulk actions or admin tasks.
14
15
 
15
16
 
16
- Installation instructions
17
- -------------------------
17
+ Installation
18
+ ------------
18
19
 
19
20
  Hammer CLI is packaged for the following RPM based distributions:
20
21
 
21
22
  - RHEL and derivatives, version 6
22
23
  - Fedora 18, 19
24
+ - Debian Wheezy, Squeezy
25
+ - Ubuntu Precise
26
+
27
+ ### Installation from RPMs
28
+
29
+ #### Step 1: setup yum repositories
30
+
31
+ For Foreman 1.3 stable the hammer packages are part of your installation repo and you can skip this step.
32
+
33
+ You can choose from stable or nightly repo. Nightly has more recent version of hammer packages, but it was subject to less testing so there is a higher risk of issues.
34
+ Add the Foreman yum repository to your yum repo files. For Fedora installations replace 'el6' with 'f18' or 'f19' as appropriate.
23
35
 
24
36
 
25
- #### Step 1: setup rpm repositories
26
- Add the Foreman's nightly rpm repository to your yum repo files. For Fedora installations replace 'el6' with 'f18' or 'f19' as appropriate.
37
+ Using stable
27
38
 
28
39
  ```bash
29
- http://yum.theforeman.org/nightly/el6/$basearch/
40
+ yum -y install http://yum.theforeman.org/releases/1.3/el6/x86_64/foreman-release.rpm
41
+ ```
42
+
43
+ or nightly
44
+
45
+ ```bash
46
+ cat > /etc/yum.repos.d/foreman.repo << EOF
47
+ [foreman]
48
+ name=Foreman Nightly
49
+ baseurl=http://yum.theforeman.org/nightly/el6/x86_64
50
+ gpgcheck=0
51
+ enabled=1
52
+ EOF
30
53
  ```
31
54
 
32
55
  On RHEL systems you will also have to add [EPEL repository](https://fedoraproject.org/wiki/EPEL) as it contains some of the required dependencies.
33
56
 
34
57
 
35
58
  #### Step 2: install hammer core
59
+
60
+ ```bash
61
+ yum install rubygem-hammer_cli
62
+ ```
63
+
64
+ #### Step 3: install plugins
65
+ Currently, there are two plugins, both available as rpm packages.
66
+
67
+ - commands for managing foreman
68
+
69
+ ```bash
70
+ yum install rubygem-hammer_cli_foreman
36
71
  ```
37
- $ yum install rubygem-hammer_cli
72
+
73
+ - 1:1 bridge to [katello cli](https://github.com/Katello/katello)
74
+
75
+ ```bash
76
+ yum install rubygem-hammer_cli_katello_bridge
38
77
  ```
39
78
 
79
+ To install any other hammer plugin just make sure the appropriate gem is installed and follow with the configuration.
80
+
81
+
82
+ ### Installation from DEBs
83
+
84
+ #### Step 1: setup apt repositories
85
+
86
+ For Foreman 1.3 stable the hammer packages are part of your installation repo and you can skip this step.
87
+
88
+ You can choose from stable or nightly repo. Nightly has more recent version of hammer packages, but it was subject to less testing so there is a highr risk of issues.
89
+
90
+ Choose stable (don't forget to replace "squeeze" with version name of your system)
91
+
92
+ ```bash
93
+ echo "deb http://deb.theforeman.org/ squeeze stable" > /etc/apt/sources.list.d/foreman.list
94
+ ```
95
+
96
+ or nightly
97
+
98
+ ```bash
99
+ echo "deb http://deb.theforeman.org/ squeeze nightly" > /etc/apt/sources.list.d/foreman.list
100
+ ```
101
+
102
+ and update the keys
103
+
104
+ ```bash
105
+ wget -q http://deb.theforeman.org/foreman.asc -O- | apt-key add -
106
+ ```
107
+
108
+ #### Step 2: install hammer core
109
+
110
+ ```bash
111
+ apt-get update && apt-get install ruby-hammer-cli
112
+ ```
40
113
 
41
114
  #### Step 3: install plugins
42
- Currently, there are two plugins, both available as rpm packages.
115
+ Currently, there are two plugins, both available as deb packages.
43
116
 
44
117
  - commands for managing foreman
118
+
119
+ ```bash
120
+ $ apt-get install ruby-hammer-cli-foreman
45
121
  ```
46
- $ yum install rubygem-hammer_cli_foreman
122
+
123
+ - 1:1 bridge to [katello cli](https://github.com/Katello/katello)
124
+
125
+ ```bash
126
+ $ apt-get install ruby-hammer-cli-katello-bridge
127
+ ```
128
+
129
+ To install any other hammer plugin just make sure the appropriate gem is installed and follow with the configuration.
130
+
131
+
132
+ ### Installation from GEMs
133
+
134
+ Make sure you have ```gem``` command installed on your system
135
+
136
+ #### Step 1: install hammer core
137
+
138
+ ```bash
139
+ $ gem install hammer_cli
140
+ ```
141
+
142
+ #### Step 2: install plugins
143
+ Currently, there are two plugins, both available on rubygems.org
144
+
145
+ - commands for managing foreman
146
+
147
+ ```bash
148
+ $ gem install hammer_cli_foreman
47
149
  ```
48
150
 
49
151
  - 1:1 bridge to [katello cli](https://github.com/Katello/katello)
152
+
153
+ ```bash
154
+ $ gem install hammer_cli_katello_bridge
50
155
  ```
51
- $ yum install rubygem-hammer_cli_katello_bridge
156
+
157
+ To install any other hammer plugin just install the appropriate gem and follow with the configuration.
158
+
159
+
160
+ ### Installation from SOURCE
161
+
162
+ If you can install hammer from git checkouts, you will just need ```rake``` installed on your system.
163
+ Clone and install CLI core
164
+
165
+ ```bash
166
+ $ git clone https://github.com/theforeman/hammer-cli.git
167
+ $ cd hammer-cli
168
+ $ rake install
169
+ $ cd ..
52
170
  ```
53
171
 
54
- Plugins are disabled after the installation. You have to edit the config file and enable them manually.
172
+ clone plugin with foreman commands
55
173
 
174
+ ```bash
175
+ $ git clone https://github.com/theforeman/hammer-cli-foreman.git
176
+ $ cd hammer-cli-foreman
177
+ $ rake install
178
+ $ cd ..
179
+ ```
56
180
 
57
- #### Step 4: configuration
181
+ and optionally other plugins via any of the methods mentioned above.
58
182
 
59
- Edit ```/etc/foreman/cli_config.yml``` or ```~/.foreman/cli_config.yml``` and uncomment lines with names of modules you've just installed to enable them:
60
183
 
61
- ```yaml
62
- :modules:
63
- # - hammer_cli_foreman
64
- # - hammer_cli_katello_bridge
184
+ Configuration
185
+ -------------
186
+
187
+ ### Format and locations
188
+
189
+ Configuration is set based on the following files, loaded in this order:
190
+
191
+ - ```/etc/foreman/cli_config.yml```.
192
+ - ```~/.foreman/cli_config.yml```
193
+ - ```./config/cli_config.yml``` (config dir in CWD)
194
+ - custom location specified on command line - ```-c CONF_FILE_PATH```
195
+
196
+ Later files have precedence if they redefines the same option.
197
+
198
+ Hammer uses yaml formatting for its configuration. The config file template is contained in the hammer_cli gem
199
+
200
+ ```bash
201
+ gem contents hammer_cli|grep cli_config.template.yml
65
202
  ```
203
+ and can be copied to one of the locations above and changed as needed. The packaged version of hammer copies the template to /etc for you.
204
+
66
205
 
67
- Confirm your setup by running ```$ hammer -h``` and see if the desired commands are listed.
206
+ ### Plugins
68
207
 
69
- You will also most likely want to change the url of the Foreman server.
208
+ Plugins are disabled by default. You have to edit the config file and enable them manually under ```modules``` option, as can be seen in the sample config below.
209
+
210
+ Plugin specific configuration should be nested under plugin's name.
211
+
212
+ ### Options
213
+
214
+ - ```:log_dir: <path>``` - directory where the logs are stored. The default is ```/var/log/foreman/``` and the log file is named ```hammer.log```
215
+ - ```:log_level: <level>``` - logging level. One of ```debug```, ```info```, ```warning```, ```error```, ```fatal```
216
+ - ```:log_owner: <owner>``` - logfile owner
217
+ - ```:log_group: <group>``` - logfile group
218
+ - ```:log_size: 1048576``` - size in bytes, when exceeded the log rotates. Default is 1MB
219
+ - ```:watch_plain: false``` - turn on/off syntax highlighting of data being logged in debug mode
220
+
221
+ ### Sample config
70
222
 
71
223
  ```yaml
224
+ :modules:
225
+ - hammer_cli_foreman
226
+ - hammer_cli_katello_bridge
227
+
72
228
  :foreman:
73
229
  :host: 'https://localhost/'
74
230
  :username: 'admin'
75
231
  :password: 'changeme'
76
- ```
77
232
 
78
- Done. Your hammer client is configured and ready to use.
233
+ :katello_bridge:
234
+ :cli_description: '/home/mbacovsk/work/theforeman/hammer-cli-katello-bridge/katello.json'
79
235
 
80
- #### Git installation
81
- Optionally you can install hammer from git checkouts. You will need ```rake``` and ```bundler```.
82
- Clone and install CLI core
83
-
84
- $ git clone git@github.com:theforeman/hammer-cli.git
85
- $ cd hammer-cli
86
- $ rake install
87
- $ cd ..
88
236
 
237
+ :log_dir: '/var/log/foreman/'
238
+ :log_level: 'debug'
239
+ ```
89
240
 
90
- clone plugin with foreman commands
91
-
92
- $ git clone git@github.com:theforeman/hammer-cli-foreman.git
93
- $ cd hammer-cli-foreman
94
- $ rake install
95
- $ cd ..
96
-
97
- and configure. Configuration is by default looked for in ```~/.foreman/``` or in ```/etc/foreman/```.
98
- Optionally you can put your configuration in ```./config/``` or point hammer
99
- to some other location using ```-c CONF_FILE``` option
241
+ Use the hammer
242
+ --------------
100
243
 
101
- You can start with config file template we created for you and update it to suit your needs. E.g.:
244
+ Confirm your setup by running ```$ hammer -h``` and check that the desired commands are listed.
102
245
 
103
- $ cp hammer-cli/config/cli_config.template.yaml ~/.foreman/cli_config.yml
246
+ ```
247
+ $ hammer -h
248
+ Usage:
249
+ hammer [OPTIONS] SUBCOMMAND [ARG] ...
250
+
251
+ Parameters:
252
+ SUBCOMMAND subcommand
253
+ [ARG] ... subcommand arguments
254
+
255
+ Subcommands:
256
+ architecture Manipulate Foreman's architectures.
257
+ global_parameter Manipulate Foreman's global parameters.
258
+ compute_resource Manipulate Foreman's compute resources.
259
+ domain Manipulate Foreman's domains.
260
+ fact Search Foreman's facts.
261
+ report Browse and read reports.
262
+ puppet_class Browse and read reports.
263
+ host Manipulate Foreman's hosts.
264
+ hostgroup Manipulate Foreman's hostgroups.
265
+ location Manipulate Foreman's locations.
266
+ medium Manipulate Foreman's installation media.
267
+ model Manipulate Foreman's hardware models.
268
+ os Manipulate Foreman's operating system.
269
+ organization Manipulate Foreman's organizations.
270
+ partition_table Manipulate Foreman's partition tables.
271
+ proxy Manipulate Foreman's smart proxies.
272
+ subnet Manipulate Foreman's subnets.
273
+ template Manipulate Foreman's config templates.
274
+ about status of the katello server and its subcomponents
275
+ activation_key activation key specific actions in the katello server
276
+ admin various administrative actions
277
+ changeset changeset specific actions in the katello server
278
+ client client specific actions in the katello server
279
+ content content namespace command
280
+ distribution repo specific actions in the katello server
281
+ distributor distributor specific actions in the katello server
282
+ environment environment specific actions in the katello server
283
+ errata errata specific actions in the katello server
284
+ gpg_key GPG key specific actions in the katello server
285
+ node node specific actions in the katello server
286
+ org organization specific actions in the katello server
287
+ package package specific actions in the katello server
288
+ package_group package group specific actions in the katello server
289
+ permission permission specific actions in the katello server
290
+ ping get the status of the katello server
291
+ product product specific actions in the katello server
292
+ provider provider specific actions in the katello server
293
+ puppet_module puppet module specific actions in the katello server
294
+ repo repo specific actions in the katello server
295
+ shell run the cli as a shell
296
+ sync_plan synchronization plan specific actions in the katello server
297
+ system system specific actions in the katello server
298
+ system_group system group specific actions in the katello server
299
+ task commands for retrieving task information
300
+ user user specific actions in the katello server
301
+ user_role user role specific actions in the katello server
302
+ version get the version of the katello server
303
+
304
+ Options:
305
+ -v, --verbose be verbose
306
+ -c, --config CFG_FILE path to custom config file
307
+ -u, --username USERNAME username to access the remote system
308
+ -p, --password PASSWORD password to access the remote system
309
+ --version show version
310
+ --show-ids Show ids of associated resources
311
+ --csv Output as CSV (same as --adapter=csv)
312
+ --output ADAPTER Set output format. One of [base, table, silent, csv]
313
+ --csv-separator SEPARATOR Character to separate the values
314
+ -P, --ask-pass Ask for password
315
+ --autocomplete LINE Get list of possible endings
316
+ -h, --help print help
317
+ ```
104
318
 
105
319
 
320
+ And you are Done. Your hammer client is configured and ready to use.
106
321
 
107
322
 
108
323
  Autocompletion
109
324
  --------------
110
325
 
111
- It is necessary to copy script hammer_cli_complete to the bash_completion.d directory.
326
+ It is necessary to copy the hammer_cli_complete script to the bash_completion.d directory.
112
327
 
113
328
  $ sudo cp hammer-cli/hammer_cli_complete /etc/bash_completion.d/
114
329
 
115
- Then in new shell the completion should work.
116
-
117
-
118
- How to test
119
- ------------
120
-
121
- Development of almost all the code was test driven.
330
+ Then after starting a new shell the completion should work.
122
331
 
123
- $ bundle install
124
- $ bundle exec "rake test"
125
332
 
126
- should work in any of the cli related repos. Generated coverage reports are stored in ./coverage directory.
333
+ Further reading
334
+ ---------------
335
+ If you're interested in hammer and want to develop some plugins for Foreman
336
+ or use it as a base for your own cli, read
337
+ [the developer docs](doc/developer_docs.md#hammer-developer-docs).
127
338
 
128
339
  License
129
340
  -------
@@ -0,0 +1,613 @@
1
+ Hammer Developer Docs
2
+ =====================
3
+
4
+ Hammer is a generic clamp-based CLI framework. It uses existing clamp features and adds some extra utilities.
5
+ We recommend to get familiar with the [Clamp documentation](https://github.com/mdub/clamp/#quick-start)
6
+ before creating some hammer specific plugins.
7
+
8
+
9
+ Writing your own Hammer plugin
10
+ ------------------------------
11
+
12
+ In this tutorial we will create a simple hello world plugin.
13
+
14
+ Hammer plugins are nothing but gems. Details on how to build a gem can be found for example at [rubygems.org](http://guides.rubygems.org/make-your-own-gem/).
15
+ In the first part of this tutorial we will briefly guide you through the process of creating a very simple gem. First of all you will need rubygems package installed on your system.
16
+
17
+ Create the basic gem structure in a project subdirectory of your choice:
18
+ ```
19
+ $ cd ./my_first_hammer_plugin/
20
+ $ touch Gemfile
21
+ $ touch hammer_cli_hello.gemspec
22
+ $ mkdir -p lib/hammer_cli_hello
23
+ $ touch lib/hammer_cli_hello.rb
24
+ $ touch lib/hammer_cli_hello/version.rb
25
+ ```
26
+
27
+ Example `Gemfile`:
28
+ ```ruby
29
+ source "https://rubygems.org"
30
+
31
+ gemspec
32
+ ```
33
+
34
+ Example `hammer_cli_hello.gemspec` file:
35
+ ```ruby
36
+ $:.unshift File.expand_path("../lib", __FILE__)
37
+ require "hammer_cli_hello/version"
38
+
39
+ Gem::Specification.new do |s|
40
+
41
+ s.name = "hammer_cli_hello"
42
+ s.authors = ["Me"]
43
+ s.version = HammerCLIHello.version.dup
44
+ s.platform = Gem::Platform::RUBY
45
+ s.summary = %q{Hello world commands for Hammer}
46
+
47
+ s.files = Dir['lib/**/*.rb']
48
+ s.require_paths = ["lib"]
49
+
50
+ s.add_dependency 'hammer_cli', '>= 0.0.6'
51
+ end
52
+ ```
53
+ More details about the gemspec structure is again at [rubygems.org](http://guides.rubygems.org/specification-reference/).
54
+
55
+ We'll have to specify the plugins version in `lib/hammer_cli_hello/version.rb`:
56
+ ```ruby
57
+ module HammerCLIHello
58
+ def self.version
59
+ @version ||= Gem::Version.new '0.0.1'
60
+ end
61
+ end
62
+ ```
63
+
64
+ This should be enough for creating a minimalist gem. Let's build and install it.
65
+ ```
66
+ $ gem build ./hammer_cli_hello.gemspec
67
+ $ gem install hammer_cli_hello-0.0.1.gem
68
+ ```
69
+
70
+ Update the hammer config to enable your plugin.
71
+ ```yaml
72
+ :modules:
73
+ - hammer_cli_hello
74
+ # - hammer_cli_foreman
75
+ # - hammer_cli_katello_bridge
76
+ ```
77
+
78
+
79
+ Verify the installation by running:
80
+ ```
81
+ $ hammer -v > /dev/null
82
+ ```
83
+
84
+ You should see a message saying that your module was loaded (second line in the sample output).
85
+ ```
86
+ [ INFO 2013-10-16 11:19:06 Init] Configuration from the file /etc/foreman/cli_config.yml has been loaded
87
+ [ INFO 2013-10-16 11:19:06 Init] Extension module hammer_cli_hello loaded
88
+ [ INFO 2013-10-16 11:19:06 HammerCLI::MainCommand] Called with options: {"verbose"=>true}
89
+ ```
90
+
91
+ Done. Your first hammer plugin is installed. Unfortunatelly it does not contain any commands yet. So let's start adding some to finally enjoy real results.
92
+
93
+ Optionally you can add a Rakefile and build and install the gem with `rake install`
94
+ ```ruby
95
+ # ./Rakefile
96
+ require 'bundler/gem_tasks'
97
+ ```
98
+
99
+
100
+ Create your first command
101
+ -------------------------
102
+
103
+ We will create a simple command called `hello` that will print a sentence "Hello World!" to stdout.
104
+
105
+ ### Declare the command
106
+
107
+ ```
108
+ touch ./lib/hammer_cli_hello/hello_world.rb
109
+ ```
110
+
111
+ ```ruby
112
+ # ./lib/hammer_cli_hello/hello_world.rb
113
+ require 'hammer_cli'
114
+
115
+ # it's a good practise to nest commands into modules
116
+ module HammerCLIHello
117
+
118
+ # hammer commands must be descendants of AbstractCommand
119
+ class HelloCommand < HammerCLI::AbstractCommand
120
+
121
+ # execute is the heart of the command
122
+ def execute
123
+ # we use print_message instead of simple puts
124
+ # the reason will be described later in the part called Output
125
+ print_message "Hello World!"
126
+ end
127
+ end
128
+
129
+ # now plug your command into the hammer's main command
130
+ HammerCLI::MainCommand.subcommand
131
+ 'hello', # command's name
132
+ "Say Hello World!", # description
133
+ HammerCLIHello::HelloCommand # the class
134
+ end
135
+ ```
136
+
137
+ The last bit is to require the file with your command in `hammer_cli_hello.rb`.
138
+ Hammer actually loads this file and this is how the commands from plugins get loaded
139
+ into hammer.
140
+ ```ruby
141
+ # ./lib/hammer_cli_hello.rb
142
+ require 'hammer_cli_hello/hello_world'
143
+ ```
144
+
145
+ Rebuild and reinstall your plugin and see the results of `hammer -h`
146
+ ```
147
+ gem build ./hammer_cli_hello.gemspec && gem install hammer_cli_hello-0.0.1.gem
148
+ ```
149
+
150
+
151
+ ```
152
+ $ hammer -h
153
+ Usage:
154
+ hammer [OPTIONS] SUBCOMMAND [ARG] ...
155
+
156
+ Parameters:
157
+ SUBCOMMAND subcommand
158
+ [ARG] ... subcommand arguments
159
+
160
+ Subcommands:
161
+ shell Interactive Shell
162
+ hello Say Hello World!
163
+
164
+ Options:
165
+ -v, --verbose be verbose
166
+ -c, --config CFG_FILE path to custom config file
167
+ -u, --username USERNAME username to access the remote system
168
+ -p, --password PASSWORD password to access the remote system
169
+ --version show version
170
+ --show-ids Show ids of associated resources
171
+ --csv Output as CSV (same as --adapter=csv)
172
+ --output ADAPTER Set output format. One of [csv, table, base, silent]
173
+ --csv-separator SEPARATOR Character to separate the values
174
+ -P, --ask-pass Ask for password
175
+ --autocomplete LINE Get list of possible endings
176
+ -h, --help print help
177
+ ```
178
+
179
+ Now try running the command.
180
+
181
+ ```
182
+ $ hammer hello
183
+ Hello World!
184
+ Error: exit code must be integer
185
+ ```
186
+
187
+ What's wrong here? Hammer requires integer exit codes as return values from the method `execute`.
188
+ It's usually just `HammerCLI::EX_OK`. Add it as the very last line of `execute`, rebuild and the
189
+ command should run fine.
190
+
191
+ See [exit_codes.rb](https://github.com/theforeman/hammer-cli/blob/master/lib/hammer_cli/exit_codes.rb)
192
+ for the full list of available exit codes.
193
+
194
+
195
+ ### Declaring options
196
+ Our new command has only one option so far. It's `-h` which is built in for every command by default.
197
+ Option declaration is the same as in clamp so please read it's
198
+ [documentation](https://github.com/mdub/clamp/#declaring-options)
199
+ on that topic.
200
+
201
+ Example option usage could go like this:
202
+ ```ruby
203
+ class HelloCommand < HammerCLI::AbstractCommand
204
+
205
+ option '--name', "NAME", "Name of the person you want to greet"
206
+
207
+ def execute
208
+ print_message "Hello %s!" % (name || "World")
209
+ HammerCLI::EX_OK
210
+ end
211
+ end
212
+ ```
213
+
214
+ ```
215
+ $ hammer hello -h
216
+ Usage:
217
+ hammer hello [OPTIONS]
218
+
219
+ Options:
220
+ --name NAME Name of the person you want to greet
221
+ -h, --help print help
222
+ ```
223
+
224
+ ```
225
+ $ hammer hello --name 'Foreman'
226
+ Hello Foreman!
227
+ ```
228
+
229
+
230
+ ### Option validation
231
+ Hammer provides extended functionality for validating options.
232
+
233
+ #### DSL
234
+ First of all there is a dsl for validating combinations of options:
235
+ ```ruby
236
+ validate_options do
237
+ all(:name, :surname).required # requires all the options
238
+ option(:age).required # requires a single option,
239
+ # equivalent of :required => true in option declaration
240
+ any(:email, :phone).required # requires at least one of the options
241
+
242
+ # Tt is possible to create more complicated constructs.
243
+ # This example requires either the full address or nothing
244
+ if any(:street, :city, :zip).exist?
245
+ all(:street, :city, :zip).required
246
+ end
247
+
248
+ # Here you can reject all address related option when --no-address is passed
249
+ if option(:no_address).exist?
250
+ all(:street, :city, :zip).rejected
251
+ end
252
+ end
253
+
254
+ ```
255
+
256
+ #### Option formatters
257
+ Another option-related feature is a set of formatters for specific option types:
258
+
259
+ * _HammerCLI::OptionFormatters.list_
260
+
261
+ Parses comma separated strings to a list of values.
262
+
263
+ Usage:
264
+ ```ruby
265
+ option "--users", "USER_NAMES", "List of user names", &HammerCLI::OptionFormatters.method(:list)
266
+ ```
267
+ `--users='J.R.,Gary,Bobby'` -> `['J.R.', 'Gary', 'Bobby']`
268
+
269
+ * _HammerCLI::OptionFormatters.file_
270
+
271
+ Loads contents of a file and returns it as a value of the option.
272
+
273
+ Usage:
274
+ ```ruby
275
+ option "--poem", "PATH_TO_POEM", "File containing the text of your poem", &HammerCLI::OptionFormatters.method(:file)
276
+ ```
277
+ `--poem=~/verlaine/les_poetes_maudits.txt` -> content of the file
278
+
279
+
280
+ ### Adding subcommands
281
+ Commands in the cli can be structured into a tree of parent commands (nodes) and subcommands (leaves).
282
+ Neither the number of subcommands nor the nesting is limited. Please note that no parent command
283
+ can perform any action and therefore it's useless to define `execute` method for them. This limit
284
+ comes from Clamp's implementation of the command hierarchy.
285
+
286
+ We've already used command nesting for plugging the `HelloCommand` command into the main command.
287
+ But let's create a new command `say` and show how to connect it with others to be more demonstrative.
288
+
289
+ ```ruby
290
+ module HammerCLIHello
291
+
292
+ # a new parent command 'say'
293
+ class SayCommand < HammerCLI::AbstractCommand
294
+
295
+ # subcommand 'hello' remains the same
296
+ class HelloCommand < HammerCLI::AbstractCommand
297
+
298
+ option '--name', "NAME", "Name of the person you want to greet"
299
+
300
+ def execute
301
+ print_message "Hello %s!" % (name || "World")
302
+ HammerCLI::EX_OK
303
+ end
304
+ end
305
+
306
+ # plug the original command into 'say'
307
+ subcommand 'hello', "Say Hello World!", HammerCLIHello::SayCommand::HelloCommand
308
+ end
309
+
310
+ # plug the 'say' command into the main command
311
+ HammerCLI::MainCommand.subcommand 'say', "Say something", HammerCLIHello::SayCommand
312
+ end
313
+ ```
314
+
315
+ The result will be:
316
+ ```
317
+ $ hammer say hello
318
+ Hello World!
319
+ ```
320
+
321
+ This is very typical usage of subcommands. When you create more of them it may feel a bit
322
+ duplicit to always define the subcommand structure at the end of the class definition.
323
+ Hammer provides utility methods for subcommand autoloading. This is handy especially
324
+ when you have growing number of subcommands. See how it works in the following example:
325
+
326
+ ```ruby
327
+ module HammerCLIHello
328
+
329
+ class SayCommand < HammerCLI::AbstractCommand
330
+
331
+ class HelloCommand < HammerCLI::AbstractCommand
332
+ command_name 'hello' # name and description moves to the command's class
333
+ desc 'Say Hello World!'
334
+ # ...
335
+ end
336
+
337
+ class HiCommand < HammerCLI::AbstractCommand
338
+ command_name 'hi'
339
+ desc 'Say Hi World!'
340
+ # ...
341
+ end
342
+
343
+ class ByeCommand < HammerCLI::AbstractCommand
344
+ command_name 'bye'
345
+ desc 'Say Bye World!'
346
+ # ...
347
+ end
348
+
349
+ autoload_subcommands
350
+ end
351
+
352
+ HammerCLI::MainCommand.subcommand 'say', "Say something", HammerCLIHello::SayCommand
353
+ end
354
+ ```
355
+
356
+ ```
357
+ $ hammer say
358
+ Usage:
359
+ hammer say [OPTIONS] SUBCOMMAND [ARG] ...
360
+
361
+ Parameters:
362
+ SUBCOMMAND subcommand
363
+ [ARG] ... subcommand arguments
364
+
365
+ Subcommands:
366
+ hi Say Hi World!
367
+ hello Say Hello World!
368
+ bye Say Bye World!
369
+
370
+ Options:
371
+ -h, --help print help
372
+ ```
373
+
374
+
375
+ ### Conflicting subcommands
376
+ It can happen that two different plugins define subcommands with the same name by accident.
377
+ In such situations `subcommand` will throw an exception. If this is intentional and you
378
+ want to redefine the existing command, use `subcommand!`.
379
+ This method does not throw exceptions, replaces the original subcommand, and leaves
380
+ a message in a log for debugging purposes.
381
+
382
+
383
+ ### Printing some output
384
+ We've mentioned above that it's not recommended practice to print output
385
+ directly with `puts` in Hammer. The reason is we separate definition
386
+ of the output from its interpretation. Hammer uses so called _output adapters_
387
+ that can modify the output format.
388
+
389
+ Hammer comes with four basic output adapters:
390
+ * __base__ - simple output, structured records
391
+ * __table__ - records printed in tables, ideal for printing lists of records
392
+ * __csv__ - comma separated output, ideal for scripting and grepping
393
+ * __silent__ - no output, used for testing
394
+
395
+ The detailed documentation on creating adapters is coming soon.
396
+
397
+ #### Printing messages
398
+ Very simple, just call
399
+ ```ruby
400
+ print_message(msg)
401
+ ```
402
+
403
+ #### Printing hash records
404
+ Typical usage of a cli is interaction with some api. In many cases it's listing
405
+ some records returned by the api.
406
+
407
+ Hammer comes with support for selecting and formatting of hash record fields.
408
+ You first create so called _output definition_ that you apply on your data. The result
409
+ is a collection of fields each having its type. The collection is then passed to some
410
+ _output adapter_ which handles the actuall formatting and printing.
411
+
412
+ Hammer provides a DSL for defining the output. Next rather complex example will
413
+ explain how to use it in action.
414
+
415
+ Imagine there's an API of some service that returns list of users:
416
+ ```ruby
417
+ [{
418
+ :id => 1,
419
+ :email => 'tom@email.com',
420
+ :phone => '123456111',
421
+ :first_name => 'Tom',
422
+ :last_name => 'Sawyer',
423
+ :roles => ['Admin', 'Editor'],
424
+ :timestamps => {
425
+ :created => '2012-12-18T15:24:42Z',
426
+ :updated => '2012-12-18T15:24:42Z'
427
+ }
428
+ },{
429
+ :id => 2,
430
+ :email => 'huckleberry@email.com',
431
+ :phone => '123456222',
432
+ :first_name => 'Huckleberry',
433
+ :last_name => 'Finn',
434
+ :roles => ['Admin'],
435
+ :timestamps => {
436
+ :created => '2012-12-18T15:25:00Z',
437
+ :updated => '2012-12-20T14:00:15Z'
438
+ }
439
+ }]
440
+ ```
441
+
442
+ We can create an output definition that selects and formats some of the fields:
443
+ ```ruby
444
+ dsl = HammerCLI::Output::Dsl.new
445
+ dsl.build do
446
+
447
+ # Simple field with a label. The first parameter is key in the printed hash.
448
+ field :id, 'ID'
449
+
450
+ # Fields can have types. The type determines how the field is printed.
451
+ # All available types are listed below.
452
+ # Here we want the roles to act as list.
453
+ field :roles, 'System Roles', Fields::List
454
+
455
+ # Label is used for grouping fields.
456
+ label 'Contacts' do
457
+ field :email, 'Email'
458
+ field :phone, 'Phone No.'
459
+ end
460
+
461
+ # From is used for accessing nested fields.
462
+ from :timestamps do
463
+ # See how date gets formatted in the output
464
+ field :created, 'Created At', Fields::Date
465
+ end
466
+ end
467
+
468
+ definition = HammerCLI::Output::Definition.new
469
+ definition.append(dsl.fields)
470
+
471
+ print_records(definition, data)
472
+
473
+ ```
474
+
475
+ Using the base adapter the output will look like:
476
+ ```
477
+ ID: 1
478
+ System Roles: Admin, Editor
479
+ Name: Tom Sawyer
480
+ Contacts:
481
+ Email: tom@email.com
482
+ Phone No.: 123456111
483
+ Created At: 2012/12/18 15:24:42
484
+
485
+ ID: 2
486
+ System Roles: Admin
487
+ Name: Huckleberry Finn
488
+ Contacts:
489
+ Email: huckleberry@email.com
490
+ Phone No.: 123456222
491
+ Created At: 2012/12/18 15:25:00
492
+ ```
493
+
494
+ All Hammer field types are:
495
+ * __Date__
496
+ * __Id__ - Used to mark ID values, current print adapters have support for turning id printing on/off.
497
+ See hammer's parameter `--show-ids`.
498
+ * __List__
499
+ * __KeyValue__ - Formats hashes containing `:name` and `:value`
500
+ * __Collection__ - Enables to render subcollections. Takes a block with another output definition.
501
+
502
+ The default adapter for every command is Base adapter. It is possible to override
503
+ the default one by redefining command's method `adapter`.
504
+
505
+ ```ruby
506
+ def adapter
507
+ # return :base, :table, :csv or name of your own adapter here
508
+ :table
509
+ end
510
+ ```
511
+
512
+
513
+ Other useful command features
514
+ -----------------------------
515
+
516
+ #### Logging
517
+ Hammer provides integrated [logger](https://github.com/TwP/logging)
518
+ with broad setting options (use hammer's config file):
519
+
520
+ ```yaml
521
+ :log_dir: '<path>' # - directory where the logs are stored.
522
+ # The default is /var/log/foreman/ and the log file is named hammer.log
523
+ :log_level: '<level>' # - logging level. One of debug, info, warning, error, fatal
524
+ :log_owner: '<owner>' # - logfile owner
525
+ :log_group: '<group>' # - logfile group
526
+ :log_size: 1048576 # - size in bytes, when exceeded the log rotates. Default is 1MB
527
+ :watch_plain: false # - turn on/off syntax highlighting of data being logged in debug mode
528
+ ```
529
+
530
+ Example usage in commands:
531
+ ```ruby
532
+ # Get a logger instance
533
+ logger('Logger name')
534
+
535
+ # It uses a command class name as the logger's name by default
536
+ logger
537
+
538
+ # Log a message at corresponding log level
539
+ logger.debug("...")
540
+ logger.error("...")
541
+ logger.info("...")
542
+ logger.fatal("...")
543
+ logger.warn("...")
544
+
545
+ # Writes an awesome print dump of a value to the log
546
+ logger.watch('Some label', value)
547
+ ```
548
+
549
+ #### Exception handling
550
+ Exception handling in Hammer is centralized by
551
+ [ExceptionHandler](https://github.com/theforeman/hammer-cli/blob/master/lib/hammer_cli/exception_handler.rb).
552
+ Each plugin, module or even a command can have a separate exception handler. The exception handler class
553
+ is looked up in the module structure from a command to the top level.
554
+
555
+ Define method `self.exception_handler_class` in your plugin's module to use a custom exception handler:
556
+ ```ruby
557
+ # ./lib/hammer_cli_hello.rb
558
+
559
+ module HammerCLIHello
560
+
561
+ def self.exception_handler_class
562
+ HammerCLIHello::CustomExceptionHandler
563
+ end
564
+ end
565
+
566
+ require 'hammer_cli_hello/hello_world'
567
+ ```
568
+
569
+ Centralized exception handling implies that you should raise exceptions on error states in your command
570
+ rather than handle it and return error codes. This approach guarrantees that error messages are logged and
571
+ printed consistently and correct exit codes are returned.
572
+
573
+
574
+ #### Configuration
575
+ Values form config files are accesible via class `HammerCLI::Settings`.
576
+ It's method `get` returns either the value or nil when it's not found.
577
+
578
+ Config values belonging to a specific plugin must be nested under
579
+ the plugin's name in config files.
580
+
581
+ ```yaml
582
+ #cli_config.yml
583
+ :log_dir: /var/log/hammer/
584
+ :hello_world:
585
+ :name: John
586
+ ```
587
+
588
+ ```ruby
589
+ HammerCLI::Settings.get(:log_dir) # get a value
590
+ HammerCLI::Settings.get(:hello_world, :name) # get a nested value
591
+ ```
592
+
593
+ There's more ways where to place your config file for hammer.
594
+ Read more in [the settings howto](https://github.com/theforeman/hammer-cli#configuration).
595
+
596
+ Creating commands for RESTful API with ApiPie
597
+ ---------------------------------------------
598
+ Coming soon...
599
+
600
+
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
+ -->
609
+
610
+
611
+
612
+
613
+
@@ -32,7 +32,9 @@ module HammerCLI
32
32
  def parse(arguments)
33
33
  super
34
34
  validate_options
35
- logger.info "Called with options: %s" % options.inspect
35
+ safe_options = options.dup
36
+ safe_options.keys.each { |k| safe_options[k] = '***' if k.end_with?('password') }
37
+ logger.info "Called with options: %s" % safe_options.inspect
36
38
  rescue HammerCLI::Validator::ValidationError => e
37
39
  signal_usage_error e.message
38
40
  end
@@ -50,7 +50,11 @@ module Fields
50
50
 
51
51
  def symbolize_hash_keys(h)
52
52
  if h.is_a? Hash
53
- return h.inject({}){|result,(k,v)| result.update k.to_sym => symbolize_hash_keys(v)}
53
+ return h.inject({}) do |result,(k,v)|
54
+ # symbolizing empty string fails in ruby 1.8
55
+ result.update k.to_sym => symbolize_hash_keys(v) unless k.to_s.empty?
56
+ result
57
+ end
54
58
  elsif h.is_a? Array
55
59
  return h.collect{|item| symbolize_hash_keys(item)}
56
60
  else
@@ -1,5 +1,5 @@
1
1
  module HammerCLI
2
2
  def self.version
3
- @version ||= Gem::Version.new '0.0.7'
3
+ @version ||= Gem::Version.new '0.0.8'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hammer_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Martin Bačovský
@@ -9,94 +10,107 @@ authors:
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-10-09 00:00:00.000000000 Z
13
+ date: 2013-10-29 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: clamp
16
17
  requirement: !ruby/object:Gem::Requirement
18
+ none: false
17
19
  requirements:
18
- - - '>='
20
+ - - ! '>='
19
21
  - !ruby/object:Gem::Version
20
22
  version: '0'
21
23
  type: :runtime
22
24
  prerelease: false
23
25
  version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
24
27
  requirements:
25
- - - '>='
28
+ - - ! '>='
26
29
  - !ruby/object:Gem::Version
27
30
  version: '0'
28
31
  - !ruby/object:Gem::Dependency
29
32
  name: rest-client
30
33
  requirement: !ruby/object:Gem::Requirement
34
+ none: false
31
35
  requirements:
32
- - - '>='
36
+ - - ! '>='
33
37
  - !ruby/object:Gem::Version
34
38
  version: '0'
35
39
  type: :runtime
36
40
  prerelease: false
37
41
  version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
38
43
  requirements:
39
- - - '>='
44
+ - - ! '>='
40
45
  - !ruby/object:Gem::Version
41
46
  version: '0'
42
47
  - !ruby/object:Gem::Dependency
43
48
  name: logging
44
49
  requirement: !ruby/object:Gem::Requirement
50
+ none: false
45
51
  requirements:
46
- - - '>='
52
+ - - ! '>='
47
53
  - !ruby/object:Gem::Version
48
54
  version: '0'
49
55
  type: :runtime
50
56
  prerelease: false
51
57
  version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
52
59
  requirements:
53
- - - '>='
60
+ - - ! '>='
54
61
  - !ruby/object:Gem::Version
55
62
  version: '0'
56
63
  - !ruby/object:Gem::Dependency
57
64
  name: awesome_print
58
65
  requirement: !ruby/object:Gem::Requirement
66
+ none: false
59
67
  requirements:
60
- - - '>='
68
+ - - ! '>='
61
69
  - !ruby/object:Gem::Version
62
70
  version: '0'
63
71
  type: :runtime
64
72
  prerelease: false
65
73
  version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
66
75
  requirements:
67
- - - '>='
76
+ - - ! '>='
68
77
  - !ruby/object:Gem::Version
69
78
  version: '0'
70
79
  - !ruby/object:Gem::Dependency
71
80
  name: table_print
72
81
  requirement: !ruby/object:Gem::Requirement
82
+ none: false
73
83
  requirements:
74
- - - '>='
84
+ - - ! '>='
75
85
  - !ruby/object:Gem::Version
76
86
  version: '0'
77
87
  type: :runtime
78
88
  prerelease: false
79
89
  version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
80
91
  requirements:
81
- - - '>='
92
+ - - ! '>='
82
93
  - !ruby/object:Gem::Version
83
94
  version: '0'
84
95
  - !ruby/object:Gem::Dependency
85
96
  name: highline
86
97
  requirement: !ruby/object:Gem::Requirement
98
+ none: false
87
99
  requirements:
88
- - - '>='
100
+ - - ! '>='
89
101
  - !ruby/object:Gem::Version
90
102
  version: '0'
91
103
  type: :runtime
92
104
  prerelease: false
93
105
  version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
94
107
  requirements:
95
- - - '>='
108
+ - - ! '>='
96
109
  - !ruby/object:Gem::Version
97
110
  version: '0'
98
- description: |
99
- Hammer cli provides universal extendable CLI interface for ruby apps
111
+ description: ! 'Hammer cli provides universal extendable CLI interface for ruby apps
112
+
113
+ '
100
114
  email: mbacovsk@redhat.com
101
115
  executables:
102
116
  - hammer
@@ -108,39 +122,40 @@ extra_rdoc_files:
108
122
  - config/cli_config.template.yml
109
123
  - doc/design.png
110
124
  - doc/design.uml
125
+ - doc/developer_docs.md
111
126
  files:
112
- - lib/hammer_cli/logger_watch.rb
113
- - lib/hammer_cli/apipie/write_command.rb
114
- - lib/hammer_cli/apipie/resource.rb
115
- - lib/hammer_cli/apipie/read_command.rb
116
- - lib/hammer_cli/apipie/options.rb
117
- - lib/hammer_cli/apipie/command.rb
118
- - lib/hammer_cli/logger.rb
127
+ - lib/hammer_cli.rb
119
128
  - lib/hammer_cli/messages.rb
120
- - lib/hammer_cli/option_formatters.rb
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
129
+ - lib/hammer_cli/output/fields.rb
130
+ - lib/hammer_cli/output/formatters.rb
128
131
  - lib/hammer_cli/output/adapter/table.rb
129
132
  - lib/hammer_cli/output/adapter/base.rb
130
133
  - lib/hammer_cli/output/adapter/abstract.rb
131
134
  - lib/hammer_cli/output/adapter/silent.rb
132
135
  - lib/hammer_cli/output/adapter/csv.rb
133
- - lib/hammer_cli/output/definition.rb
134
136
  - lib/hammer_cli/output/adapter.rb
135
- - lib/hammer_cli/output/dsl.rb
136
- - lib/hammer_cli/output/formatters.rb
137
137
  - lib/hammer_cli/output/output.rb
138
- - lib/hammer_cli/output/fields.rb
139
- - lib/hammer_cli/settings.rb
138
+ - lib/hammer_cli/output/dsl.rb
139
+ - lib/hammer_cli/output/definition.rb
140
+ - lib/hammer_cli/abstract.rb
141
+ - lib/hammer_cli/shell.rb
142
+ - lib/hammer_cli/autocompletion.rb
143
+ - lib/hammer_cli/logger_watch.rb
144
+ - lib/hammer_cli/logger.rb
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
140
151
  - lib/hammer_cli/validator.rb
152
+ - lib/hammer_cli/version.rb
141
153
  - lib/hammer_cli/exit_codes.rb
154
+ - lib/hammer_cli/output.rb
155
+ - lib/hammer_cli/exception_handler.rb
142
156
  - lib/hammer_cli/main.rb
143
- - lib/hammer_cli.rb
157
+ - lib/hammer_cli/option_formatters.rb
158
+ - lib/hammer_cli/settings.rb
144
159
  - bin/hammer
145
160
  - README.md
146
161
  - LICENSE
@@ -148,29 +163,31 @@ files:
148
163
  - config/cli_config.template.yml
149
164
  - doc/design.png
150
165
  - doc/design.uml
166
+ - doc/developer_docs.md
151
167
  homepage: http://github.com/theforeman/hammer-cli
152
168
  licenses:
153
169
  - GPL-3
154
- metadata: {}
155
170
  post_install_message:
156
171
  rdoc_options: []
157
172
  require_paths:
158
173
  - lib
159
174
  required_ruby_version: !ruby/object:Gem::Requirement
175
+ none: false
160
176
  requirements:
161
- - - '>='
177
+ - - ! '>='
162
178
  - !ruby/object:Gem::Version
163
179
  version: '0'
164
180
  required_rubygems_version: !ruby/object:Gem::Requirement
181
+ none: false
165
182
  requirements:
166
- - - '>='
183
+ - - ! '>='
167
184
  - !ruby/object:Gem::Version
168
185
  version: '0'
169
186
  requirements: []
170
187
  rubyforge_project:
171
- rubygems_version: 2.0.8
188
+ rubygems_version: 1.8.24
172
189
  signing_key:
173
- specification_version: 4
190
+ specification_version: 3
174
191
  summary: Universal command-line interface
175
192
  test_files: []
176
193
  has_rdoc:
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 1f5774b64875357f58a29b8f41584e1257696886
4
- data.tar.gz: f1643f5fb4d1e545646e50ab663765b173684823
5
- SHA512:
6
- metadata.gz: 8ecb458b3c3cf2705a513431c718e5f2bac89c8528f7f11fe1f7364e32c66f20431801787a9d501dbef713b3f9f031750aa24c5f51465bd91778bd129cc867f4
7
- data.tar.gz: eec75c3547ddbe73e9ac5a772f617bb440da9f3ae425c330b7b53eaa9bd0841c246b09c4e3b085b1d9f9ea91754f6d955667816bd8fcce2108c3faed69f1c42f