helium-console 0.1.9 → 0.1.13

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: 9bae4abb107ba24a4d9e30434e48cbfb8ff57b75b1bc5f636c2208a4af066b20
4
- data.tar.gz: 910786c0c312352c2a202bc82646c38135fa8e1890318ba4f91e4fc4f9955467
3
+ metadata.gz: a7a2bce9c0f3fe1f94baf382a4f23d10b9fab87164dddbce7736a00a10357cf4
4
+ data.tar.gz: 420ff6d68c766405c2f29a7ee37cd7fa4d78341ac52769341c4ed54f995ccd24
5
5
  SHA512:
6
- metadata.gz: 7d0cf4390043396e2dcf8f550b5ac4ef827dca0445abe9c740290a97d9c09c2986c1ab8b283870e2e9bf2821590c828033d86a6bd3247611a633f26f8d333009
7
- data.tar.gz: 26a26250b08bdf800e852887047f8c8848886bee69afa105bdd456688af6ee570a22fd17889624c5113d34b081f98bb1f5790ebb8be0733f62d841a089ff0730
6
+ metadata.gz: 83528cf238a38ca7de2a49534233a1e8c2bce5c761291b1115114e7ac814096e669008445f063fe2eb1f3cdd4ef12de02258547344098cf776bed721ed95d666
7
+ data.tar.gz: 19f87c531bf302a9d66d8eced560fe81303d728ea778937396d4d439fc987d5fd9a97a88c39976df3431a65ca469c702f0aacca232e57587bbade9fd4ba99de0
@@ -0,0 +1,37 @@
1
+ name: Pull request
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ jobs:
7
+ test:
8
+ name: Spec
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ ruby-version: ['2.6', '2.7', '3.0']
13
+ fail-fast: false
14
+
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - name: Set up Ruby
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby-version }}
21
+ bundler-cache: true
22
+ - name: Run tests
23
+ run: bundle exec rspec
24
+
25
+ lint:
26
+ name: Lint
27
+ runs-on: ubuntu-latest
28
+
29
+ steps:
30
+ - uses: actions/checkout@v2
31
+ - name: Set up Ruby
32
+ uses: ruby/setup-ruby@v1
33
+ with:
34
+ ruby-version: 2.7
35
+ bundler-cache: true
36
+ - name: Run tests
37
+ run: bundle exec rubocop
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
- --format documentation
2
1
  --color
3
2
  --require spec_helper
data/.rubocop.yml CHANGED
@@ -67,4 +67,22 @@ Style/NumericPredicate:
67
67
  Style/EachWithObject:
68
68
  Enabled: false
69
69
 
70
+ RSpec/MultipleMemoizedHelpers:
71
+ Enabled: false
72
+
73
+ RSpec/FilePath:
74
+ Enabled: false
75
+
76
+ RSpec/DescribeMethod:
77
+ Enabled: false
78
+
79
+ RSpec/DescribeClass:
80
+ Enabled: false
81
+
82
+ Metrics/ParameterLists:
83
+ CountKeywordArgs: false
84
+
85
+ Metrics/AbcSize:
86
+ Max: 20
87
+
70
88
 
data/Gemfile CHANGED
@@ -12,3 +12,4 @@ gem 'byebug'
12
12
  gem 'rubocop', require: false
13
13
  gem 'rubocop-rspec', require: false
14
14
  gem 'rubocop-rake', require: false
15
+ gem 'simplecov'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- helium-console (0.1.9)
4
+ helium-console (0.1.13)
5
5
  colorize
6
6
  pry
7
7
 
@@ -13,6 +13,7 @@ GEM
13
13
  coderay (1.1.3)
14
14
  colorize (0.8.1)
15
15
  diff-lcs (1.4.4)
16
+ docile (1.4.0)
16
17
  ffaker (2.18.0)
17
18
  method_source (1.0.0)
18
19
  parallel (1.20.1)
@@ -55,6 +56,12 @@ GEM
55
56
  rubocop (~> 1.0)
56
57
  rubocop-ast (>= 1.1.0)
57
58
  ruby-progressbar (1.11.0)
59
+ simplecov (0.21.2)
60
+ docile (~> 1.1)
61
+ simplecov-html (~> 0.11)
62
+ simplecov_json_formatter (~> 0.1)
63
+ simplecov-html (0.12.3)
64
+ simplecov_json_formatter (0.1.3)
58
65
  unicode-display_width (2.0.0)
59
66
 
60
67
  PLATFORMS
@@ -69,6 +76,7 @@ DEPENDENCIES
69
76
  rubocop
