output_mode 1.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4d5f1fa1744eeff5535f33a65593b370d317124736089a8b24e2d76cb824b6e
4
- data.tar.gz: 10e77ade81138ebe9c6ca3e6b3299ba864c213603e54682eebb611057f72e2d6
3
+ metadata.gz: 4beed6d9f51548ef131e475bb68aa9c6d6fe079e39672b4691566ff80ffb65c2
4
+ data.tar.gz: df648ec5ee82875c189123dca55bfaabfa3bf4d5ba24e160be1d0f15bb1a624b
5
5
  SHA512:
6
- metadata.gz: a754089932ba840c19e0070b03e99e10fd4a957e4204ee08806dde91d54d51899950f817b8a34d3736f6c1fa9df4f85321fd938909c381f12230fe53df312ed1
7
- data.tar.gz: 7d5858053e4d55868893bbfa2c13ed63a52bd39f05f6b4ae690a29072c0ab7f7a4d9919204c6fd387dc2bd0ad19efec2fa5e0d023e22929452d2efa616cdf6d6
6
+ metadata.gz: 4e19a13c9e5b0b6e3fa497aadd4d1ed59bb6297fd8063f88d7f6a01bd4dd84f801baae9be8efe46a0e42e63024824d227fa51c0f7b84ad283baec7f5960fb2f6
7
+ data.tar.gz: b9ea560f3b42886ca8baaa04452cc5e4557c82ddf23068b095b544f2649b425b5b30a30212d6ef7955cd1fde41c624b4f9fbe73cfc32a3a2ddb1092f6d16fd54
data/.travis.yml CHANGED
@@ -3,7 +3,6 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.6.5
7
6
  - 2.7.0
8
7
  before_install: gem install bundler -v 2.0.2
9
8
  script: bundle exec rspec --format documentation --order random
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/demo CHANGED
@@ -29,61 +29,50 @@ require "bundler/setup"
29
29
  require "output_mode"
30
30
  require 'erb'
31
31
 
32
- module DemoIndex
33
- extend OutputMode::TLDR::Index
34
-
35
- register_callable(header: 'Integer', row_color: [:yellow, :bold]) { |i| i }
36
- register_callable(header: 'Standard', header_color: [:strikethrough] ) { 'always visible' }
37
- register_callable(header: 'Verbose', verbose: true) { 'verbose visible' }
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
- 'non-interactive-simplified'
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
- module DemoShow
58
- extend OutputMode::TLDR::Show
59
-
60
- register_callable(header: 'Integer') { |i| i }
61
- register_callable(header: 'Standard') { 'always visible' }
62
- register_callable(header: 'Verbose', verbose: true) { 'verbose visible' }
63
- register_callable(header: 'Simplified', verbose: false) { 'simplified visible' }
64
- register_callable(header: 'Interactive', interactive: true) { 'interactive visible' }
65
- register_callable(header: 'Non Interactive', interactive: false) { 'non-interactive visible' }
66
- register_callable(header: 'Yes/True', section: :boolean) { true }
67
- register_callable(header: 'No/False', section: :boolean) { false }
68
- register_callable(header: 'Missing') { nil }
69
- register_callable(header: 'Tab') { "tab1\ttab2" }
70
- register_callable(header: 'New line') { "line1\nline2" }
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.build_output.render(*data)}
87
+ #{DemoIndex.render(*data)}
99
88
 
100
89
  #==============================================================================
101
90
  # Demo Verbose Index
102
91
  #==============================================================================
103
- #{DemoIndex.build_output(verbose: true).render(*data)}
92
+ #{DemoIndex.render(*data, verbose: true)}
104
93
 
105
94
  #==============================================================================
106
95
  # Demo "Simplified" Index
107
96
  #==============================================================================
108
- #{DemoIndex.build_output(verbose: false).render(*data)}
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.build_output(interactive: true).render(*data)}
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.build_output(interactive: false).render(*data)}
109
+ #{DemoIndex.render(*data, interactive: false)}
121
110
 
122
111
  #==============================================================================
123
112
  # Demo ASCII Index
124
113
  #==============================================================================
125
- #{DemoIndex.build_output(ascii: true).render(*data)}
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.build_output.render(*data)}
126
+ #{DemoShow.render(data.first)}
138
127
 
139
128
  #==============================================================================
140
129
  # Demo Verbose Show
141
130
  #==============================================================================
142
- #{DemoShow.build_output(verbose: true).render(*data)}
131
+ #{DemoShow.render(data.first, verbose: true)}
143
132
 
144
133
  #==============================================================================
145
134
  # Demo "Simplified" Show
146
135
  #==============================================================================
147
- #{DemoShow.build_output(verbose: false).render(*data)}
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.build_output(interactive: true).render(*data)}
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.build_output(interactive: false).render(*data)}
148
+ #{DemoShow.render(data.first, interactive: false)}
160
149
 
161
150
  #==============================================================================
162
151
  # Demo ASCII Index
163
152
  #==============================================================================
164
- #{DemoShow.build_output(ascii: true).render(*data)}
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
@@ -58,7 +58,7 @@ module OutputMode
58
58
  @callables.each(&block)
59
59
  end
60
60
 
61
- def pad_each(*ctx, **input_opts)
61
+ def pad_each(*ctx, **input_opts, &block)
62
62
  fields = self.map do |callables|
63
63
  field = callables.config[:header]
64
64
  if field.respond_to?(:call)
@@ -83,11 +83,14 @@ module OutputMode
83
83
  [callable, { padding: ' ' * length, field: field }]
