output_mode 1.5.0 → 1.7.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/.travis.yml +0 -1
- data/bin/bundle +114 -0
- data/bin/byebug +29 -0
- data/bin/coderay +29 -0
- data/bin/demo +47 -65
- data/bin/htmldiff +29 -0
- data/bin/ldiff +29 -0
- data/bin/pry +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/lib/output_mode/callable.rb +39 -11
- data/lib/output_mode/default_erb.rb +23 -1
- data/lib/output_mode/formatter.rb +146 -0
- data/lib/output_mode/formatters/index.rb +59 -0
- data/lib/output_mode/formatters/show.rb +98 -0
- data/lib/output_mode/formatters.rb +36 -0
- data/lib/output_mode/non_interactive_erb.rb +41 -0
- data/lib/output_mode/outputs/tabulated.rb +6 -1
- data/lib/output_mode/outputs/templated.rb +20 -9
- data/lib/output_mode/tldr/show.rb +23 -23
- data/lib/output_mode/version.rb +1 -1
- data/lib/output_mode.rb +1 -0
- data/output_mode.gemspec +1 -0
- metadata +29 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4beed6d9f51548ef131e475bb68aa9c6d6fe079e39672b4691566ff80ffb65c2
|
|
4
|
+
data.tar.gz: df648ec5ee82875c189123dca55bfaabfa3bf4d5ba24e160be1d0f15bb1a624b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4e19a13c9e5b0b6e3fa497aadd4d1ed59bb6297fd8063f88d7f6a01bd4dd84f801baae9be8efe46a0e42e63024824d227fa51c0f7b84ad283baec7f5960fb2f6
|
|
7
|
+
data.tar.gz: b9ea560f3b42886ca8baaa04452cc5e4557c82ddf23068b095b544f2649b425b5b30a30212d6ef7955cd1fde41c624b4f9fbe73cfc32a3a2ddb1092f6d16fd54
|
data/.travis.yml
CHANGED
data/bin/bundle
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'bundle' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "rubygems"
|
|
12
|
+
|
|
13
|
+
m = Module.new do
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def invoked_as_script?
|
|
17
|
+
File.expand_path($0) == File.expand_path(__FILE__)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def env_var_version
|
|
21
|
+
ENV["BUNDLER_VERSION"]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cli_arg_version
|
|
25
|
+
return unless invoked_as_script? # don't want to hijack other binstubs
|
|
26
|
+
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
|
27
|
+
bundler_version = nil
|
|
28
|
+
update_index = nil
|
|
29
|
+
ARGV.each_with_index do |a, i|
|
|
30
|
+
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
|
31
|
+
bundler_version = a
|
|
32
|
+
end
|
|
33
|
+
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
|
34
|
+
bundler_version = $1
|
|
35
|
+
update_index = i
|
|
36
|
+
end
|
|
37
|
+
bundler_version
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def gemfile
|
|
41
|
+
gemfile = ENV["BUNDLE_GEMFILE"]
|
|
42
|
+
return gemfile if gemfile && !gemfile.empty?
|
|
43
|
+
|
|
44
|
+
File.expand_path("../../Gemfile", __FILE__)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def lockfile
|
|
48
|
+
lockfile =
|
|
49
|
+
case File.basename(gemfile)
|
|
50
|
+
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
|
51
|
+
else "#{gemfile}.lock"
|
|
52
|
+
end
|
|
53
|
+
File.expand_path(lockfile)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def lockfile_version
|
|
57
|
+
return unless File.file?(lockfile)
|
|
58
|
+
lockfile_contents = File.read(lockfile)
|
|
59
|
+
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
|
60
|
+
Regexp.last_match(1)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def bundler_version
|
|
64
|
+
@bundler_version ||=
|
|
65
|
+
env_var_version || cli_arg_version ||
|
|
66
|
+
lockfile_version
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def bundler_requirement
|
|
70
|
+
return "#{Gem::Requirement.default}.a" unless bundler_version
|
|
71
|
+
|
|
72
|
+
bundler_gem_version = Gem::Version.new(bundler_version)
|
|
73
|
+
|
|
74
|
+
requirement = bundler_gem_version.approximate_recommendation
|
|
75
|
+
|
|
76
|
+
return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
|
|
77
|
+
|
|
78
|
+
requirement += ".a" if bundler_gem_version.prerelease?
|
|
79
|
+
|
|
80
|
+
requirement
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def load_bundler!
|
|
84
|
+
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
|
85
|
+
|
|
86
|
+
activate_bundler
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def activate_bundler
|
|
90
|
+
gem_error = activation_error_handling do
|
|
91
|
+
gem "bundler", bundler_requirement
|
|
92
|
+
end
|
|
93
|
+
return if gem_error.nil?
|
|
94
|
+
require_error = activation_error_handling do
|
|
95
|
+
require "bundler/version"
|
|
96
|
+
end
|
|
97
|
+
return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
|
98
|
+
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
|
|
99
|
+
exit 42
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def activation_error_handling
|
|
103
|
+
yield
|
|
104
|
+
nil
|
|
105
|
+
rescue StandardError, LoadError => e
|
|
106
|
+
e
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
m.load_bundler!
|
|
111
|
+
|
|
112
|
+
if m.invoked_as_script?
|
|
113
|
+
load Gem.bin_path("bundler", "bundle")
|
|
114
|
+
end
|
data/bin/byebug
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'byebug' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("byebug", "byebug")
|
data/bin/coderay
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'coderay' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("coderay", "coderay")
|
data/bin/demo
CHANGED
|
@@ -29,61 +29,50 @@ require "bundler/setup"
|
|
|
29
29
|
require "output_mode"
|
|
30
30
|
require 'erb'
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
register_callable(header: 'Simplified', verbose: false) { 'simplified visible' }
|
|
39
|
-
register_callable(header: 'Interactive', interactive: true) { 'interactive visible' }
|
|
40
|
-
register_callable(header: 'Non Interactive', interactive: false) { 'non-interactive visible' }
|
|
41
|
-
register_callable(header: 'Yes/True') { true }
|
|
42
|
-
register_callable(header: 'No/False', row_color: [:clear]) { false }
|
|
43
|
-
register_callable(header: 'Missing') { nil }
|
|
44
|
-
register_callable(header: 'Inline') do |interactive:, verbose:|
|
|
45
|
-
if interactive && verbose
|
|
46
|
-
'interactive-verbose'
|
|
47
|
-
elsif interactive
|
|
48
|
-
'interactive-simplified'
|
|
49
|
-
elsif verbose
|
|
50
|
-
'non-interactive-verbose'
|
|
32
|
+
class DemoIndex < OutputMode::Formatters::Index
|
|
33
|
+
constructor do
|
|
34
|
+
register(header: 'Integer', row_color: [:yellow, :bold]) { |i| i }
|
|
35
|
+
register(header: 'Standard', header_color: [:strikethrough] ) { 'always visible' }
|
|
36
|
+
if verbose?
|
|
37
|
+
register(header: 'Verbose') { 'verbose visible' }
|
|
51
38
|
else
|
|
52
|
-
'
|
|
39
|
+
register(header: 'Simplified') { 'simplified visible' }
|
|
53
40
|
end
|
|
41
|
+
if interactive?
|
|
42
|
+
register(header: 'Interactive') { 'interactive visible' }
|
|
43
|
+
else
|
|
44
|
+
register(header: 'Non Interactive') { 'non-interactive visible' }
|
|
45
|
+
end
|
|
46
|
+
register(header: 'Yes/True') { true }
|
|
47
|
+
register(header: 'No/False', row_color: [:clear]) { false }
|
|
48
|
+
register(header: 'Missing') { nil }
|
|
54
49
|
end
|
|
55
50
|
end
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
class DemoShow < OutputMode::Formatters::Show
|
|
53
|
+
constructor do
|
|
54
|
+
register(header: 'Integer') { |i| i }
|
|
55
|
+
register(header: 'Standard') { 'always visible' }
|
|
56
|
+
if verbose?
|
|
57
|
+
register(header: 'Verbose') { 'verbose visible' }
|
|
58
|
+
else
|
|
59
|
+
register(header: 'Simplified') { 'simplified visible' }
|
|
60
|
+
end
|
|
61
|
+
if interactive?
|
|
62
|
+
register(header: 'Interactive') { 'interactive visible' }
|
|
63
|
+
else
|
|
64
|
+
register(header: 'Non Interactive') { 'non-interactive visible' }
|
|
65
|
+
end
|
|
66
|
+
register(header: 'Yes/True', section: :boolean) { true }
|
|
67
|
+
register(header: 'No/False', section: :boolean) { false }
|
|
68
|
+
register(header: 'Missing') { nil }
|
|
69
|
+
register(header: 'Tab') { "tab1\ttab2" }
|
|
70
|
+
register(header: 'New line') { "line1\nline2" }
|
|
71
|
+
end
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
data = [1, 2, 3]
|
|
74
75
|
|
|
75
|
-
other_template = ERB.new(<<~TEMPLATE, nil, '-')
|
|
76
|
-
# Non boolean values
|
|
77
|
-
<% each(:other) do |value, field:, padding:, **_| -%>
|
|
78
|
-
<%= padding -%><%= pastel.blue.bold field -%><%= pastel.bold ':' -%> <%= pastel.green value %>
|
|
79
|
-
<% end -%>
|
|
80
|
-
|
|
81
|
-
# Boolean Values
|
|
82
|
-
<% each(:boolean) do |value, field:, padding:, **_| -%>
|
|
83
|
-
<%= padding -%><%= pastel.blue.bold field -%><%= pastel.bold ':' -%> <%= pastel.green value %>
|
|
84
|
-
<% end -%>
|
|
85
|
-
TEMPLATE
|
|
86
|
-
|
|
87
76
|
puts <<~EOF
|
|
88
77
|
#==============================================================================
|
|
89
78
|
#==============================================================================
|
|
@@ -95,34 +84,34 @@ puts <<~EOF
|
|
|
95
84
|
# Default Demo Index
|
|
96
85
|
# Simplified in interactive shells, verbose in non-interactive
|
|
97
86
|
#==============================================================================
|
|
98
|
-
#{DemoIndex.
|
|
87
|
+
#{DemoIndex.render(*data)}
|
|
99
88
|
|
|
100
89
|
#==============================================================================
|
|
101
90
|
# Demo Verbose Index
|
|
102
91
|
#==============================================================================
|
|
103
|
-
#{DemoIndex.
|
|
92
|
+
#{DemoIndex.render(*data, verbose: true)}
|
|
104
93
|
|
|
105
94
|
#==============================================================================
|
|
106
95
|
# Demo "Simplified" Index
|
|
107
96
|
#==============================================================================
|
|
108
|
-
#{DemoIndex.
|
|
97
|
+
#{DemoIndex.render(*data, verbose: false)}
|
|
109
98
|
|
|
110
99
|
#==============================================================================
|
|
111
100
|
# Force Interactive
|
|
112
101
|
# Always print as if the shell is interactive
|
|
113
102
|
#==============================================================================
|
|
114
|
-
#{DemoIndex.
|
|
103
|
+
#{DemoIndex.render(*data, interactive: true)}
|
|
115
104
|
|
|
116
105
|
#==============================================================================
|
|
117
106
|
# Force Non-Interactive
|
|
118
107
|
# Always print as if the shell is non-interactive
|
|
119
108
|
#==============================================================================
|
|
120
|
-
#{DemoIndex.
|
|
109
|
+
#{DemoIndex.render(*data, interactive: false)}
|
|
121
110
|
|
|
122
111
|
#==============================================================================
|
|
123
112
|
# Demo ASCII Index
|
|
124
113
|
#==============================================================================
|
|
125
|
-
#{DemoIndex.
|
|
114
|
+
#{DemoIndex.render(*data, ascii: true)}
|
|
126
115
|
|
|
127
116
|
#==============================================================================
|
|
128
117
|
#==============================================================================
|
|
@@ -134,39 +123,32 @@ puts <<~EOF
|
|
|
134
123
|
# Default Settings
|
|
135
124
|
# Simplified in interactive shells, verbose in non-interactive
|
|
136
125
|
#==============================================================================
|
|
137
|
-
#{DemoShow.
|
|
126
|
+
#{DemoShow.render(data.first)}
|
|
138
127
|
|
|
139
128
|
#==============================================================================
|
|
140
129
|
# Demo Verbose Show
|
|
141
130
|
#==============================================================================
|
|
142
|
-
#{DemoShow.
|
|
131
|
+
#{DemoShow.render(data.first, verbose: true)}
|
|
143
132
|
|
|
144
133
|
#==============================================================================
|
|
145
134
|
# Demo "Simplified" Show
|
|
146
135
|
#==============================================================================
|
|
147
|
-
#{DemoShow.
|
|
136
|
+
#{DemoShow.render(data.first, verbose: false)}
|
|
148
137
|
|
|
149
138
|
#==============================================================================
|
|
150
139
|
# Force Interactive
|
|
151
140
|
# Always print as if the shell is interactive
|
|
152
141
|
#==============================================================================
|
|
153
|
-
#{DemoShow.
|
|
142
|
+
#{DemoShow.render(data.first, interactive: true)}
|
|
154
143
|
|
|
155
144
|
#==============================================================================
|
|
156
145
|
# Force Non-Interactive
|
|
157
146
|
# Always print as if the shell is non-interactive
|
|
158
147
|
#==============================================================================
|
|
159
|
-
#{DemoShow.
|
|
148
|
+
#{DemoShow.render(data.first, interactive: false)}
|
|
160
149
|
|
|
161
150
|
#==============================================================================
|
|
162
151
|
# Demo ASCII Index
|
|
163
152
|
#==============================================================================
|
|
164
|
-
#{DemoShow.
|
|
165
|
-
|
|
166
|
-
#==============================================================================
|
|
167
|
-
# Group the boolean value separately
|
|
168
|
-
# NOTE: This only occurs in interactive mode
|
|
169
|
-
# Non-Interactive sessions have a fix order
|
|
170
|
-
#==============================================================================
|
|
171
|
-
#{DemoShow.build_output(template: other_template).render(*data)}
|
|
153
|
+
#{DemoShow.render(data.first, ascii: true)}
|
|
172
154
|
EOF
|
data/bin/htmldiff
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'htmldiff' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("diff-lcs", "htmldiff")
|
data/bin/ldiff
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'ldiff' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("diff-lcs", "ldiff")
|
data/bin/pry
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'pry' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("pry", "pry")
|
data/bin/rake
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/lib/output_mode/callable.rb
CHANGED
|
@@ -34,7 +34,9 @@ module OutputMode
|
|
|
34
34
|
@callables = []
|
|
35
35
|
case callables
|
|
36
36
|
when Array, Callables
|
|
37
|
-
callables.each
|
|
37
|
+
callables.each do |c|
|
|
38
|
+
@callables << (c.is_a?(Callable) ? c : Callable.new(&c))
|
|
39
|
+
end
|
|
38
40
|
when nil
|
|
39
41
|
# NOOP
|
|
40
42
|
else
|
|
@@ -56,20 +58,39 @@ module OutputMode
|
|
|
56
58
|
@callables.each(&block)
|
|
57
59
|
end
|
|
58
60
|
|
|
59
|
-
def pad_each(
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
def pad_each(*ctx, **input_opts, &block)
|
|
62
|
+
fields = self.map do |callables|
|
|
63
|
+
field = callables.config[:header]
|
|
64
|
+
if field.respond_to?(:call)
|
|
65
|
+
opts = if field.parameters.include?(:keyrest)
|
|
66
|
+
input_opts.dup
|
|
67
|
+
else
|
|
68
|
+
keys = field.parameters
|
|
69
|
+
.select { |type, _| [:key, :keyreq].include?(type) }
|
|
70
|
+
.map { |_, k| k }
|
|
71
|
+
input_opts.slice(*keys)
|
|
72
|
+
end
|
|
73
|
+
opts.empty? ? field.call(*ctx) : field.call(*ctx, **opts)
|
|
74
|
+
else
|
|
75
|
+
field
|
|
76
|
+
end
|
|
77
|
+
end
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
max_length = fields.map { |f| f.to_s.length }.max
|
|
80
|
+
pads = self.each_with_index.map do |callable, idx|
|
|
81
|
+
field = fields[idx]
|
|
82
|
+
length = max_length - field.to_s.length
|
|
83
|
+
[callable, { padding: ' ' * length, field: field }]
|
|
66
84
|
end
|
|
67
85
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
pads.each
|
|
86
|
+
# Generate an enum
|
|
87
|
+
# NOTE: This fixes the double splate deprecation warning
|
|
88
|
+
enum = Enumerator.new do |yielder|
|
|
89
|
+
pads.each do |callable, opts|
|
|
90
|
+
yielder.yield(callable, **opts)
|
|
91
|
+
end
|
|
72
92
|
end
|
|
93
|
+
enum.each(&block)
|
|
73
94
|
end
|
|
74
95
|
|
|
75
96
|
def config_select(key, *values)
|
|
@@ -83,6 +104,10 @@ module OutputMode
|
|
|
83
104
|
end
|
|
84
105
|
Callables.new(selected)
|
|
85
106
|
end
|
|
107
|
+
|
|
108
|
+
def length
|
|
109
|
+
@callables.length
|
|
110
|
+
end
|
|
86
111
|
end
|
|
87
112
|
|
|
88
113
|
class Callable
|
|
@@ -116,6 +141,8 @@ module OutputMode
|
|
|
116
141
|
|
|
117
142
|
# Handles the dynamic +<query>?+ and +<explicit-negation>!+ methods
|
|
118
143
|
#
|
|
144
|
+
# DEPRECATED: The explicit! negation operator should not be used
|
|
145
|
+
#
|
|
119
146
|
# @return [Boolean] The result of the query or explicit-negation
|
|
120
147
|
# @raise [NoMethodError] All other method calls
|
|
121
148
|
def method_missing(s, *args, &b)
|
|
@@ -175,6 +202,7 @@ module OutputMode
|
|
|
175
202
|
callable.call(*a)
|
|
176
203
|
end
|
|
177
204
|
|
|
205
|
+
# DEPRECATED: Use a Formatter class
|
|
178
206
|
def generator(output)
|
|
179
207
|
->(*a) do
|
|
180
208
|
# Implicitly determine which parts of the context can be passed through
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
#==============================================================================
|
|
2
|
-
#
|
|
2
|
+
# Copyright 2021 William McCumstie
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
# this list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
# and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
15
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
18
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
19
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
20
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
21
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
22
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
23
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
24
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
3
25
|
#==============================================================================
|
|
4
26
|
|
|
5
27
|
require 'erb'
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#==============================================================================
|
|
2
|
+
# Copyright 2021 William McCumstie
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
# this list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
# and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
15
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
18
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
19
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
20
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
21
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
22
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
23
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
24
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
25
|
+
#==============================================================================
|
|
26
|
+
|
|
27
|
+
require 'tty-color'
|
|
28
|
+
|
|
29
|
+
module OutputMode
|
|
30
|
+
class Formatter
|
|
31
|
+
def self.constructor(&block)
|
|
32
|
+
@constructor ||= block
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.build(*objects, **opts)
|
|
36
|
+
new(*objects, **opts).tap do |policy|
|
|
37
|
+
next unless constructor
|
|
38
|
+
policy.instance_exec(&constructor)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.render(*objects, **opts)
|
|
43
|
+
build(*objects, **opts).render
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def initialize(*objects, verbose: nil, ascii: nil, interactive: nil, color: nil)
|
|
47
|
+
@verbose = verbose
|
|
48
|
+
@ascii = ascii
|
|
49
|
+
@interactive = interactive
|
|
50
|
+
@color = color
|
|
51
|
+
|
|
52
|
+
# NOTE: This is intentionally not exposed on the base class
|
|
53
|
+
# It is up to the individual implementations to expose it
|
|
54
|
+
@objects = objects
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def callables
|
|
58
|
+
@callables ||= Callables.new
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def register(**config, &block)
|
|
62
|
+
callables << Callable.new(**config) do |*args, **opts|
|
|
63
|
+
value = block.call(*args, **opts)
|
|
64
|
+
format(value, **config)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def format(value, **config)
|
|
69
|
+
case value
|
|
70
|
+
when TrueClass
|
|
71
|
+
config[:yes] || yes
|
|
72
|
+
when FalseClass
|
|
73
|
+
config[:no] || no
|
|
74
|
+
when NilClass
|
|
75
|
+
config[:default] || default
|
|
76
|
+
when Time
|
|
77
|
+
format = config[:time] || time
|
|
78
|
+
value.strftime(format)
|
|
79
|
+
else
|
|
80
|
+
value
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def build_output
|
|
85
|
+
raise NotImplementedError
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def render
|
|
89
|
+
build_output.render(*@objects)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def interactive?
|
|
93
|
+
if @interactive.nil?
|
|
94
|
+
$stdout.tty?
|
|
95
|
+
else
|
|
96
|
+
@interactive
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def color?
|
|
101
|
+
if @color.nil? && (ascii? || !interactive?)
|
|
102
|
+
false
|
|
103
|
+
elsif @color.nil?
|
|
104
|
+
TTY::Color.color?
|
|
105
|
+
else
|
|
106
|
+
@color
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def ascii?
|
|
111
|
+
if @ascii.nil?
|
|
112
|
+
!interactive?
|
|
113
|
+
else
|
|
114
|
+
@ascii
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def verbose?
|
|
119
|
+
if @verbose.nil?
|
|
120
|
+
!interactive?
|
|
121
|
+
else
|
|
122
|
+
@verbose
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def yes(value = nil)
|
|
127
|
+
@yes = value unless value.nil?
|
|
128
|
+
@yes ? @yes : (ascii? ? 'yes' : '✓')
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def no(value = nil)
|
|
132
|
+
@no = value unless value.nil?
|
|
133
|
+
@no ? @no : (ascii? ? 'no' : '✕')
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def default(value = nil)
|
|
137
|
+
@default = value unless value.nil?
|
|
138
|
+
@default ? @default : (interactive? ? '(none)' : '')
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def time(value = nil)
|
|
142
|
+
@time = value unless value.nil?
|
|
143
|
+
@time ? @time : (verbose? ? "%Y-%m-%dT%H:%M:%S%:z" : "%d/%m/%y %H:%M")
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#==============================================================================
|
|
2
|
+
# Copyright 2021 William McCumstie
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
# this list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
# and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
15
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
18
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
19
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
20
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
21
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
22
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
23
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
24
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
25
|
+
#==============================================================================
|
|
26
|
+
|
|
27
|
+
module OutputMode
|
|
28
|
+
module Formatters
|
|
29
|
+
class Index < Formatter
|
|
30
|
+
attr_reader :objects
|
|
31
|
+
|
|
32
|
+
def header_color(value = nil)
|
|
33
|
+
@header_color = value unless value.nil?
|
|
34
|
+
@header_color ? @header_color : :bold
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def row_color(value = nil)
|
|
38
|
+
@row_color = value unless value.nil?
|
|
39
|
+
@row_color ? @row_color : :cyan
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def build_output
|
|
43
|
+
if interactive?
|
|
44
|
+
opts = {
|
|
45
|
+
renderer: ascii? ? :ascii : :unicode,
|
|
46
|
+
header_color: header_color,
|
|
47
|
+
row_color: row_color,
|
|
48
|
+
colorize: color?,
|
|
49
|
+
rotate: false,
|
|
50
|
+
padding: [0, 1]
|
|
51
|
+
}
|
|
52
|
+
Outputs::Tabulated.new(*callables, **opts)
|
|
53
|
+
else
|
|
54
|
+
Outputs::Delimited.new(*callables, col_sep: "\t")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#==============================================================================
|
|
2
|
+
# Copyright 2021 William McCumstie
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
# this list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
# and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
15
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
18
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
19
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
20
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
21
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
22
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
23
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
24
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
25
|
+
#==============================================================================
|
|
26
|
+
|
|
27
|
+
require 'output_mode/default_erb'
|
|
28
|
+
require 'output_mode/non_interactive_erb'
|
|
29
|
+
|
|
30
|
+
module OutputMode
|
|
31
|
+
module Formatters
|
|
32
|
+
class Show < Formatter
|
|
33
|
+
include Enumerable
|
|
34
|
+
|
|
35
|
+
# Limit the policy to a single object
|
|
36
|
+
def initialize(object, **opts)
|
|
37
|
+
super
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def object
|
|
41
|
+
@objects.first
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def template(value = nil)
|
|
45
|
+
@template = value unless value.nil?
|
|
46
|
+
@template ? @template : (interactive? ? DEFAULT_ERB : NON_INTERACTIVE_ERB)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def scope(value = nil)
|
|
50
|
+
@scope = value unless value.nil?
|
|
51
|
+
@scope ? @scope : self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def build_output
|
|
55
|
+
opts = {
|
|
56
|
+
template: template,
|
|
57
|
+
colorize: color?,
|
|
58
|
+
bind: scope.instance_exec { binding }
|
|
59
|
+
}
|
|
60
|
+
OutputMode::Outputs::Templated.new(*callables, **opts)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @yieldparam value An attribute to be rendered
|
|
64
|
+
# @yieldparam field: An optional field header for the value
|
|
65
|
+
# @yieldparam padding: A padding string which will right align the +field+
|
|
66
|
+
# @yieldparam **config TBA
|
|
67
|
+
def each(section = nil, &block)
|
|
68
|
+
# Select the callable objects
|
|
69
|
+
selected = if section == nil
|
|
70
|
+
callables
|
|
71
|
+
elsif section == :default
|
|
72
|
+
callables.config_select(:section, :default, nil)
|
|
73
|
+
else
|
|
74
|
+
callables.config_select(:section, section)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Yield each selected attribute
|
|
78
|
+
objs = selected.pad_each(object).map do |callable, opts|
|
|
79
|
+
field = opts[:field]
|
|
80
|
+
padding = opts[:padding]
|
|
81
|
+
value = callable.call(object)
|
|
82
|
+
[value, { field: field, padding: padding }]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Runs the provided block
|
|
86
|
+
objs.each do |model, opts|
|
|
87
|
+
block.call(model, **opts)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Library for colorizing the output. It is automatically disabled when the
|
|
92
|
+
# +colorize+ flag is +false+
|
|
93
|
+
def pastel
|
|
94
|
+
@pastel ||= Pastel.new(enabled: color?)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#==============================================================================
|
|
2
|
+
# Copyright 2021 William McCumstie
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
# this list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
# and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
15
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
18
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
19
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
20
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
21
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
22
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
23
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
24
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
25
|
+
#==============================================================================
|
|
26
|
+
|
|
27
|
+
module OutputMode
|
|
28
|
+
autoload 'Formatter', File.expand_path('formatter.rb', __dir__)
|
|
29
|
+
|
|
30
|
+
module Formatters
|
|
31
|
+
Dir.glob(File.expand_path('formatters/*.rb', __dir__)).each do |path|
|
|
32
|
+
autoload File.basename(path).chomp('.rb').capitalize, path
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#==============================================================================
|
|
2
|
+
# Copyright 2021 William McCumstie
|
|
3
|
+
#
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
# this list of conditions and the following disclaimer.
|
|
9
|
+
#
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
# and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
15
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
18
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
19
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
20
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
21
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
22
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
23
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
24
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
25
|
+
#==============================================================================
|
|
26
|
+
|
|
27
|
+
require 'erb'
|
|
28
|
+
|
|
29
|
+
module OutputMode
|
|
30
|
+
NON_INTERACTIVE_ERB = ERB.new(<<~TEMPLATE, nil, '-')
|
|
31
|
+
<% each do |value, field:, padding:, **_| -%>
|
|
32
|
+
<% if value.nil? && field.nil? -%>
|
|
33
|
+
\t
|
|
34
|
+
<% elsif field.nil? -%>
|
|
35
|
+
\t<%= value %>
|
|
36
|
+
<% else -%>
|
|
37
|
+
<%= field -%>\t<%= value.to_s.dump[1...-1] %>
|
|
38
|
+
<% end -%>
|
|
39
|
+
<% end -%>
|
|
40
|
+
TEMPLATE
|
|
41
|
+
end
|
|
@@ -77,11 +77,16 @@ module OutputMode
|
|
|
77
77
|
|
|
78
78
|
private
|
|
79
79
|
|
|
80
|
+
def has_header?
|
|
81
|
+
callables.any? { |c| c.config.key?(:header) }
|
|
82
|
+
end
|
|
83
|
+
|
|
80
84
|
# Colorizes the header when requested
|
|
81
85
|
def processed_header
|
|
86
|
+
return nil unless has_header?
|
|
82
87
|
callables.map do |callable|
|
|
83
88
|
header = callable.config.fetch(:header, '')
|
|
84
|
-
color = callable.config.fetch(:header_color) || header_color
|
|
89
|
+
color = callable.config.fetch(:header_color, nil) || header_color
|
|
85
90
|
case color
|
|
86
91
|
when nil
|
|
87
92
|
header.to_s
|
|
@@ -49,14 +49,15 @@ module OutputMode
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# Yield each selected attribute
|
|
52
|
-
objs = callables.pad_each.map do |callable, padding:|
|
|
52
|
+
objs = callables.pad_each(model, **output.context).map do |callable, padding:, field:|
|
|
53
53
|
value = callable.generator(output).call(model)
|
|
54
|
-
field
|
|
55
|
-
[value, {field: field, padding: padding }]
|
|
54
|
+
[value, { field: field, padding: padding }]
|
|
56
55
|
end
|
|
57
56
|
|
|
58
57
|
# Runs the provided block
|
|
59
|
-
objs.each
|
|
58
|
+
objs.each do |model, opts|
|
|
59
|
+
block.call(model, **opts)
|
|
60
|
+
end
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
# Renders an ERB object within the entry's context. This provides access to the
|
|
@@ -87,7 +88,8 @@ module OutputMode
|
|
|
87
88
|
# @!attribute [r] fields
|
|
88
89
|
# @!attribute [r] colorize
|
|
89
90
|
# @!attribute [r] sections
|
|
90
|
-
|
|
91
|
+
# @!attribute [r] bind
|
|
92
|
+
attr_reader :erb, :fields, :separator, :colorize, :sections, :bind
|
|
91
93
|
|
|
92
94
|
# Create a new +output+ which will render using +ERB+. The provided +template+ should
|
|
93
95
|
# only render the +output+ for a single +entry+ (aka model, record, data object, etc).
|
|
@@ -105,10 +107,11 @@ module OutputMode
|
|
|
105
107
|
# @param [Array] *procs see {OutputMode::Output#initialize}
|
|
106
108
|
# @param [ERB] template: The +template+ object used by the renderer
|
|
107
109
|
# @param [Array] fields: An optional array of field headers that map to the procs, repeating the last value if required
|
|
108
|
-
# @param fields: A static value to use as all field headers
|
|
110
|
+
# @param fields: DEPRECATED A static value to use as all field headers
|
|
109
111
|
# @param separator: The character(s) used to join the "entries" together
|
|
110
112
|
# @param colorize: Flags if the caller wants the colorized version, this maybe ignored by +template+
|
|
111
|
-
# @param sections: An optional array that groups the procs into sections
|
|
113
|
+
# @param sections: DEPRECATED An optional array that groups the procs into sections
|
|
114
|
+
# @param bind: An optional execution context to render the template within
|
|
112
115
|
# @param [Hash] **config see {OutputMode::Output#initialize}
|
|
113
116
|
def initialize(*procs,
|
|
114
117
|
template: nil,
|
|
@@ -116,6 +119,7 @@ module OutputMode
|
|
|
116
119
|
separator: "\n",
|
|
117
120
|
colorize: false,
|
|
118
121
|
sections: nil,
|
|
122
|
+
bind: nil,
|
|
119
123
|
**config)
|
|
120
124
|
@erb = case template
|
|
121
125
|
when String
|
|
@@ -129,6 +133,7 @@ module OutputMode
|
|
|
129
133
|
@separator = separator
|
|
130
134
|
@colorize = colorize
|
|
131
135
|
@sections = sections
|
|
136
|
+
@bind = bind
|
|
132
137
|
super(*procs, **config)
|
|
133
138
|
end
|
|
134
139
|
|
|
@@ -138,11 +143,17 @@ module OutputMode
|
|
|
138
143
|
#
|
|
139
144
|
# @see OutputMode::Output#render
|
|
140
145
|
def render(*data)
|
|
141
|
-
data.map
|
|
142
|
-
|
|
146
|
+
data.map do |datum|
|
|
147
|
+
if bind
|
|
148
|
+
erb.result(bind)
|
|
149
|
+
else
|
|
150
|
+
Entry.new(self, datum, colorize).render(erb)
|
|
151
|
+
end
|
|
152
|
+
end.join(separator)
|
|
143
153
|
end
|
|
144
154
|
|
|
145
155
|
# Returns the length of the maximum field
|
|
156
|
+
# DEPRECATED
|
|
146
157
|
def max_field_length
|
|
147
158
|
if fields.is_a? Array
|
|
148
159
|
fields.map { |f| f.to_s.length }.max
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
#==============================================================================
|
|
26
26
|
|
|
27
27
|
require 'tty-color'
|
|
28
|
+
require 'output_mode/default_erb'
|
|
29
|
+
require 'output_mode/non_interactive_erb'
|
|
28
30
|
|
|
29
31
|
module OutputMode
|
|
30
32
|
module TLDR
|
|
@@ -39,7 +41,7 @@ module OutputMode
|
|
|
39
41
|
# @param section: Define the grouping a callable belongs to. Ignored by default
|
|
40
42
|
# @param modes: Additional modes flags for the callable
|
|
41
43
|
# @yieldparam model The subject the column is describing, some sort of data model
|
|
42
|
-
def register_callable(modes: {}, header:, verbose: nil, interactive: nil, section: :
|
|
44
|
+
def register_callable(modes: {}, header:, verbose: nil, interactive: nil, section: :default, &b)
|
|
43
45
|
modes = modes.map { |m| [m, true] }.to_h if modes.is_a? Array
|
|
44
46
|
super(modes: modes.merge(verbose: verbose, interactive: interactive),
|
|
45
47
|
header: header,
|
|
@@ -67,11 +69,15 @@ module OutputMode
|
|
|
67
69
|
# for consumption by machines. This output ignores the provided +verbose+
|
|
68
70
|
# flag as it is always verbose.
|
|
69
71
|
#
|
|
70
|
-
# The +template+ overrides the default erb template for
|
|
72
|
+
# The +template+ overrides the default erb template for interactive sessions.
|
|
73
|
+
# The +non_interactive_template+ overrides the template for non-interactive
|
|
74
|
+
# sessions.
|
|
71
75
|
#
|
|
72
76
|
# An interative/ non-interactive output can be forced by setting the
|
|
73
77
|
# +interactive+ flag to +true+/+false+ respectively
|
|
74
|
-
def build_output(verbose: nil, ascii: nil, interactive: nil,
|
|
78
|
+
def build_output(verbose: nil, ascii: nil, interactive: nil, context: {},
|
|
79
|
+
template: OutputMode::DEFAULT_ERB,
|
|
80
|
+
non_interactive_template: OutputMode::NON_INTERACTIVE_ERB)
|
|
75
81
|
# Set the interactive and verbose flags if not provided
|
|
76
82
|
interactive = $stdout.tty? if interactive.nil?
|
|
77
83
|
verbose = !interactive if verbose.nil?
|
|
@@ -96,28 +102,22 @@ module OutputMode
|
|
|
96
102
|
callables.reject { |o| o.interactive? }
|
|
97
103
|
end
|
|
98
104
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
# Define the templating parameters
|
|
106
|
+
opts = if ascii && interactive
|
|
107
|
+
{ yes: 'yes', no: 'no', colorize: false, default: '(none)', template: template }
|
|
108
|
+
elsif interactive
|
|
109
|
+
{ yes: '✓', no: '✕', colorize: TTY::Color.color?, default: '(none)', template: template }
|
|
110
|
+
else
|
|
111
|
+
{ yes: 'yes', no: 'no', colorize: false, default: '', template: non_interactive_template }
|
|
112
|
+
end
|
|
106
113
|
|
|
107
|
-
|
|
114
|
+
sections = callables.map { |o| o.config[:section] }
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
context: context,
|
|
115
|
-
**opts)
|
|
116
|
-
else
|
|
117
|
-
# Creates the machine readable output
|
|
118
|
-
Outputs::Delimited.new(*callables, col_sep: "\t", yes: 'yes', no: 'no', default: nil,
|
|
119
|
-
context: context)
|
|
120
|
-
end
|
|
116
|
+
Outputs::Templated.new(*callables,
|
|
117
|
+
fields: callables.map { |c| c.config.fetch(:header, 'missing') },
|
|
118
|
+
sections: sections,
|
|
119
|
+
context: context,
|
|
120
|
+
**opts)
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
end
|
data/lib/output_mode/version.rb
CHANGED
data/lib/output_mode.rb
CHANGED
data/output_mode.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: output_mode
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- William McCumsite
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-10-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: tty-table
|
|
@@ -108,6 +108,20 @@ dependencies:
|
|
|
108
108
|
- - ">"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0.11'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: pry-byebug
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '3.0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '3.0'
|
|
111
125
|
description:
|
|
112
126
|
email:
|
|
113
127
|
- openlicense.williams@gmail.com
|
|
@@ -122,14 +136,27 @@ files:
|
|
|
122
136
|
- LICENSE.txt
|
|
123
137
|
- README.md
|
|
124
138
|
- Rakefile
|
|
139
|
+
- bin/bundle
|
|
140
|
+
- bin/byebug
|
|
141
|
+
- bin/coderay
|
|
125
142
|
- bin/console
|
|
126
143
|
- bin/demo
|
|
144
|
+
- bin/htmldiff
|
|
145
|
+
- bin/ldiff
|
|
146
|
+
- bin/pry
|
|
147
|
+
- bin/rake
|
|
148
|
+
- bin/rspec
|
|
127
149
|
- bin/setup
|
|
128
150
|
- lib/output_mode.rb
|
|
129
151
|
- lib/output_mode/builder_dsl.rb
|
|
130
152
|
- lib/output_mode/callable.rb
|
|
131
153
|
- lib/output_mode/default_erb.rb
|
|
132
154
|
- lib/output_mode/errors.rb
|
|
155
|
+
- lib/output_mode/formatter.rb
|
|
156
|
+
- lib/output_mode/formatters.rb
|
|
157
|
+
- lib/output_mode/formatters/index.rb
|
|
158
|
+
- lib/output_mode/formatters/show.rb
|
|
159
|
+
- lib/output_mode/non_interactive_erb.rb
|
|
133
160
|
- lib/output_mode/output.rb
|
|
134
161
|
- lib/output_mode/outputs.rb
|
|
135
162
|
- lib/output_mode/outputs/delimited.rb
|