70
77
  rubocop-rake
71
78
  rubocop-rspec
79
+ simplecov
72
80
 
73
81
  BUNDLED WITH
74
82
  2.1.4
data/README.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # Helium::Console
2
2
 
3
+ Readable console output for your ruby project!
4
+
3
5
  It is really tricky to display data in the console in the readable and consistent way. Many objects needs to display other objects, which might break their own formatting.
4
- Helium:Console is to make it easier by formatting strings in accordance to current console size.
6
+ Helium:Console is an attempt to make your development console more readable by:
7
+ * limiting displayed nesting levels to 3
8
+ * limiting data displayed for nested objects
9
+ * using nested table layout
10
+ * automatically dividing and wrapping long strings
5
11
 
6
12
  ## Installation
7
13
 
@@ -21,24 +27,74 @@ Or install it yourself as:
21
27
 
22
28
  ## Usage
23
29
 
24
- Helium::Console helps you to format any string in such a way that it displays nicely in the console:
30
+ You can start helium console same way as you would start Pry:
31
+
32
+ ``` ruby
33
+ require 'helium/console'
34
+ Helium::Console.start
35
+ ```
36
+
37
+ ### Custom formatters
38
+
39
+ Helium::Console hooks into pry and brings a number of default formatters. Unlike IRB and Pry, it does not use object's methods for display
40
+ (so no `inspect` nor `pretty_print`) and replaces them by the collection of inheritable formatters objects stored in its registry.
41
+
42
+ Formatter can be any object that conforms to the following interface:
43
+ * `initialize(object_to_format, format, console_instance, **options)`
44
+ * `call` method returning any object responding to `lines` (e.g. `String` )
45
+
46
+ Formatter bellow will simply return a result of `inspect` call on the object:
47
+
48
+ ```ruby
49
+ class InspectFormatter
50
+ def initialize(object, _console, **)
51
+ @object = object
52
+ end
53
+
54
+ def call
55
+ @object.inspect
56
+ end
57
+ end
58
+ ```
59
+
60
+ You can register your formatter in console registry with:
25
61
 
62
+ ```ruby
63
+ Helium::Console.register(Kernel, InspectFormatter)
26
64
  ```
27
- string = "- Hello there!\nGeneral Kenobi"
28
- Helium::Console.format(string, indent: 2)
29
- => " - Hello there!\n General Kenobi"
65
+
66
+ The call above makes `InspectFormatter` available for all the objects that derives from Kernel module.
67
+
68
+ To make formatting easier, you can subclass your formatter from `Helium::Console::Registry::Element`. By doing so, the following methods will be available to you inside your formatter class:
69
+ * `object`, `console` and `options` readers
70
+ * default `call` implementation, delegating formatting to one
71
+ * `format(other_object, **options)` - formats some other object **using the exact same options, including nesting level**.
72
+ * `format_nested(other_object, **options)` - as above, but increases nesting level.
73
+ * `format_string(string, **options)` - formats string by splitting it into lines of appropriate length and truncating (depending on nesting level).
74
+ This is different to `format` and `format_nested` as it will not trigger `String` formatter (which by default adds quotes, escapes inner quotes and colors the result light green)
75
+ * `red(string)`, `light_red(string)`, `yellow(string)`, etc - returns colorized string when `Pry.color` is set to true.
76
+ * `length_of(string)` - utility option returning the length of displayed string, handling both colorized and non-colorized strings.
77
+
78
+ ### Displaying as a table
79
+
80
+ To display object in a form of a table, format instance of `Helium::Console::Table`:
81
+
82
+ ```ruby
83
+ class MyFormatter < Helium::Console::Registry::Element
84
+ def call
85
+ table = Helium::Console::Table.new(runner: '--@', after_key: '--@--', format_keys: false)
86
+ table.row magenta("property 1"), object.prop1
87
+ table.row magenta("property 2"), object.instance_variable_get(:@prop2)
88
+
89
+ format table
90
+ end
91
+ end
30
92
  ```
31
93
 
32
- When executed in a non-console environment `format` simply returns the string.
94
+ Table will automatically format all the right-hand values with an increased nesting level. By default, it will also format the left-hand keys, however this is controlled with `format_keys` option.
33
95
 
34
- Supported formattign options:
96
+ Other options: `runner` is a string to be displayed at the beginning of each line, and `after_key` is a string to be injected between left and right values.
35
97
 
