helium-console 0.1.9 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
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