84
84
  end
85
85
 
86
- if block_given?
87
- pads.each { |c, opts| yield(c, **opts) }
88
- else
89
- 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
90
92
  end
93
+ enum.each(&block)
91
94
  end
92
95
 
93
96
  def config_select(key, *values)
@@ -138,6 +141,8 @@ module OutputMode
138
141
 
139
142
  # Handles the dynamic +<query>?+ and +<explicit-negation>!+ methods
140
143
  #
144
+ # DEPRECATED: The explicit! negation operator should not be used
145
+ #
141
146
  # @return [Boolean] The result of the query or explicit-negation
142
147
  # @raise [NoMethodError] All other method calls
143
148
  def method_missing(s, *args, &b)
@@ -197,6 +202,7 @@ module OutputMode
197
202
  callable.call(*a)
198
203
  end
199
204
 
205
+ # DEPRECATED: Use a Formatter class
200
206
  def generator(output)
201
207
  ->(*a) do
202
208
  # Implicitly determine which parts of the context can be passed through
@@ -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
+
@@ -55,7 +55,9 @@ module OutputMode
55
55
  end
56
56
 
57
57
  # Runs the provided block
58
- objs.each(&block)
58
+ objs.each do |model, opts|
59
+ block.call(model, **opts)
60
+ end
59
61
  end
60
62
 
61
63
  # Renders an ERB object within the entry's context. This provides access to the
@@ -86,7 +88,8 @@ module OutputMode
86
88
  # @!attribute [r] fields
87
89
  # @!attribute [r] colorize
88
90
  # @!attribute [r] sections
89
- attr_reader :erb, :fields, :separator, :colorize, :sections
91
+ # @!attribute [r] bind
92
+ attr_reader :erb, :fields, :separator, :colorize, :sections, :bind
90
93
 
91
94
  # Create a new +output+ which will render using +ERB+. The provided +template+ should
92
95
  # only render the +output+ for a single +entry+ (aka model, record, data object, etc).
@@ -104,10 +107,11 @@ module OutputMode
104
107
  # @param [Array] *procs see {OutputMode::Output#initialize}
105
108
  # @param [ERB] template: The +template+ object used by the renderer
106
109
  # @param [Array] fields: An optional array of field headers that map to the procs, repeating the last value if required
107
- # @param fields: A static value to use as all field headers
110
+ # @param fields: DEPRECATED A static value to use as all field headers
108
111
  # @param separator: The character(s) used to join the "entries" together
109
112
  # @param colorize: Flags if the caller wants the colorized version, this maybe ignored by +template+
110
- # @param sections: An optional array that groups the procs into sections. This is ignored by default
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
111
115
  # @param [Hash] **config see {OutputMode::Output#initialize}
112
116
  def initialize(*procs,
113
117
  template: nil,
@@ -115,6 +119,7 @@ module OutputMode
115
119
  separator: "\n",
116
120
  colorize: false,
117
121
  sections: nil,
122
+ bind: nil,
118
123
  **config)
119
124
  @erb = case template
120
125
  when String
@@ -128,6 +133,7 @@ module OutputMode
128
133
  @separator = separator
129
134
  @colorize = colorize
130
135
  @sections = sections
136
+ @bind = bind
131
137
  super(*procs, **config)
132
138
  end
133
139
 
@@ -137,11 +143,17 @@ module OutputMode
137
143
  #
138
144
  # @see OutputMode::Output#render
139
145
  def render(*data)
140
- data.map { |d| Entry.new(self, d, colorize).render(erb) }
141
- .join(separator)
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)
142
153
  end
143
154
 
144
155
  # Returns the length of the maximum field
156
+ # DEPRECATED
145
157
  def max_field_length
146
158
  if fields.is_a? Array
147
159
  fields.map { |f| f.to_s.length }.max
@@ -25,5 +25,5 @@
25
25
  #==============================================================================
26
26
 
27
27
  module OutputMode
28
- VERSION = "1.6.0"
28
+ VERSION = "1.7.0"
29
29
  end
data/lib/output_mode.rb CHANGED
@@ -29,6 +29,7 @@ require 'output_mode/errors'
29
29
 
30
30
  require 'output_mode/outputs'
31
31
  require 'output_mode/callable'
32
+ require 'output_mode/formatters'
32
33
  require 'output_mode/builder_dsl'
33
34
  require 'output_mode/tldr'
34
35
 
data/output_mode.gemspec CHANGED
@@ -57,4 +57,5 @@ Gem::Specification.new do |spec|
57
57
  spec.add_development_dependency "rake", ">= 12.3.3"
58
58
  spec.add_development_dependency "rspec", "~> 3.0"
59
59
  spec.add_development_dependency "pry", "> 0.11"
60
+ spec.add_development_dependency "pry-byebug", "~> 3.0"
60
61
  end
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.6.0
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-05-24 00:00:00.000000000 Z
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
@@ -123,6 +137,7 @@ files:
123
137
  - README.md
124
138
  - Rakefile
125
139
  - bin/bundle
140
+ - bin/byebug
126
141
  - bin/coderay
127
142
  - bin/console
128
143
  - bin/demo
@@ -137,6 +152,10 @@ files:
137
152
  - lib/output_mode/callable.rb
138
153
  - lib/output_mode/default_erb.rb
139
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
140
159
  - lib/output_mode/non_interactive_erb.rb
141
160
  - lib/output_mode/output.rb
142
161
  - lib/output_mode/outputs.rb