hammer_cli 0.17.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|