36
- * `indent` - specifies the amount of spaces added to each new line. Also accepts hash `{first:, other:}`. Defaults to 0.
37
- * `overflow` - specifies how to handle lines longer than console line width.
38
- * `:wrap` - splits the long line into few lines and applies the required indent.
39
- * `:wrap_words` - similar to wrap, but will try to avoid breaking the words.
40
- * `:none` - does not modify long strings.
41
- * `max-lines` - specifies how many lines to display. Last line will be truncated with `...`. Defaults to `nil`
42
98
 
43
99
  ## Development
44
100
 
data/bin/console CHANGED
@@ -7,4 +7,4 @@ require 'helium/console'
7
7
  require 'byebug'
8
8
  require 'ffaker'
9
9
 
10
- Pry.start
10
+ Helium::Console.start
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'colorized_string'
4
+
5
+ module Helium
6
+ class Console
7
+ class ColorizedString < ColorizedString
8
+ def length
9
+ uncolorize.to_s.length
10
+ end
11
+
12
+ def colorize(*)
13
+ return self unless Pry.color
14
+
15
+ super
16
+ end
17
+
18
+ module Helpers
19
+ ColorizedString.colors.each do |color|
20
+ define_method(color) do |string|
21
+ ColorizedString.new(string).colorize(color)
22
+ end
23
+ end
24
+
25
+ def length_of(string)
26
+ ColorizedString.new(string).length
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Helium
4
+ class Console
5
+ class Formatter
6
+ DEFAULT_STYLES = {
7
+ 1 => [:full, {}],
8
+ 2 => [:partial, {}],
9
+ 3 => [:partial, { max_lines: 1 }]
10
+ }.freeze
11
+
12
+ class LazyStringEvaluator
13
+ def initialize(&block)
14
+ @lines = Enumerator.new { |y| block.(y) }
15
+ end
16
+
17
+ attr_reader :lines
18
+
19
+ def to_s
20
+ lines.to_a.join
21
+ end
22
+ end
23
+
24
+ def initialize(object, style, console, **options)
25
+ @object = object
26
+ @options = options
27
+ @style = style
28
+ @console = console
29
+ end
30
+
31
+ def call
32
+ method_name = [:render, @style].compact.join('_')
33
+ public_send(method_name)
34
+ end
35
+
36
+ def optimal_format
37
+ DEFAULT_STYLES.fetch(level) { [:compact, {}] }
38
+ end
39
+
40
+ def render
41
+ style, options = optimal_format
42
+ format(object, style, **options)
43
+ end
44
+
45
+ def render_full
46
+ render_partial
47
+ end
48
+
49
+ def render_partial
50
+ format_string(render_inline, max_width: max_width, indent: indent)
51
+ end
52
+
53
+ def render_inline
54
+ render_compact
55
+ end
56
+
57
+ def render_compact
58
+ raise NotImplementedError
59
+ end
60
+
61
+ attr_reader :object, :options
62
+
63
+ def format_nested(other_object, style = nil, **options)
64
+ @console.format(other_object, style, **nested_opts(options))
65
+ end
66
+
67
+ def format(other_object, style = nil, **options)
68
+ @console.format(other_object, style, **nested_opts(options, increase_level: false))
69
+ end
70
+
71
+ def format_string(string, **options)
72
+ @console.format_string(string, **options)
73
+ end
74
+
75
+ def simple?
76
+ false
77
+ end
78
+
79
+ ColorizedString.colors.each do |color|
80
+ define_method color do |string|
81
+ ColorizedString.new(string).colorize(color)
82
+ end
83
+ end
84
+
85
+ def method_missing(name, *args)
86
+ return @options[name] if @options.key?(name)
87
+
88
+ super
89
+ end
90
+
91
+ def respond_to_missing?(name, private = false)
92
+ @options.key?(name) || super
93
+ end
94
+
95
+ def build_key_value(&block)
96
+ key_value = KeyValue.new
97
+ block.(key_value)
98
+ key_value
99
+ end
100
+
101
+ def format_key_value(key_value, **options)
102
+ key_value.as_table(level: level, console: @console, max_width: max_width, **options)
103
+ end
104
+
105
+ private
106
+
107
+ def nested_objects
108
+ []
109
+ end
110
+
111
+ def nested_opts(new_options, increase_level: true)
112
+ new_options = options.merge(new_options)
113
+ new_options[:level] += 1 if increase_level
114
+ new_options[:ignore_objects] = nested_objects
115
+ new_options
116
+ end
117
+
118
+ def length_of(string)
119
+ ColorizedString.new(string).length
120
+ end
121
+
122
+ def yield_lines(&block)
123
+ LazyStringEvaluator.new(&block)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -10,6 +10,8 @@ module Helium
10
10
  end
