output_mode 1.5.1 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 +57 -75
- 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 +32 -14
- data/lib/output_mode/default_erb.rb +23 -1
- data/lib/output_mode/formatter.rb +143 -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: 0e8e7233d045c7642d64d90ac04d0af8561ce06f1fb24125f4a849ce5cbf40c6
|
4
|
+
data.tar.gz: acba4cceeb3626e5350f637fa6d3422115622e1e5ef2cde3dbb5186b6df20173
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8faf122204eb774c31e5feefdd68cab98f12817b04a904e5c1270dc9cd95496ef0585494a48f7a4ff93050ed41607dc0bb50aab9ba2c3513e8335bd426165df9
|
7
|
+
data.tar.gz: a460316ed1272d74ba1b8a398c9d8b197cdfa9d1682304648ad04534cb52437b0bbbe48d9eb308412e143898ff58ed4530201cac19bdba56a5dbbd9299bfb790
|
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
|
+
def register_all
|
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 humanize?
|
42
|
+
register(header: 'humanize') { 'humanize visible' }
|
43
|
+
else
|
44
|
+
register(header: 'Non humanize') { 'non-humanize 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
|
+
def register_all
|
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 humanize?
|
62
|
+
register(header: 'humanize') { 'humanize visible' }
|
63
|
+
else
|
64
|
+
register(header: 'Non humanize') { 'non-humanize 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
|
#==============================================================================
|
@@ -93,36 +82,36 @@ puts <<~EOF
|
|
93
82
|
|
94
83
|
#==============================================================================
|
95
84
|
# Default Demo Index
|
96
|
-
# Simplified in
|
85
|
+
# Simplified in humanize shells, verbose in non-humanize
|
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
|
-
# Force
|
112
|
-
# Always print as if the shell is
|
100
|
+
# Force humanize
|
101
|
+
# Always print as if the shell is humanize
|
113
102
|
#==============================================================================
|
114
|
-
#{DemoIndex.
|
103
|
+
#{DemoIndex.render(*data, humanize: true)}
|
115
104
|
|
116
105
|
#==============================================================================
|
117
|
-
# Force Non-
|
118
|
-
# Always print as if the shell is non-
|
106
|
+
# Force Non-humanize
|
107
|
+
# Always print as if the shell is non-humanize
|
119
108
|
#==============================================================================
|
120
|
-
#{DemoIndex.
|
109
|
+
#{DemoIndex.render(*data, humanize: 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
|
#==============================================================================
|
@@ -132,41 +121,34 @@ puts <<~EOF
|
|
132
121
|
|
133
122
|
#==============================================================================
|
134
123
|
# Default Settings
|
135
|
-
# Simplified in
|
124
|
+
# Simplified in humanize shells, verbose in non-humanize
|
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
|
-
# Force
|
151
|
-
# Always print as if the shell is
|
139
|
+
# Force humanize
|
140
|
+
# Always print as if the shell is humanize
|
152
141
|
#==============================================================================
|
153
|
-
#{DemoShow.
|
142
|
+
#{DemoShow.render(data.first, humanize: true)}
|
154
143
|
|
155
144
|
#==============================================================================
|
156
|
-
# Force Non-
|
157
|
-
# Always print as if the shell is non-
|
145
|
+
# Force Non-humanize
|
146
|
+
# Always print as if the shell is non-humanize
|
158
147
|
#==============================================================================
|
159
|
-
#{DemoShow.
|
148
|
+
#{DemoShow.render(data.first, humanize: 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,30 +58,39 @@ module OutputMode
|
|
56
58
|
@callables.each(&block)
|
57
59
|
end
|
58
60
|
|
59
|
-
def pad_each(
|
61
|
+
def pad_each(*ctx, **input_opts, &block)
|
60
62
|
fields = self.map do |callables|
|
61
|
-
field = callables.config[
|
62
|
-
if field.respond_to?(:call)
|
63
|
-
|
64
|
-
|
65
|
-
|
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)
|
66
74
|
else
|
67
|
-
field
|
75
|
+
field
|
68
76
|
end
|
69
77
|
end
|
70
78
|
|
71
|
-
max_length = fields.map
|
79
|
+
max_length = fields.map { |f| f.to_s.length }.max
|
72
80
|
pads = self.each_with_index.map do |callable, idx|
|
73
81
|
field = fields[idx]
|
74
|
-
length = max_length - field.length
|
82
|
+
length = max_length - field.to_s.length
|
75
83
|
[callable, { padding: ' ' * length, field: field }]
|
76
84
|
end
|
77
85
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
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
|
82
92
|
end
|
93
|
+
enum.each(&block)
|
83
94
|
end
|
84
95
|
|
85
96
|
def config_select(key, *values)
|
@@ -93,6 +104,10 @@ module OutputMode
|
|
93
104
|
end
|
94
105
|
Callables.new(selected)
|
95
106
|
end
|
107
|
+
|
108
|
+
def length
|
109
|
+
@callables.length
|
110
|
+
end
|
96
111
|
end
|
97
112
|
|
98
113
|
class Callable
|
@@ -126,6 +141,8 @@ module OutputMode
|
|
126
141
|
|
127
142
|
# Handles the dynamic +<query>?+ and +<explicit-negation>!+ methods
|
128
143
|
#
|
144
|
+
# DEPRECATED: The explicit! negation operator should not be used
|
145
|
+
#
|
129
146
|
# @return [Boolean] The result of the query or explicit-negation
|
130
147
|
# @raise [NoMethodError] All other method calls
|
131
148
|
def method_missing(s, *args, &b)
|
@@ -185,6 +202,7 @@ module OutputMode
|
|
185
202
|
callable.call(*a)
|
186
203
|
end
|
187
204
|
|
205
|
+
# DEPRECATED: Use a Formatter class
|
188
206
|
def generator(output)
|
189
207
|
->(*a) do
|
190
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,143 @@
|
|
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.build(*objects, **opts)
|
32
|
+
new(*objects, **opts).tap(&:register_all)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.render(*objects, **opts)
|
36
|
+
build(*objects, **opts).render
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(*objects, verbose: nil, ascii: nil, humanize: nil, color: nil)
|
40
|
+
@verbose = verbose
|
41
|
+
@ascii = ascii
|
42
|
+
@humanize = humanize
|
43
|
+
@color = color
|
44
|
+
|
45
|
+
# NOTE: This is intentionally not exposed on the base class
|
46
|
+
# It is up to the individual implementations to expose it
|
47
|
+
@objects = objects
|
48
|
+
end
|
49
|
+
|
50
|
+
# Override in sub-classes to define the attributes
|
51
|
+
def register_all
|
52
|
+
end
|
53
|
+
|
54
|
+
def callables
|
55
|
+
@callables ||= Callables.new
|
56
|
+
end
|
57
|
+
|
58
|
+
def register(**config, &block)
|
59
|
+
callables << Callable.new(**config) do |*args, **opts|
|
60
|
+
value = block.call(*args, **opts)
|
61
|
+
format(value, **config)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def format(value, **config)
|
66
|
+
case value
|
67
|
+
when TrueClass
|
68
|
+
config[:yes] || yes
|
69
|
+
when FalseClass
|
70
|
+
config[:no] || no
|
71
|
+
when NilClass
|
72
|
+
config[:default] || default
|
73
|
+
when Time
|
74
|
+
format = config[:time] || time
|
75
|
+
value.strftime(format)
|
76
|
+
else
|
77
|
+
value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_output
|
82
|
+
raise NotImplementedError
|
83
|
+
end
|
84
|
+
|
85
|
+
def render
|
86
|
+
build_output.render(*@objects)
|
87
|
+
end
|
88
|
+
|
89
|
+
def humanize?
|
90
|
+
if @humanize.nil?
|
91
|
+
$stdout.tty?
|
92
|
+
else
|
93
|
+
@humanize
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def color?
|
98
|
+
if @color.nil? && (ascii? || !humanize?)
|
99
|
+
false
|
100
|
+
elsif @color.nil?
|
101
|
+
TTY::Color.color?
|
102
|
+
else
|
103
|
+
@color
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def ascii?
|
108
|
+
if @ascii.nil?
|
109
|
+
!humanize?
|
110
|
+
else
|
111
|
+
@ascii
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def verbose?
|
116
|
+
if @verbose.nil?
|
117
|
+
!humanize?
|
118
|
+
else
|
119
|
+
@verbose
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def yes(value = nil)
|
124
|
+
@yes = value unless value.nil?
|
125
|
+
@yes ? @yes : (ascii? ? 'yes' : '✓')
|
126
|
+
end
|
127
|
+
|
128
|
+
def no(value = nil)
|
129
|
+
@no = value unless value.nil?
|
130
|
+
@no ? @no : (ascii? ? 'no' : '✕')
|
131
|
+
end
|
132
|
+
|
133
|
+
def default(value = nil)
|
134
|
+
@default = value unless value.nil?
|
135
|
+
@default ? @default : (humanize? ? '(none)' : '')
|
136
|
+
end
|
137
|
+
|
138
|
+
def time(value = nil)
|
139
|
+
@time = value unless value.nil?
|
140
|
+
@time ? @time : (verbose? ? "%Y-%m-%dT%H:%M:%S%:z" : "%d/%m/%y %H:%M")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
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 humanize?
|
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 : (humanize? ? 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.1
|
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-12 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
|