hammer_cli 0.8.0 → 0.9.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 +2 -2
- data/doc/release_notes.md +15 -0
- data/lib/hammer_cli/abstract.rb +22 -31
- data/lib/hammer_cli/apipie.rb +1 -0
- data/lib/hammer_cli/apipie/api_connection.rb +27 -0
- data/lib/hammer_cli/apipie/option_builder.rb +3 -3
- data/lib/hammer_cli/apipie/resource.rb +1 -26
- data/lib/hammer_cli/connection.rb +12 -11
- data/lib/hammer_cli/context.rb +4 -2
- data/lib/hammer_cli/exception_handler.rb +1 -1
- data/lib/hammer_cli/help/builder.rb +58 -0
- data/lib/hammer_cli/help/text_builder.rb +79 -0
- data/lib/hammer_cli/logger.rb +24 -21
- data/lib/hammer_cli/options/normalizers.rb +13 -2
- data/lib/hammer_cli/output/adapter/table.rb +2 -0
- data/lib/hammer_cli/output/formatters.rb +1 -1
- data/lib/hammer_cli/output/utils.rb +43 -0
- data/lib/hammer_cli/table_print/column.rb +19 -0
- data/lib/hammer_cli/table_print/formatter.rb +18 -0
- data/lib/hammer_cli/utils.rb +5 -1
- data/lib/hammer_cli/validator.rb +12 -2
- data/lib/hammer_cli/version.rb +1 -1
- data/locale/ca/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/ca/hammer-cli.edit.po +26 -15
- data/locale/ca/hammer-cli.po +22 -6
- data/locale/de/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/de/hammer-cli.edit.po +32 -20
- data/locale/de/hammer-cli.po +22 -6
- data/locale/en/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/en/hammer-cli.edit.po +47 -29
- data/locale/en/hammer-cli.po +21 -5
- data/locale/en_GB/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/en_GB/hammer-cli.edit.po +28 -17
- data/locale/en_GB/hammer-cli.po +22 -6
- data/locale/es/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/es/hammer-cli.edit.po +28 -17
- data/locale/es/hammer-cli.po +22 -6
- data/locale/fr/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/fr/hammer-cli.edit.po +28 -17
- data/locale/fr/hammer-cli.po +22 -6
- data/locale/hammer-cli.pot +49 -31
- data/locale/it/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/it/hammer-cli.edit.po +23 -12
- data/locale/it/hammer-cli.po +22 -6
- data/locale/ja/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/ja/hammer-cli.edit.po +28 -17
- data/locale/ja/hammer-cli.po +22 -6
- data/locale/ko/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/ko/hammer-cli.edit.po +27 -16
- data/locale/ko/hammer-cli.po +22 -6
- data/locale/pt_BR/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/pt_BR/hammer-cli.edit.po +30 -19
- data/locale/pt_BR/hammer-cli.po +22 -6
- data/locale/ru/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/ru/hammer-cli.edit.po +28 -17
- data/locale/ru/hammer-cli.po +22 -6
- data/locale/zh_CN/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/zh_CN/hammer-cli.edit.po +28 -17
- data/locale/zh_CN/hammer-cli.po +22 -6
- data/locale/zh_TW/LC_MESSAGES/hammer-cli.mo +0 -0
- data/locale/zh_TW/hammer-cli.edit.po +27 -16
- data/locale/zh_TW/hammer-cli.po +22 -6
- data/man/hammer.1.gz +0 -0
- data/test/functional/help_test.rb +87 -0
- data/test/test_helper.rb +6 -0
- data/test/unit/abstract_test.rb +9 -0
- data/test/unit/apipie/api_connection_test.rb +38 -0
- data/test/unit/apipie/command_test.rb +6 -46
- data/test/unit/connection_test.rb +45 -54
- data/test/unit/exception_handler_test.rb +1 -1
- data/test/unit/fixtures/apipie/documented.json +1 -1
- data/test/unit/help/builder_test.rb +57 -0
- data/test/unit/help/text_builder_test.rb +140 -0
- data/test/unit/logger_test.rb +19 -0
- data/test/unit/options/normalizers_test.rb +12 -0
- data/test/unit/output/adapter/table_test.rb +101 -13
- data/test/unit/output/formatters_test.rb +6 -3
- data/test/unit/validator_test.rb +31 -2
- metadata +207 -179
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c28ad069fcd115c4b6f01ad2b5d0b2cdf8b3cbd
|
4
|
+
data.tar.gz: 2f9a543685a78f837bd1d1b657702c745c2ae0ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e641575e3077ce1a854ba6f6e7bfc96d9b03c634a56ee09dcb8d5d486db188aa2bd5f4e7856dcc4f30c08b9ab6630d73397e3a9726db833064d11fc8afdfeecb
|
7
|
+
data.tar.gz: 287e3b3f607305f302ff58d6c74c6cc871b46d627fd32e0392a2443a2f516561934344419ab9333f3083804a15996c4d9f4695b0eeb32357b8fb022416fdb2e3
|
@@ -6,6 +6,8 @@
|
|
6
6
|
# :per_page: 20
|
7
7
|
# Location of shell history file
|
8
8
|
:history_file: '~/.hammer/history'
|
9
|
+
# Mark translated strings with X characters (for developers)
|
10
|
+
#:mark_translated: false
|
9
11
|
|
10
12
|
|
11
13
|
# Enable/disable color output of logger in Clamp commands
|
@@ -29,5 +31,3 @@
|
|
29
31
|
# Log record pattern (logging gem syntax)
|
30
32
|
#:log_pattern: '[%5l %d %c] %m'
|
31
33
|
|
32
|
-
# Mark translated strings with X characters (for developers)
|
33
|
-
#:mark_translated: false
|
data/doc/release_notes.md
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
Release notes
|
2
2
|
=============
|
3
3
|
|
4
|
+
### 0.9.0 (2016-12-15)
|
5
|
+
* Double quotes in list type params ([#17180](http://projects.theforeman.org/issues/17180))
|
6
|
+
* API connection moved to context ([PR #227](https://github.com/theforeman/hammer-cli/pull/227)) ([#8016](http://projects.theforeman.org/issues/8016))
|
7
|
+
* Log related error messages to stderr ([#17508](http://projects.theforeman.org/issues/17508))
|
8
|
+
* Respect special chars width ([#3520](http://projects.theforeman.org/issues/3520))
|
9
|
+
* Properly detect booleans ([#17021](http://projects.theforeman.org/issues/17021))
|
10
|
+
* Support for additional text in help ([PR #222](https://github.com/theforeman/hammer-cli/pull/222)) ([#16408](http://projects.theforeman.org/issues/16408))
|
11
|
+
* Permit clamp 1.1 and higher, compatibility restored ([PR #224](https://github.com/theforeman/hammer-cli/pull/224)) ([#16973](http://projects.theforeman.org/issues/16973))
|
12
|
+
* Pin clamp to 1.0.x ([PR #223](https://github.com/theforeman/hammer-cli/pull/223)) ([#16973](http://projects.theforeman.org/issues/16973))
|
13
|
+
* Allow multiple opt validation blocks ([#16811](http://projects.theforeman.org/issues/16811))
|
14
|
+
* Check for nil default values ([PR #221](https://github.com/theforeman/hammer-cli/pull/221)) ([#16822](http://projects.theforeman.org/issues/16822))
|
15
|
+
* Use defaults in validators ([#16631](http://projects.theforeman.org/issues/16631))
|
16
|
+
* Boolean formatter prints "no" for 0 ([PR #219](https://github.com/theforeman/hammer-cli/pull/219)) ([#16406](http://projects.theforeman.org/issues/16406))
|
17
|
+
* Fix mark_translated in cli_config ([PR #216](https://github.com/theforeman/hammer-cli/pull/216)) ([#16470](http://projects.theforeman.org/issues/16470))
|
18
|
+
|
4
19
|
### 0.8.0 (2016-09-01)
|
5
20
|
* Fix tests with rest-client >= 2.0.0 ([#16404](http://projects.theforeman.org/issues/16404))
|
6
21
|
* Add missing apostrophe in error message ([#16316](http://projects.theforeman.org/issues/16316))
|
data/lib/hammer_cli/abstract.rb
CHANGED
@@ -4,6 +4,8 @@ require 'hammer_cli/options/option_definition'
|
|
4
4
|
require 'hammer_cli/clamp'
|
5
5
|
require 'hammer_cli/subcommand'
|
6
6
|
require 'hammer_cli/options/matcher'
|
7
|
+
require 'hammer_cli/help/builder'
|
8
|
+
require 'hammer_cli/help/text_builder'
|
7
9
|
require 'logging'
|
8
10
|
module HammerCLI
|
9
11
|
|
@@ -11,7 +13,7 @@ module HammerCLI
|
|
11
13
|
include HammerCLI::Subcommand
|
12
14
|
|
13
15
|
class << self
|
14
|
-
attr_accessor :
|
16
|
+
attr_accessor :validation_blocks
|
15
17
|
end
|
16
18
|
|
17
19
|
def adapter
|
@@ -39,11 +41,14 @@ module HammerCLI
|
|
39
41
|
end
|
40
42
|
|
41
43
|
def self.validate_options(&block)
|
42
|
-
self.
|
44
|
+
self.validation_blocks ||= []
|
45
|
+
self.validation_blocks << block
|
43
46
|
end
|
44
47
|
|
45
48
|
def validate_options
|
46
|
-
|
49
|
+
if self.class.validation_blocks && self.class.validation_blocks.any?
|
50
|
+
self.class.validation_blocks.each { |validation_block| validator.run(&validation_block) }
|
51
|
+
end
|
47
52
|
end
|
48
53
|
|
49
54
|
def exception_handler
|
@@ -60,38 +65,24 @@ module HammerCLI
|
|
60
65
|
context[:path][-2]
|
61
66
|
end
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
29
|
67
|
-
end
|
68
|
-
|
69
|
-
def add_list(heading, items)
|
70
|
-
items.sort! do |a, b|
|
71
|
-
a.help[0] <=> b.help[0]
|
72
|
-
end
|
73
|
-
items.reject! {|item| item.respond_to?(:hidden?) && item.hidden?}
|
74
|
-
|
75
|
-
puts "\n#{heading}:"
|
68
|
+
def help
|
69
|
+
self.class.help(invocation_path, HammerCLI::Help::Builder.new(context[:is_tty?]))
|
70
|
+
end
|
76
71
|
|
77
|
-
|
78
|
-
|
79
|
-
label, description = item.help
|
80
|
-
label_width = label.size if label.size > label_width
|
81
|
-
end
|
72
|
+
def self.help(invocation_path, builder = HammerCLI::Help::Builder.new)
|
73
|
+
super(invocation_path, builder)
|
82
74
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
label = ''
|
88
|
-
end
|
89
|
-
end
|
75
|
+
if @help_extension_block
|
76
|
+
help_extension = HammerCLI::Help::TextBuilder.new(builder.richtext)
|
77
|
+
@help_extension_block.call(help_extension)
|
78
|
+
builder.add_text(help_extension.string)
|
90
79
|
end
|
80
|
+
builder.string
|
91
81
|
end
|
92
82
|
|
93
|
-
def
|
94
|
-
|
83
|
+
def self.extend_help(&block)
|
84
|
+
# We save the block for execution on object level, where we can access command's context and check :is_tty? flag
|
85
|
+
@help_extension_block = block
|
95
86
|
end
|
96
87
|
|
97
88
|
def self.output(definition=nil, &block)
|
@@ -254,7 +245,7 @@ module HammerCLI
|
|
254
245
|
value
|
255
246
|
end
|
256
247
|
end
|
257
|
-
|
248
|
+
|
258
249
|
def self.inherited_output_definition
|
259
250
|
od = nil
|
260
251
|
if superclass.respond_to? :output_definition
|
data/lib/hammer_cli/apipie.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'apipie_bindings'
|
2
|
+
module HammerCLI::Apipie
|
3
|
+
class ApiConnection < HammerCLI::AbstractConnector
|
4
|
+
attr_reader :api
|
5
|
+
|
6
|
+
def initialize(params, options = {})
|
7
|
+
@logger = options[:logger]
|
8
|
+
@api = ApipieBindings::API.new(params)
|
9
|
+
if options[:reload_cache]
|
10
|
+
@api.clean_cache
|
11
|
+
@logger.debug 'Apipie cache was cleared' unless @logger.nil?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def resources
|
16
|
+
@api.resources
|
17
|
+
end
|
18
|
+
|
19
|
+
def resource(resource_name)
|
20
|
+
@api.resource(resource_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_resource?(resource_name)
|
24
|
+
@api.has_resource?(resource_name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -64,14 +64,14 @@ module HammerCLI::Apipie
|
|
64
64
|
def option_opts(param)
|
65
65
|
opts = {}
|
66
66
|
opts[:required] = true if (param.required? and require_options?)
|
67
|
-
if param.expected_type ==
|
67
|
+
if param.expected_type.to_s == 'array' || param.validator =~ /Array/i
|
68
68
|
opts[:format] = HammerCLI::Options::Normalizers::List.new
|
69
|
-
elsif param.expected_type == 'boolean' || param.validator =~ /Boolean/i
|
69
|
+
elsif param.expected_type.to_s == 'boolean' || param.validator =~ /Boolean/i
|
70
70
|
opts[:format] = HammerCLI::Options::Normalizers::Bool.new
|
71
71
|
elsif param.validator =~ /Must be one of: (.*)\./
|
72
72
|
allowed = $1.split(/,\ ?/).map { |val| val.gsub(/<[^>]*>/i,'') }
|
73
73
|
opts[:format] = HammerCLI::Options::Normalizers::Enum.new(allowed)
|
74
|
-
elsif param.expected_type == 'number' || param.validator =~ /Number/i
|
74
|
+
elsif param.expected_type.to_s == 'number' || param.validator =~ /Number/i
|
75
75
|
opts[:format] = HammerCLI::Options::Normalizers::Number.new
|
76
76
|
end
|
77
77
|
opts[:attribute_name] = HammerCLI.option_accessor_name(param.name)
|
@@ -1,21 +1,5 @@
|
|
1
1
|
require 'apipie_bindings'
|
2
2
|
module HammerCLI::Apipie
|
3
|
-
|
4
|
-
|
5
|
-
class ApipieConnector < HammerCLI::AbstractConnector
|
6
|
-
|
7
|
-
attr_reader :api
|
8
|
-
|
9
|
-
def initialize(params)
|
10
|
-
@api = ApipieBindings::API.new(params)
|
11
|
-
if HammerCLI::Settings.get(:_params, :reload_cache) || HammerCLI::Settings.get(:reload_cache)
|
12
|
-
@api.clean_cache
|
13
|
-
Logging.logger['Init'].debug 'Apipie cache was cleared'
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
3
|
module Resource
|
20
4
|
|
21
5
|
def self.included(base)
|
@@ -44,12 +28,6 @@ module HammerCLI::Apipie
|
|
44
28
|
{}
|
45
29
|
end
|
46
30
|
|
47
|
-
def connection_options
|
48
|
-
{
|
49
|
-
:connector => HammerCLI::Apipie::ApipieConnector
|
50
|
-
}
|
51
|
-
end
|
52
|
-
|
53
31
|
def connection_name(resource_class)
|
54
32
|
:apipie
|
55
33
|
end
|
@@ -70,10 +48,7 @@ module HammerCLI::Apipie
|
|
70
48
|
|
71
49
|
def resource(resource=nil, action=nil)
|
72
50
|
unless resource.nil?
|
73
|
-
api = HammerCLI
|
74
|
-
connection_name(resource),
|
75
|
-
resource_config,
|
76
|
-
connection_options).api
|
51
|
+
api = HammerCLI.context[:api_connection].get(connection_name(resource))
|
77
52
|
if api.has_resource?(resource)
|
78
53
|
@api_resource = api.resource(resource)
|
79
54
|
else
|
@@ -6,39 +6,40 @@ module HammerCLI
|
|
6
6
|
end
|
7
7
|
|
8
8
|
class Connection
|
9
|
+
def initialize(logger = nil)
|
10
|
+
@logger = logger
|
11
|
+
end
|
9
12
|
|
10
|
-
def
|
13
|
+
def drop(name)
|
11
14
|
connections.delete(name)
|
12
15
|
end
|
13
16
|
|
14
|
-
def
|
17
|
+
def drop_all()
|
15
18
|
connections.keys.each { |c| drop(c) }
|
16
19
|
end
|
17
20
|
|
18
|
-
def
|
21
|
+
def create(name, &create_connector_block)
|
19
22
|
unless connections[name]
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
connections[name] = connector.new(conector_params)
|
23
|
+
connector = yield
|
24
|
+
@logger.debug("Registered: #{name}") if @logger
|
25
|
+
connections[name] = connector
|
24
26
|
end
|
25
27
|
connections[name]
|
26
28
|
end
|
27
29
|
|
28
|
-
def
|
30
|
+
def exist?(name)
|
29
31
|
!get(name).nil?
|
30
32
|
end
|
31
33
|
|
32
|
-
def
|
34
|
+
def get(name)
|
33
35
|
connections[name]
|
34
36
|
end
|
35
37
|
|
36
38
|
private
|
37
39
|
|
38
|
-
def
|
40
|
+
def connections
|
39
41
|
@connections_hash ||= {}
|
40
42
|
@connections_hash
|
41
43
|
end
|
42
|
-
|
43
44
|
end
|
44
45
|
end
|
data/lib/hammer_cli/context.rb
CHANGED
@@ -3,8 +3,10 @@ require 'hammer_cli/defaults'
|
|
3
3
|
module HammerCLI
|
4
4
|
|
5
5
|
def self.context
|
6
|
-
{
|
7
|
-
:defaults => HammerCLI.defaults
|
6
|
+
@context ||= {
|
7
|
+
:defaults => HammerCLI.defaults,
|
8
|
+
:is_tty? => HammerCLI.tty?,
|
9
|
+
:api_connection => HammerCLI::Connection.new(Logging.logger['Connection'])
|
8
10
|
}
|
9
11
|
end
|
10
12
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module HammerCLI
|
2
|
+
module Help
|
3
|
+
class Builder < Clamp::Help::Builder
|
4
|
+
DEFAULT_LABEL_INDENT = 29
|
5
|
+
|
6
|
+
attr_reader :richtext
|
7
|
+
|
8
|
+
def initialize(richtext = false)
|
9
|
+
super()
|
10
|
+
@richtext = richtext
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_usage(invocation_path, usage_descriptions)
|
14
|
+
heading(Clamp.message(:usage_heading))
|
15
|
+
usage_descriptions.each do |usage|
|
16
|
+
puts " #{invocation_path} #{usage}".rstrip
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_list(heading, items)
|
21
|
+
items.sort! do |a, b|
|
22
|
+
a.help[0] <=> b.help[0]
|
23
|
+
end
|
24
|
+
items.reject! {|item| item.respond_to?(:hidden?) && item.hidden?}
|
25
|
+
|
26
|
+
puts
|
27
|
+
heading(heading)
|
28
|
+
|
29
|
+
label_width = DEFAULT_LABEL_INDENT
|
30
|
+
items.each do |item|
|
31
|
+
label, description = item.help
|
32
|
+
label_width = label.size if label.size > label_width
|
33
|
+
end
|
34
|
+
|
35
|
+
items.each do |item|
|
36
|
+
label, description = item.help
|
37
|
+
description.each_line do |line|
|
38
|
+
puts " %-#{label_width}s %s" % [label, line]
|
39
|
+
label = ''
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_text(content)
|
45
|
+
puts
|
46
|
+
puts content
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def heading(label)
|
52
|
+
label = "#{label}:"
|
53
|
+
label = HighLine.color(label, :bold) if @richtext
|
54
|
+
puts label
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module HammerCLI
|
2
|
+
module Help
|
3
|
+
class TextBuilder
|
4
|
+
INDENT_STEP = 2
|
5
|
+
LIST_INDENT = 20
|
6
|
+
|
7
|
+
def initialize(richtext = false)
|
8
|
+
@out = StringIO.new
|
9
|
+
@richtext = richtext
|
10
|
+
end
|
11
|
+
|
12
|
+
def string
|
13
|
+
@out.string
|
14
|
+
end
|
15
|
+
|
16
|
+
def text(content)
|
17
|
+
puts unless first_print?
|
18
|
+
puts content
|
19
|
+
end
|
20
|
+
|
21
|
+
def list(items)
|
22
|
+
return if items.empty?
|
23
|
+
|
24
|
+
items = normalize_list(items)
|
25
|
+
max_len = items.map { |i| i[0].to_s.length }.max
|
26
|
+
indent_size = (max_len + INDENT_STEP > LIST_INDENT) ? (max_len + INDENT_STEP) : LIST_INDENT
|
27
|
+
|
28
|
+
puts unless first_print?
|
29
|
+
items.each do |col1, col2|
|
30
|
+
# handle multiple lines in the second column
|
31
|
+
col2 = indent(col2.to_s, ' ' * indent_size).lstrip
|
32
|
+
|
33
|
+
line = "%-#{indent_size}s%s" % [col1, col2]
|
34
|
+
line.strip!
|
35
|
+
puts line
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def section(label, &block)
|
40
|
+
puts unless first_print?
|
41
|
+
heading(label)
|
42
|
+
|
43
|
+
sub_builder = TextBuilder.new(@richtext)
|
44
|
+
yield(sub_builder) if block_given?
|
45
|
+
puts indent(sub_builder.string)
|
46
|
+
end
|
47
|
+
|
48
|
+
def indent(content, indentation = nil)
|
49
|
+
indentation ||= " " * INDENT_STEP
|
50
|
+
content = content.split("\n") unless content.is_a? Array
|
51
|
+
content.map do |line|
|
52
|
+
(indentation + line).rstrip
|
53
|
+
end.join("\n")
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def heading(label)
|
59
|
+
label = "#{label}:"
|
60
|
+
label = HighLine.color(label, :bold) if @richtext
|
61
|
+
puts label
|
62
|
+
end
|
63
|
+
|
64
|
+
def puts(*args)
|
65
|
+
@out.puts(*args)
|
66
|
+
end
|
67
|
+
|
68
|
+
def first_print?
|
69
|
+
@out.size == 0
|
70
|
+
end
|
71
|
+
|
72
|
+
def normalize_list(items)
|
73
|
+
items.map do |i|
|
74
|
+
i.is_a?(Array) ? i : [i]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|