11
11
 
12
12
  def call(string)
13
+ return string unless @max_width
14
+
13
15
  result = string.lines.flat_map do |line|
14
16
  line.chomp.chars.each_slice(@max_width).map(&:join)
15
17
  end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Helium
4
+ class Console
5
+ class KeyValue
6
+ def row(key, value, format_key: {}, format_value: {})
7
+ rows << [key, value, { format_key: format_key, format_value: format_value }]
8
+ end
9
+
10
+ def rows
11
+ @rows ||= []
12
+ end
13
+
14
+ def as_table(console:, max_width:, level:, left: '|', between: ' ', right: '', format_keys: {}, format_values: {})
15
+ TableFormatter.(
16
+ self,
17
+ console: console,
18
+ left: left,
19
+ between: between,
20
+ right: right,
21
+ key_options: format_keys,
22
+ value_options: format_values,
23
+ max_width: max_width,
24
+ level: level
25
+ )
26
+ end
27
+
28
+ class TableFormatter
29
+ include ColorizedString::Helpers
30
+
31
+ def self.call(*args, **options)
32
+ new(*args, **options).()
33
+ end
34
+
35
+ def initialize(
36
+ key_value,
37
+ console:,
38
+ level:,
39
+ left:,
40
+ between:,
41
+ right:,
42
+ max_width:,
43
+ key_options: {},
44
+ value_options: {}
45
+ )
46
+ @key_value = key_value
47
+ @console = console
48
+ @left = left
49
+ @between = between
50
+ @right = right
51
+ @max_width = max_width
52
+ @key_options = key_options
53
+ @level = level
54
+ @value_options = value_options
55
+
56
+ set_width_limits
57
+ end
58
+
59
+ def call
60
+ Formatter::LazyStringEvaluator.new do |y|
61
+ key_value.rows.each do |key, value, options|
62
+ format_entry(y, key, value, **options)
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ attr_reader :key_value, :console, :left, :between, :right, :key_options, :value_options, :max_width
70
+
71
+ def format_entry(yielder, key, value, format_key:, format_value:)
72
+ key = format(key, merge_options(key_options, format_key))
73
+ value = format(value, merge_options(value_options, format_value))
74
+ zip_lines(key, value) do |key_line, value_line, index|
75
+ yielder << [
76
+ left,
77
+ rjust(key_line, key_width),
78
+ index.zero? ? between : ' ' * length_of(between),
79
+ value_line,
80
+ right
81
+ ].join
82
+ end
83
+ end
84
+
85
+ def key_width
86
+ @key_width ||= key_value.rows.first(100).map do |key, _, format_key:, **|
87
+ length_of(format(key, merge_options(key_options, format_key)))
88
+ end.max
89
+ end
90
+
91
+ def format(object, options = {})
92
+ return object unless options
93
+
94
+ console.format(object, options.delete(:style), level: @level + 1, **options)
95
+ end
96
+
97
+ def merge_options(opt1, opt2)
98
+ return false unless opt1 && opt2
99
+
100
+ opt1.merge(opt2)
101
+ end
102
+
103
+ def set_default(hash, key, &block)
104
+ hash[key] = block.() if hash && !hash[key]
105
+ end
106
+
107
+ def zip_lines(str1, str2)
108
+ str1 = (str1.lines.each + [nil].cycle).lazy
109
+ str2 = (str2.lines.each + [nil].cycle).lazy
110
+ str1.zip(str2).with_index.each do |(line1, line2), index|
111
+ break unless line1 || line2
112
+
113
+ yield [line1 || '', line2 || '', index]
114
+ end
115
+ end
116
+
117
+ def set_width_limits
118
+ set_default(key_options, :max_width) { max_width / 3 }
119
+ set_default(value_options, :max_width) do
120
+ max_width - key_width - length_of(left) - length_of(between) - length_of(right)
121
+ end
122
+ end
123
+
124
+ def rjust(string, length)
125
+ ' ' * (length - length_of(string)) + string
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -11,12 +11,24 @@ module Helium
11
11
  end
12
12
 
13
13
  def pp(object)
14
- formatted = Helium::Console.format(object)
15
- formatted = "\n#{formatted}" if formatted.lines.count > 1
16
- output << formatted
14
+ formatted = Helium::Console.format(object, max_width: maxwidth)
15
+ output << "\n" if multiline?(formatted)
16
+
17
+ formatted.lines.each.with_index do |line, index|
18
+ output << "\n" unless index.zero?
19
+ output << line.chomp
20
+ end
21
+ end
22
+
23
+ def multiline?(formatted)
24
+ result = formatted.is_a?(Formatter::LazyStringEvaluator)
25
+ result ||= formatted.lines.count > 1
26
+ result || length_of(formatted.chomp) >= maxwidth - 2
17
27
  end
