hammer_cli 0.17.1 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/cli_config.template.yml +3 -0
- data/doc/commands_modification.md +12 -1
- data/doc/creating_commands.md +35 -3
- data/doc/release_notes.md +6 -1
- data/lib/hammer_cli/abstract.rb +18 -8
- data/lib/hammer_cli/apipie/resource.rb +1 -1
- data/lib/hammer_cli/command_extensions.rb +17 -1
- data/lib/hammer_cli/options/predefined.rb +22 -0
- data/lib/hammer_cli/output/adapter/abstract.rb +24 -12
- data/lib/hammer_cli/output/adapter/base.rb +5 -11
- data/lib/hammer_cli/output/adapter/csv.rb +7 -3
- data/lib/hammer_cli/output/adapter/table.rb +9 -9
- data/lib/hammer_cli/output/adapter/tree_structure.rb +5 -10
- data/lib/hammer_cli/output/definition.rb +84 -0
- data/lib/hammer_cli/output/field_filter.rb +72 -9
- data/lib/hammer_cli/output/fields.rb +23 -3
- data/lib/hammer_cli/output/output.rb +2 -0
- data/lib/hammer_cli/ssloptions.rb +1 -1
- data/lib/hammer_cli/version.rb +1 -1
- data/test/unit/abstract_test.rb +5 -0
- data/test/unit/output/definition_test.rb +20 -0
- data/test/unit/output/field_filter_test.rb +60 -15
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb040c3f40863eea4ec0c05ac2d1ffc597b3ca9499470bda6ecc18ff66bcfad2
|
4
|
+
data.tar.gz: acb3307c3d9fcda2903ae19f026f1b4767950562c1507cb02afc2e433a4094c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89d7b3eb46518f18b718cef888205a1a62f03a079bd1e8600aae8217b99f882aa18a7fcc3a7787ab727c05a0a8ddc15690bcfc81ab8e19b2a7cb01c1f5034ac8
|
7
|
+
data.tar.gz: bbbd9bc7fb32e52edfe4451c98c534330d577380074ca3095f00d7666f72e8879fc9efaffd0a141ee86daf006db78cce878e304af21eae50c555a5075da4d0a4
|
@@ -72,3 +72,6 @@
|
|
72
72
|
# Local CA cert store path where hammer stores certificates fetched from the server.
|
73
73
|
# Certs from the local storage are used only when neither :ssl_ca_file: nor :ssl_ca_path: is cofigured.
|
74
74
|
#:local_ca_store_path: '~/.hammer/certs'
|
75
|
+
|
76
|
+
# Allows setting the SSL version to use when making API calls
|
77
|
+
#:ssl_version: 'TLSv1_2'
|
@@ -33,10 +33,15 @@ HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
|
|
33
33
|
end
|
34
34
|
# Returns output definition of the command or field specified with path.
|
35
35
|
# Where:
|
36
|
-
# path
|
36
|
+
# path is an Array of field's :key or/and :id or/and 'label'
|
37
37
|
definition.at(path)
|
38
38
|
# Returns field from current output definition.
|
39
39
|
definition.find_field(:id)
|
40
|
+
# Finds and adds fields to a set (creates a new one if the set doesn't exist).
|
41
|
+
# Where:
|
42
|
+
# sets is an Array of String with set names
|
43
|
+
# fields is an Array of field's :key or/and :id or/and 'label'
|
44
|
+
definition.update_field_sets(sets, fields)
|
40
45
|
# Deletes all fields from current output definition.
|
41
46
|
definition.clear
|
42
47
|
end
|
@@ -55,6 +60,12 @@ HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
|
|
55
60
|
definition.at(:path)
|
56
61
|
.find_field(:id).label = _('New label')
|
57
62
|
end
|
63
|
+
# Update fields in a field set
|
64
|
+
# Adds fields with :field_id and 'My field' to 'SET' set
|
65
|
+
HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
|
66
|
+
definition.at(:path)
|
67
|
+
.update_field_sets('SET', [:field_id, 'My field'])
|
68
|
+
end
|
58
69
|
# Expand a field with new definition
|
59
70
|
HammerCLIForeman::Host::InfoCommand.extend_output_definition do |definition|
|
60
71
|
definition.at([_('some'), :path])
|
data/doc/creating_commands.md
CHANGED
@@ -164,6 +164,14 @@ In cases when you want to deprecate just one of more possible switches use the e
|
|
164
164
|
:deprecated => { '--name' => _('Use --alias instead') }
|
165
165
|
```
|
166
166
|
|
167
|
+
#### Predefined options
|
168
|
+
Also Hammer offers predefined options now. Those are just options, but with
|
169
|
+
predefined functionality. To define them in your command use
|
170
|
+
`use_option :option_name` method.
|
171
|
+
|
172
|
+
Here is the list of predefined options:
|
173
|
+
* `:fields` Expects a list with fields to show in output, see [example](creating_commands.md#printing-hash-records).
|
174
|
+
|
167
175
|
|
168
176
|
### Option builders
|
169
177
|
Hammer commands offer option builders that can be used for automatic option generation.
|
@@ -523,8 +531,12 @@ Imagine there's an API of some service that returns list of users:
|
|
523
531
|
```
|
524
532
|
|
525
533
|
We can create an output definition that selects and formats some of the fields:
|
534
|
+
|
535
|
+
_NOTE_: Every field can be arranged in so-called field sets. All the fields by default go to `'DEFAULT'` and `'ALL'` sets. Fields which are in the `'DEFAULT'` set will be printed by default. To see printed other field sets, use predefined option `--fields NAME`, where `NAME` is a field set name in ALLCAPS.
|
526
536
|
```ruby
|
527
537
|
class Command < HammerCLI::AbstractCommand
|
538
|
+
# To be able to select fields which should be printed
|
539
|
+
use_option :fields
|
528
540
|
|
529
541
|
output do
|
530
542
|
# Simple field with a label. The first parameter is the key in the printed hash.
|
@@ -536,7 +548,7 @@ class Command < HammerCLI::AbstractCommand
|
|
536
548
|
field :roles, 'System Roles', Fields::List
|
537
549
|
|
538
550
|
# Label is used for grouping fields.
|
539
|
-
label 'Contacts' do
|
551
|
+
label 'Contacts', sets: ['ADDITIONAL', 'ALL'] do
|
540
552
|
field :email, 'Email'
|
541
553
|
field :phone, 'Phone No.'
|
542
554
|
end
|
@@ -565,18 +577,38 @@ Using the base adapter the output will look like:
|
|
565
577
|
ID: 1
|
566
578
|
System Roles: Admin, Editor
|
567
579
|
Name: Tom Sawyer
|
580
|
+
Created At: 2012/12/18 15:24:42
|
581
|
+
|
582
|
+
ID: 2
|
583
|
+
System Roles: Admin
|
584
|
+
Name: Huckleberry Finn
|
585
|
+
Created At: 2012/12/18 15:25:00
|
586
|
+
```
|
587
|
+
|
588
|
+
Using the base adapter with `--fields ALL` or `--fields DEFAULT,ADDITIONAL` the output will look like:
|
589
|
+
```
|
590
|
+
ID: 1
|
591
|
+
System Roles: Admin, Editor
|
592
|
+
Name: Tom Sawyer
|
593
|
+
Created At: 2012/12/18 15:24:42
|
568
594
|
Contacts:
|
569
595
|
Email: tom@email.com
|
570
596
|
Phone No.: 123456111
|
571
|
-
Created At: 2012/12/18 15:24:42
|
572
597
|
|
573
598
|
ID: 2
|
574
599
|
System Roles: Admin
|
575
600
|
Name: Huckleberry Finn
|
601
|
+
Created At: 2012/12/18 15:25:00
|
576
602
|
Contacts:
|
577
603
|
Email: huckleberry@email.com
|
578
604
|
Phone No.: 123456222
|
579
|
-
|
605
|
+
```
|
606
|
+
|
607
|
+
_NOTE_: `--fields` as well lets you to print desired fields only. E.g. to see the users' emails without any additional information use `--fields contacts/email`:
|
608
|
+
```
|
609
|
+
Email: tom@email.com
|
610
|
+
|
611
|
+
Email: huckleberry@email.com
|
580
612
|
```
|
581
613
|
|
582
614
|
You can optionally use the output definition from another command as a base and extend it with
|
data/doc/release_notes.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
Release notes
|
2
2
|
=============
|
3
|
-
### 0.
|
3
|
+
### 0.18.0 (2019-08-01)
|
4
|
+
* Unsure minimal label length ([PR #310](https://github.com/theforeman/hammer-cli/pull/310)) ([#26960](http://projects.theforeman.org/issues/26960))
|
5
|
+
* The --fields option has set help ([PR #308](https://github.com/theforeman/hammer-cli/pull/308)) ([#26960](http://projects.theforeman.org/issues/26960))
|
6
|
+
* Filter fields properly ([#26961](http://projects.theforeman.org/issues/26961))
|
7
|
+
* Possibility to limit fields that are displayed ([PR #276](https://github.com/theforeman/hammer-cli/pull/276)) ([#19135](http://projects.theforeman.org/issues/19135))
|
8
|
+
* Allow setting the SSL version to use for API calls
|
4
9
|
* Make sure list opts return a list ([#26703](http://projects.theforeman.org/issues/26703))
|
5
10
|
|
6
11
|
### 0.17.0 (2019-04-24)
|
data/lib/hammer_cli/abstract.rb
CHANGED
@@ -9,13 +9,13 @@ require 'hammer_cli/options/validators/dsl_block_validator'
|
|
9
9
|
require 'hammer_cli/clamp'
|
10
10
|
require 'hammer_cli/subcommand'
|
11
11
|
require 'hammer_cli/options/matcher'
|
12
|
+
require 'hammer_cli/options/predefined'
|
12
13
|
require 'hammer_cli/help/builder'
|
13
14
|
require 'hammer_cli/help/text_builder'
|
14
15
|
require 'hammer_cli/command_extensions'
|
15
16
|
require 'logging'
|
16
17
|
|
17
18
|
module HammerCLI
|
18
|
-
|
19
19
|
class AbstractCommand < Clamp::Command
|
20
20
|
include HammerCLI::Subcommand
|
21
21
|
|
@@ -57,6 +57,12 @@ module HammerCLI
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
60
|
+
|
61
|
+
def add_sets_help(help)
|
62
|
+
sets_details = HammerCLI::Help::Section.new(_('Predefined field sets'), nil, id: :s_sets_details, richtext: true)
|
63
|
+
sets_details.definition << HammerCLI::Help::Text.new(output_definition.sets_table)
|
64
|
+
help.definition.unshift(sets_details)
|
65
|
+
end
|
60
66
|
end
|
61
67
|
|
62
68
|
def adapter
|
@@ -118,9 +124,10 @@ module HammerCLI
|
|
118
124
|
|
119
125
|
def self.help(invocation_path, builder = HammerCLI::Help::Builder.new)
|
120
126
|
super(invocation_path, builder)
|
121
|
-
|
127
|
+
help_extension = HammerCLI::Help::TextBuilder.new(builder.richtext)
|
128
|
+
fields_switch = HammerCLI::Options::Predefined::OPTIONS[:fields].first[0]
|
129
|
+
add_sets_help(help_extension) if find_option(fields_switch)
|
122
130
|
unless help_extension_blocks.empty?
|
123
|
-
help_extension = HammerCLI::Help::TextBuilder.new(builder.richtext)
|
124
131
|
help_extension_blocks.each do |extension_block|
|
125
132
|
begin
|
126
133
|
extension_block.call(help_extension)
|
@@ -129,8 +136,8 @@ module HammerCLI
|
|
129
136
|
handler.handle_exception(e)
|
130
137
|
end
|
131
138
|
end
|
132
|
-
builder.add_text(help_extension.string)
|
133
139
|
end
|
140
|
+
builder.add_text(help_extension.string)
|
134
141
|
builder.string
|
135
142
|
end
|
136
143
|
|
@@ -161,13 +168,11 @@ module HammerCLI
|
|
161
168
|
self.class.output_definition
|
162
169
|
end
|
163
170
|
|
164
|
-
|
165
171
|
def self.output_definition
|
166
172
|
@output_definition = @output_definition || inherited_output_definition || HammerCLI::Output::Definition.new
|
167
173
|
@output_definition
|
168
174
|
end
|
169
175
|
|
170
|
-
|
171
176
|
def interactive?
|
172
177
|
HammerCLI.interactive?
|
173
178
|
end
|
@@ -197,6 +202,7 @@ module HammerCLI
|
|
197
202
|
raise ArgumentError, _('Command extensions should be inherited from %s.') % HammerCLI::CommandExtensions
|
198
203
|
end
|
199
204
|
extension.delegatee(self)
|
205
|
+
extension.extend_predefined_options(self)
|
200
206
|
extension.extend_options(self)
|
201
207
|
extension.extend_output(self)
|
202
208
|
extension.extend_help(self)
|
@@ -205,6 +211,12 @@ module HammerCLI
|
|
205
211
|
end
|
206
212
|
end
|
207
213
|
|
214
|
+
def self.use_option(*names)
|
215
|
+
names.each do |name|
|
216
|
+
HammerCLI::Options::Predefined.use(name, self)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
208
220
|
protected
|
209
221
|
|
210
222
|
def self.find_options(switch_filter, other_filters={})
|
@@ -316,7 +328,6 @@ module HammerCLI
|
|
316
328
|
@option_collector ||= HammerCLI::Options::OptionCollector.new(self.class.recognised_options, add_validators(option_sources))
|
317
329
|
end
|
318
330
|
|
319
|
-
|
320
331
|
def option_sources
|
321
332
|
sources = HammerCLI::Options::ProcessorList.new(name: 'DefaultInputs')
|
322
333
|
sources << HammerCLI::Options::Sources::CommandLine.new(self)
|
@@ -348,6 +359,5 @@ module HammerCLI
|
|
348
359
|
end
|
349
360
|
od
|
350
361
|
end
|
351
|
-
|
352
362
|
end
|
353
363
|
end
|
@@ -15,7 +15,7 @@ module HammerCLI
|
|
15
15
|
ALLOWED_EXTENSIONS = %i[
|
16
16
|
option command_options before_print data output help request
|
17
17
|
request_headers headers request_options options request_params params
|
18
|
-
option_sources
|
18
|
+
option_sources predefined_options use_option
|
19
19
|
].freeze
|
20
20
|
|
21
21
|
def initialize(options = {})
|
@@ -54,6 +54,10 @@ module HammerCLI
|
|
54
54
|
opts: opts, block: block }
|
55
55
|
end
|
56
56
|
|
57
|
+
def self.use_option(*names)
|
58
|
+
@predefined_option_names = names
|
59
|
+
end
|
60
|
+
|
57
61
|
def self.before_print(&block)
|
58
62
|
@before_print_block = block
|
59
63
|
end
|
@@ -91,6 +95,13 @@ module HammerCLI
|
|
91
95
|
self.class.extend_options(command_class)
|
92
96
|
end
|
93
97
|
|
98
|
+
def extend_predefined_options(command_class)
|
99
|
+
allowed = @only & %i[predefined_options use_option]
|
100
|
+
return if allowed.empty? || (allowed & @except).any?
|
101
|
+
|
102
|
+
self.class.extend_predefined_options(command_class)
|
103
|
+
end
|
104
|
+
|
94
105
|
def extend_before_print(data)
|
95
106
|
allowed = @only & %i[before_print data]
|
96
107
|
return if allowed.empty? || (allowed & @except).any?
|
@@ -170,6 +181,11 @@ module HammerCLI
|
|
170
181
|
end
|
171
182
|
end
|
172
183
|
|
184
|
+
def self.extend_predefined_options(command_class)
|
185
|
+
command_class.send(:use_option, *@predefined_option_names)
|
186
|
+
logger.debug("Added predefined options for #{command_class}: #{@predefined_option_names}")
|
187
|
+
end
|
188
|
+
|
173
189
|
def self.extend_before_print(data)
|
174
190
|
return if @before_print_block.nil?
|
175
191
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HammerCLI
|
4
|
+
module Options
|
5
|
+
# Contains predefined by HammerCLI options for commands
|
6
|
+
module Predefined
|
7
|
+
OPTIONS = {
|
8
|
+
fields: [['--fields'], 'FIELDS',
|
9
|
+
_('Show specified fileds or predefined filed sets only. (See below)'),
|
10
|
+
format: HammerCLI::Options::Normalizers::List.new,
|
11
|
+
context_target: :fields]
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
def self.use(option_name, command_class)
|
15
|
+
unless OPTIONS.key?(option_name)
|
16
|
+
raise ArgumentError, _('There is no such predefined option %s.') % option_name
|
17
|
+
end
|
18
|
+
command_class.send(:option, *OPTIONS[option_name])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -6,11 +6,12 @@ module HammerCLI::Output::Adapter
|
|
6
6
|
[]
|
7
7
|
end
|
8
8
|
|
9
|
-
def initialize(context={}, formatters={})
|
9
|
+
def initialize(context={}, formatters={}, filters = {})
|
10
10
|
context[:verbosity] ||= HammerCLI::V_VERBOSE
|
11
11
|
@context = context
|
12
12
|
@formatters = HammerCLI::Output::Formatters::FormatterLibrary.new(filter_formatters(formatters))
|
13
13
|
@paginate_by_default = true
|
14
|
+
@filters = filters
|
14
15
|
end
|
15
16
|
|
16
17
|
def paginate_by_default?
|
@@ -41,10 +42,14 @@ module HammerCLI::Output::Adapter
|
|
41
42
|
raise NotImplementedError
|
42
43
|
end
|
43
44
|
|
45
|
+
def reset_context
|
46
|
+
@context.delete(:fields)
|
47
|
+
end
|
48
|
+
|
44
49
|
protected
|
45
50
|
|
46
|
-
def
|
47
|
-
HammerCLI::Output::FieldFilter.new
|
51
|
+
def filter_fields(fields)
|
52
|
+
HammerCLI::Output::FieldFilter.new(fields, field_filters)
|
48
53
|
end
|
49
54
|
|
50
55
|
def self.data_for_field(field, record)
|
@@ -71,17 +76,25 @@ module HammerCLI::Output::Adapter
|
|
71
76
|
$stdout
|
72
77
|
end
|
73
78
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
field.display?(field_data)
|
81
|
-
end
|
79
|
+
def field_filters
|
80
|
+
{
|
81
|
+
classes_filter: classes_filter,
|
82
|
+
sets_filter: sets_filter
|
83
|
+
}.merge(@filters) do |_, old_filter, new_filter|
|
84
|
+
old_filter + new_filter
|
82
85
|
end
|
83
86
|
end
|
84
87
|
|
88
|
+
def classes_filter
|
89
|
+
return [] if @context[:show_ids]
|
90
|
+
|
91
|
+
[Fields::Id]
|
92
|
+
end
|
93
|
+
|
94
|
+
def sets_filter
|
95
|
+
@context[:fields] || ['DEFAULT']
|
96
|
+
end
|
97
|
+
|
85
98
|
private
|
86
99
|
|
87
100
|
def filter_formatters(formatters_map)
|
@@ -94,6 +107,5 @@ module HammerCLI::Output::Adapter
|
|
94
107
|
map
|
95
108
|
end
|
96
109
|
end
|
97
|
-
|
98
110
|
end
|
99
111
|
end
|
@@ -21,18 +21,12 @@ module HammerCLI::Output::Adapter
|
|
21
21
|
|
22
22
|
protected
|
23
23
|
|
24
|
-
def field_filter
|
25
|
-
filtered = []
|
26
|
-
filtered << Fields::Id unless @context[:show_ids]
|
27
|
-
HammerCLI::Output::FieldFilter.new(filtered)
|
28
|
-
end
|
29
|
-
|
30
24
|
def render_fields(fields, data)
|
31
|
-
output =
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
25
|
+
output = ''
|
26
|
+
fields = filter_fields(fields).filter_by_classes
|
27
|
+
.filter_by_sets
|
28
|
+
.filter_by_data(data)
|
29
|
+
.filtered_fields
|
36
30
|
label_width = label_width(fields)
|
37
31
|
|
38
32
|
fields.collect do |field|
|
@@ -126,7 +126,7 @@ module HammerCLI::Output::Adapter
|
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
|
-
def initialize(context={}, formatters={})
|
129
|
+
def initialize(context = {}, formatters = {}, filters = {})
|
130
130
|
super
|
131
131
|
@paginate_by_default = false
|
132
132
|
end
|
@@ -148,7 +148,11 @@ module HammerCLI::Output::Adapter
|
|
148
148
|
end
|
149
149
|
|
150
150
|
def print_collection(fields, collection)
|
151
|
-
fields =
|
151
|
+
fields = filter_fields(fields).filter_by_classes
|
152
|
+
.filter_by_sets
|
153
|
+
.filter_by_data(collection.first,
|
154
|
+
compact_only: true)
|
155
|
+
.filtered_fields
|
152
156
|
rows = row_data(fields, collection)
|
153
157
|
# get headers using columns heuristic
|
154
158
|
headers = rows.map{ |r| Cell.headers(r, @context) }.max_by{ |headers| headers.size }
|
@@ -198,7 +202,7 @@ module HammerCLI::Output::Adapter
|
|
198
202
|
end
|
199
203
|
|
200
204
|
def default_headers(fields)
|
201
|
-
fields.
|
205
|
+
fields.map(&:label)
|
202
206
|
end
|
203
207
|
|
204
208
|
end
|
@@ -21,9 +21,11 @@ module HammerCLI::Output::Adapter
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def print_collection(all_fields, collection)
|
24
|
-
fields =
|
25
|
-
|
26
|
-
|
24
|
+
fields = filter_fields(all_fields).filter_by_classes
|
25
|
+
.filter_by_sets
|
26
|
+
.filter_by_data(collection.first,
|
27
|
+
compact_only: true)
|
28
|
+
.filtered_fields
|
27
29
|
formatted_collection = format_values(fields, collection)
|
28
30
|
# calculate hash of column widths (label -> width)
|
29
31
|
widths = calculate_widths(fields, formatted_collection)
|
@@ -63,6 +65,10 @@ module HammerCLI::Output::Adapter
|
|
63
65
|
|
64
66
|
protected
|
65
67
|
|
68
|
+
def classes_filter
|
69
|
+
super << Fields::ContainerField
|
70
|
+
end
|
71
|
+
|
66
72
|
def normalize_column(width, value)
|
67
73
|
value = value.to_s
|
68
74
|
padding = width - HammerCLI::Output::Utils.real_length(value)
|
@@ -103,12 +109,6 @@ module HammerCLI::Output::Adapter
|
|
103
109
|
width
|
104
110
|
end
|
105
111
|
|
106
|
-
def field_filter
|
107
|
-
filtered = [Fields::ContainerField]
|
108
|
-
filtered << Fields::Id unless @context[:show_ids]
|
109
|
-
HammerCLI::Output::FieldFilter.new(filtered)
|
110
|
-
end
|
111
|
-
|
112
112
|
private
|
113
113
|
|
114
114
|
def max_width_for(field)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module HammerCLI::Output::Adapter
|
2
2
|
class TreeStructure < Abstract
|
3
|
-
|
4
|
-
def initialize(context={}, formatters={})
|
3
|
+
def initialize(context = {}, formatters = {}, filters = {})
|
5
4
|
super
|
6
5
|
@paginate_by_default = false
|
7
6
|
end
|
@@ -34,15 +33,11 @@ module HammerCLI::Output::Adapter
|
|
34
33
|
|
35
34
|
protected
|
36
35
|
|
37
|
-
def field_filter
|
38
|
-
filtered = []
|
39
|
-
filtered << Fields::Id unless @context[:show_ids]
|
40
|
-
HammerCLI::Output::FieldFilter.new(filtered)
|
41
|
-
end
|
42
|
-
|
43
36
|
def render_fields(fields, data)
|
44
|
-
fields =
|
45
|
-
|
37
|
+
fields = filter_fields(fields).filter_by_classes
|
38
|
+
.filter_by_sets
|
39
|
+
.filter_by_data(data)
|
40
|
+
.filtered_fields
|
46
41
|
fields.reduce({}) do |hash, field|
|
47
42
|
field_data = data_for_field(field, data)
|
48
43
|
next unless field.display?(field_data)
|
@@ -19,6 +19,14 @@ module HammerCLI::Output
|
|
19
19
|
@fields[field_index(field_id)]
|
20
20
|
end
|
21
21
|
|
22
|
+
def update_field_sets(set_names, field_ids)
|
23
|
+
set_names = [set_names] unless set_names.is_a?(Array)
|
24
|
+
field_ids = [field_ids] unless field_ids.is_a?(Array)
|
25
|
+
field_ids.each do |field_id|
|
26
|
+
find_field(field_id).sets = find_field(field_id).sets.concat(set_names).uniq
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
22
30
|
def insert(mode, field_id, fields = nil, &block)
|
23
31
|
definition = self.class.new
|
24
32
|
definition.append(fields, &block)
|
@@ -46,8 +54,84 @@ module HammerCLI::Output
|
|
46
54
|
@fields.empty?
|
47
55
|
end
|
48
56
|
|
57
|
+
def field_sets
|
58
|
+
nested_fields_sets(@fields).uniq.sort
|
59
|
+
end
|
60
|
+
|
61
|
+
def sets_table
|
62
|
+
fields_col_size = max_label_length || _('Fields').size
|
63
|
+
fields_col = normalize_column(fields_col_size, _('Fields'), centralize: true)
|
64
|
+
fields_col += ' ' unless (fields_col_size - fields_col.size).zero?
|
65
|
+
header_bits = [fields_col]
|
66
|
+
hline_bits = ['-' * fields_col_size]
|
67
|
+
field_sets.map do |set|
|
68
|
+
header_bits << normalize_column(set.size, set)
|
69
|
+
hline_bits << '-' * set.size
|
70
|
+
end
|
71
|
+
rows_bits = fields_row(@fields, field_sets, fields_col_size)
|
72
|
+
line = "+-#{hline_bits.join('-+-')}-+\n"
|
73
|
+
table = line
|
74
|
+
table += "| #{header_bits.join(' | ')} |\n"
|
75
|
+
table += line
|
76
|
+
table += "#{rows_bits.join("\n")}\n"
|
77
|
+
table += line
|
78
|
+
table
|
79
|
+
end
|
80
|
+
|
49
81
|
private
|
50
82
|
|
83
|
+
def max_label_length
|
84
|
+
field_labels(@fields, full_labels: true).map(&:size).max
|
85
|
+
end
|
86
|
+
|
87
|
+
def normalize_column(width, col, centralize: false)
|
88
|
+
padding = width - HammerCLI::Output::Utils.real_length(col)
|
89
|
+
if padding >= 0
|
90
|
+
if centralize
|
91
|
+
padding /= 2
|
92
|
+
col.prepend(' ' * padding)
|
93
|
+
end
|
94
|
+
col += (' ' * padding)
|
95
|
+
else
|
96
|
+
col, real_len = HammerCLI::Output::Utils.real_truncate(col, width - 3)
|
97
|
+
col += '...'
|
98
|
+
col += ' ' if real_len < (width - 3)
|
99
|
+
end
|
100
|
+
col
|
101
|
+
end
|
102
|
+
|
103
|
+
def fields_row(fields, sets, fields_col_size)
|
104
|
+
fields.each_with_object([]) do |field, rows|
|
105
|
+
next rows << fields_row(field.fields, sets, fields_col_size) if field.respond_to?(:fields)
|
106
|
+
|
107
|
+
row = [normalize_column(fields_col_size, field.full_label)]
|
108
|
+
sets.each do |set|
|
109
|
+
mark = field.sets.include?(set) ? 'x' : ' '
|
110
|
+
column = normalize_column(set.size, mark, centralize: true)
|
111
|
+
column += ' ' unless (set.size - column.size).zero?
|
112
|
+
row << column
|
113
|
+
end
|
114
|
+
rows << "| #{row.join(' | ')} |"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def field_labels(fields, full_labels: false)
|
119
|
+
fields.each_with_object([]) do |field, labels|
|
120
|
+
label = full_labels ? field.full_label : field.label
|
121
|
+
next labels << label unless field.respond_to?(:fields)
|
122
|
+
|
123
|
+
labels.concat(field_labels(field.fields, full_labels: full_labels))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def nested_fields_sets(fields)
|
128
|
+
fields.map do |field|
|
129
|
+
next field.sets unless field.respond_to?(:fields)
|
130
|
+
|
131
|
+
nested_fields_sets(field.fields)
|
132
|
+
end.flatten
|
133
|
+
end
|
134
|
+
|
51
135
|
def field_index(field_id)
|
52
136
|
index = @fields.find_index do |f|
|
53
137
|
f.match_id?(field_id)
|
@@ -1,21 +1,84 @@
|
|
1
1
|
module HammerCLI::Output
|
2
|
-
|
3
2
|
class FieldFilter
|
3
|
+
attr_reader :fields, :filtered_fields
|
4
|
+
attr_accessor :classes_filter, :sets_filter
|
5
|
+
|
6
|
+
def initialize(fields = [], filters = {})
|
7
|
+
self.fields = fields
|
8
|
+
@classes_filter = filters[:classes_filter] || []
|
9
|
+
@sets_filter = filters[:sets_filter] || []
|
10
|
+
end
|
4
11
|
|
5
|
-
def
|
6
|
-
@
|
12
|
+
def fields=(fields)
|
13
|
+
@fields = fields || []
|
14
|
+
@filtered_fields = @fields.dup
|
7
15
|
end
|
8
16
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
17
|
+
def filter_by_classes(classes = nil)
|
18
|
+
classes ||= @classes_filter
|
19
|
+
classes.each do |cls|
|
20
|
+
@filtered_fields.reject! do |f|
|
13
21
|
f.is_a? cls
|
14
22
|
end
|
15
23
|
end
|
16
|
-
|
24
|
+
self
|
17
25
|
end
|
18
26
|
|
19
|
-
|
27
|
+
def filter_by_sets(sets = nil)
|
28
|
+
sets ||= @sets_filter
|
29
|
+
return self if sets.empty?
|
30
|
+
|
31
|
+
set_names, labels = resolve_set_names(sets)
|
32
|
+
deep_filter(@filtered_fields, set_names, labels)
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def filter_by_data(data, compact_only: false)
|
37
|
+
@filtered_fields = displayable_fields(@filtered_fields,
|
38
|
+
data,
|
39
|
+
compact_only: compact_only)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def deep_filter(fields, set_names, labels)
|
46
|
+
fields.select! do |f|
|
47
|
+
allowed = include_by_label?(labels, f.full_label.downcase)
|
48
|
+
allowed ||= (f.sets & set_names).any?
|
49
|
+
deep_filter(f.fields, set_names, labels) if f.respond_to?(:fields)
|
50
|
+
allowed
|
51
|
+
end
|
52
|
+
end
|
20
53
|
|
54
|
+
def displayable_fields(fields, record, compact_only: false)
|
55
|
+
fields.select do |field|
|
56
|
+
field_data = HammerCLI::Output::Adapter::Abstract.data_for_field(
|
57
|
+
field, record
|
58
|
+
)
|
59
|
+
if compact_only && !field_data.is_a?(HammerCLI::Output::DataMissing)
|
60
|
+
true
|
61
|
+
else
|
62
|
+
field.display?(field_data)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def include_by_label?(labels, label)
|
68
|
+
labels.any? do |l|
|
69
|
+
l.start_with?("#{label}/") || label.match(%r{^#{l.gsub(/\*/, '.*')}(|\/.*)$})
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def resolve_set_names(sets)
|
74
|
+
set_names = []
|
75
|
+
labels = []
|
76
|
+
sets.each do |name|
|
77
|
+
next set_names << name if name.upcase == name
|
78
|
+
|
79
|
+
labels << name.downcase
|
80
|
+
end
|
81
|
+
[set_names, labels]
|
82
|
+
end
|
83
|
+
end
|
21
84
|
end
|
@@ -1,16 +1,17 @@
|
|
1
1
|
require 'hammer_cli/output/dsl'
|
2
2
|
|
3
3
|
module Fields
|
4
|
-
|
5
4
|
class Field
|
6
5
|
attr_reader :path
|
7
|
-
|
6
|
+
attr_writer :sets
|
7
|
+
attr_accessor :label, :parent
|
8
8
|
|
9
9
|
def initialize(options={})
|
10
10
|
@hide_blank = options[:hide_blank].nil? ? false : options[:hide_blank]
|
11
11
|
@hide_missing = options[:hide_missing].nil? ? true : options[:hide_missing]
|
12
12
|
@path = options[:path] || []
|
13
13
|
@label = options[:label]
|
14
|
+
@sets = options[:sets]
|
14
15
|
@options = options
|
15
16
|
end
|
16
17
|
|
@@ -30,6 +31,15 @@ module Fields
|
|
30
31
|
@hide_missing
|
31
32
|
end
|
32
33
|
|
34
|
+
def full_label
|
35
|
+
return @label.to_s if @parent.nil?
|
36
|
+
"#{@parent.full_label}/#{@label}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def sets
|
40
|
+
@sets || inherited_sets || default_sets
|
41
|
+
end
|
42
|
+
|
33
43
|
def display?(value)
|
34
44
|
if value.is_a?(HammerCLI::Output::DataMissing)
|
35
45
|
!hide_missing?
|
@@ -44,6 +54,16 @@ module Fields
|
|
44
54
|
@options
|
45
55
|
end
|
46
56
|
|
57
|
+
protected
|
58
|
+
|
59
|
+
def inherited_sets
|
60
|
+
return nil if @parent.nil?
|
61
|
+
@parent.sets
|
62
|
+
end
|
63
|
+
|
64
|
+
def default_sets
|
65
|
+
%w[DEFAULT ALL]
|
66
|
+
end
|
47
67
|
end
|
48
68
|
|
49
69
|
|
@@ -53,7 +73,7 @@ module Fields
|
|
53
73
|
super(options)
|
54
74
|
dsl = HammerCLI::Output::Dsl.new
|
55
75
|
dsl.build &block if block_given?
|
56
|
-
|
76
|
+
dsl.fields.each { |f| f.parent = self }
|
57
77
|
self.output_definition.append dsl.fields
|
58
78
|
end
|
59
79
|
|
@@ -25,6 +25,7 @@ module HammerCLI::Output
|
|
25
25
|
|
26
26
|
def print_record(definition, record)
|
27
27
|
adapter.print_record(definition.fields, record) if appropriate_verbosity?(:record)
|
28
|
+
adapter.reset_context
|
28
29
|
end
|
29
30
|
|
30
31
|
def print_collection(definition, collection)
|
@@ -32,6 +33,7 @@ module HammerCLI::Output
|
|
32
33
|
collection = HammerCLI::Output::RecordCollection.new([collection].flatten(1))
|
33
34
|
end
|
34
35
|
adapter.print_collection(definition.fields, collection) if appropriate_verbosity?(:collection)
|
36
|
+
adapter.reset_context
|
35
37
|
end
|
36
38
|
|
37
39
|
def adapter
|
@@ -14,7 +14,7 @@ module HammerCLI
|
|
14
14
|
|
15
15
|
def get_options(uri = nil)
|
16
16
|
ssl_options = {}
|
17
|
-
for sslopt in [:ssl_ca_file, :ssl_ca_path, :verify_ssl] do
|
17
|
+
for sslopt in [:ssl_ca_file, :ssl_ca_path, :verify_ssl, :ssl_version] do
|
18
18
|
ssloptval = read_ssl_option(sslopt)
|
19
19
|
ssl_options[sslopt] = ssloptval unless ssloptval.nil?
|
20
20
|
end
|
data/lib/hammer_cli/version.rb
CHANGED
data/test/unit/abstract_test.rb
CHANGED
@@ -270,6 +270,7 @@ describe HammerCLI::AbstractCommand do
|
|
270
270
|
option "--test", "TEST", "Test option"
|
271
271
|
option "--test-format", "TEST_FORMAT", "Test option with a formatter",
|
272
272
|
:format => HammerCLI::Options::Normalizers::List.new
|
273
|
+
use_option :fields
|
273
274
|
end
|
274
275
|
|
275
276
|
it "should create instances of hammer options" do
|
@@ -282,6 +283,10 @@ describe HammerCLI::AbstractCommand do
|
|
282
283
|
opt.value_formatter.kind_of?(HammerCLI::Options::Normalizers::List).must_equal true
|
283
284
|
end
|
284
285
|
|
286
|
+
it 'should allow using of predefined options' do
|
287
|
+
opt = TestOptionCmd.find_option('--fields')
|
288
|
+
opt.is_a?(HammerCLI::Options::OptionDefinition).must_equal true
|
289
|
+
end
|
285
290
|
end
|
286
291
|
|
287
292
|
describe "#options" do
|
@@ -257,4 +257,24 @@ describe HammerCLI::Output::Definition do
|
|
257
257
|
definition.at(path).must_equal label_field.output_definition
|
258
258
|
end
|
259
259
|
end
|
260
|
+
|
261
|
+
describe 'sets_table' do
|
262
|
+
it 'prints a table with fields and sets ' do
|
263
|
+
cont_field = Fields::ContainerField.new(id: :id1, label: 'cf', sets: ['SET']) do
|
264
|
+
field :a, 'abc', Fields::Field
|
265
|
+
field :b, 'bca', Fields::Field
|
266
|
+
end
|
267
|
+
definition.fields += [new_field, cont_field]
|
268
|
+
|
269
|
+
sets_table = "+----------+-----+---------+-----+
|
270
|
+
| Fields | ALL | DEFAULT | SET |
|
271
|
+
+----------+-----+---------+-----+
|
272
|
+
| newfield | x | x | |
|
273
|
+
| cf/abc | | | x |
|
274
|
+
| cf/bca | | | x |
|
275
|
+
+----------+-----+---------+-----+\n"
|
276
|
+
|
277
|
+
definition.sets_table.must_equal sets_table
|
278
|
+
end
|
279
|
+
end
|
260
280
|
end
|
@@ -1,27 +1,72 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '../test_helper')
|
2
2
|
|
3
3
|
describe HammerCLI::Output::FieldFilter do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
let(:fields) do
|
5
|
+
[
|
6
|
+
Fields::Field.new(:label => 'field', :hide_blank => true),
|
7
|
+
Fields::Collection.new(:label => 'collection'),
|
8
|
+
Fields::Id.new(:label => 'id', :sets => ['THIN'])
|
9
|
+
]
|
10
|
+
end
|
11
|
+
let(:container_fields) do
|
12
|
+
fields + [
|
13
|
+
Fields::ContainerField.new(:label => 'container') do
|
14
|
+
field :first, 'first'
|
15
|
+
field :second, 'second', Fields::ContainerField do
|
16
|
+
field :nested, 'nested'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
]
|
20
|
+
end
|
10
21
|
let(:field_labels) { fields.map(&:label).sort }
|
11
22
|
|
12
|
-
it
|
13
|
-
f = HammerCLI::Output::FieldFilter.new
|
14
|
-
f.
|
23
|
+
it 'lets all fields go by default' do
|
24
|
+
f = HammerCLI::Output::FieldFilter.new(fields)
|
25
|
+
f.filtered_fields.map(&:label).sort.must_equal ['field', 'collection', 'id'].sort
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'filters fields by class' do
|
29
|
+
f = HammerCLI::Output::FieldFilter.new(fields, classes_filter: [Fields::Id])
|
30
|
+
f.filter_by_classes.filtered_fields.map(&:label).sort.must_equal ['field', 'collection'].sort
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'filters fields by superclass' do
|
34
|
+
f = HammerCLI::Output::FieldFilter.new(fields, classes_filter: [Fields::ContainerField])
|
35
|
+
f.filter_by_classes.filtered_fields.map(&:label).sort.must_equal ['field', 'id'].sort
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'filters fields by sets' do
|
39
|
+
f = HammerCLI::Output::FieldFilter.new(fields, sets_filter: ['THIN'])
|
40
|
+
f.filter_by_sets.filtered_fields.map(&:label).must_equal ['id']
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'filters fields by sets with labels' do
|
44
|
+
f = HammerCLI::Output::FieldFilter.new(fields, sets_filter: ['THIN', 'field'])
|
45
|
+
f.filter_by_sets.filtered_fields.map(&:label).sort.must_equal ['field', 'id'].sort
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'filters by full labels' do
|
49
|
+
f = HammerCLI::Output::FieldFilter.new(container_fields, sets_filter: ['container/first'])
|
50
|
+
f.filter_by_sets.filtered_fields.first.fields.map(&:label).must_equal ['first']
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'filters by superclass labels' do
|
54
|
+
f = HammerCLI::Output::FieldFilter.new(container_fields, sets_filter: ['container'])
|
55
|
+
f.filter_by_sets.filtered_fields.first.fields.map(&:label).must_equal ['first', 'second']
|
15
56
|
end
|
16
57
|
|
17
|
-
it
|
18
|
-
f = HammerCLI::Output::FieldFilter.new([
|
19
|
-
f.
|
58
|
+
it 'filters by labels with wildcards' do
|
59
|
+
f = HammerCLI::Output::FieldFilter.new(container_fields, sets_filter: ['container/f*'])
|
60
|
+
f.filter_by_sets.filtered_fields.first.fields.map(&:label).must_equal ['first']
|
20
61
|
end
|
21
62
|
|
22
|
-
it
|
23
|
-
f = HammerCLI::Output::FieldFilter.new([Fields::
|
24
|
-
f.
|
63
|
+
it 'allows chained filtering' do
|
64
|
+
f = HammerCLI::Output::FieldFilter.new(fields, sets_filter: ['THIN'], classes_filter: [Fields::Id])
|
65
|
+
f.filter_by_classes.filter_by_sets.filtered_fields.map(&:label).must_equal []
|
25
66
|
end
|
26
67
|
|
68
|
+
it 'filters fields by data' do
|
69
|
+
f = HammerCLI::Output::FieldFilter.new(fields)
|
70
|
+
f.filter_by_data(nil).filtered_fields.map(&:label).sort.must_equal ['id', 'collection'].sort
|
71
|
+
end
|
27
72
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hammer_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Bačovský
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-08-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: clamp
|
@@ -242,6 +242,7 @@ files:
|
|
242
242
|
- lib/hammer_cli/options/option_collector.rb
|
243
243
|
- lib/hammer_cli/options/option_definition.rb
|
244
244
|
- lib/hammer_cli/options/option_processor.rb
|
245
|
+
- lib/hammer_cli/options/predefined.rb
|
245
246
|
- lib/hammer_cli/options/processor_list.rb
|
246
247
|
- lib/hammer_cli/options/sources/base.rb
|
247
248
|
- lib/hammer_cli/options/sources/command_line.rb
|