18
28
 
19
- ::Pry.config.print = method(:default)
29
+ def length_of(str)
30
+ ColorizedString.new(str).uncolorize.length
31
+ end
20
32
  end
21
33
  end
22
34
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Helium
4
+ class Console
5
+ class Prompt
6
+ def initialize
7
+ @line = 0
8
+ end
9
+
10
+ def pry_prompt
11
+ Pry::Prompt.new(
12
+ 'helium',
13
+ 'Default prompt for helium',
14
+ [
15
+ method(:active_prompt),
16
+ method(:wait_prompt)
17
+ ]
18
+ )
19
+ end
20
+
21
+ private
22
+
23
+ def active_prompt(context, _nesting, _pry)
24
+ @line += 1
25
+ str = [
26
+ ColorizedString.new("[#{@line}]").light_black,
27
+ ColorizedString.new("He\u269B").light_blue,
28
+ ColorizedString.new("(#{context.inspect})").magenta
29
+ ].join(' ')
30
+ "#{str}> "
31
+ end
32
+
33
+ def wait_prompt(context, _nesting, _pry)
34
+ @line += 1
35
+ str = [
36
+ ColorizedString.new("[#{@line}]").light_black,
37
+ ' ',
38
+ ColorizedString.new("(#{context.inspect})").magenta
39
+ ].join(' ')
40
+ "#{str}> "
41
+ end
42
+ end
43
+ end
44
+ end
@@ -3,13 +3,23 @@
3
3
  module Helium
4
4
  class Console
5
5
  define_formatter_for Array do
6
- def call
6
+ def render_compact
7
+ return '[]' unless object.any?
8
+
9
+ "##{format object.class, :compact}#{light_black "[#{object.size}]"}"
10
+ end
11
+
12
+ def render_partial
7
13
  return '[]' if object.none?
8
14
  return inline_with_truncation if force_inline?
9
15
 
10
16
  inline_without_truncation || format_as_table
11
17
  end
12
18
 
19
+ def render_inline
20
+ inline_with_truncation
21
+ end
22
+
13
23
  def simple?
14
24
  object.none?
15
25
  end
@@ -17,22 +27,23 @@ module Helium
17
27
  private
18
28
 
19
29
  def format_as_table
20
- table = Table.new(runner: ' ', format_keys: false)
21
- object.each.with_index do |element, index|
22
- table.row(light_black("[#{index}]:"), element)
30
+ key_value = build_key_value do |kv|
31
+ object.each.with_index do |element, index|
32
+ kv.row(light_black("[#{index}]:"), element)
33
+ end
23
34
  end
24
35
 
25
- [
26
- '[',
27
- format(table),
28
- ']'
29
- ].join("\n")
36
+ yield_lines do |y|
37
+ y << '['
38
+ format_key_value(key_value, left: ' ', format_keys: false).lines.each { |line| y << line }
39
+ y << ']'
40
+ end
30
41
  end
31
42
 
32
43
  def inline_with_truncation
33
44
  formatted = formatted_elements.with_index.inject([]) do |joined, (element, index)|
34
45
  new_joined = [*joined[0..-2], element, trunc_message(object_size - index - 1, all_truncated: index.zero?)]
35
- break joined if too_long?(new_joined, max_width: max_width - 4)
46
+ break joined if max_width && too_long?(new_joined, max_width: max_width - 4)
36
47
 
37
48
  new_joined
38
49
  end
@@ -65,7 +76,7 @@ module Helium
65
76
  def trunc_message(count, all_truncated: false)
66
77
  return if count < 1
67
78
 
68
- "(#{count} #{all_truncated ? 'elements' : 'more'})"
79
+ light_black "(#{count} #{all_truncated ? 'elements' : 'more'})"
69
80
  end
70
81
 
71
82
  def object_size
@@ -3,13 +3,13 @@
3
3
  module Helium
4
4
  class Console
5
5
  define_formatter_for TrueClass do
6
- def call
6
+ def render_compact
7
7
  green('true')
8
8
  end
9
9
  end
10
10
 
11
11
  define_formatter_for FalseClass do
12
- def call
12
+ def render_compact
13
13
  red('false')
14
14
  end
15
15
  end