eskimo 1.0.0 → 2.0.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/eskimo.rb +20 -4
  3. data/lib/eskimo/component.rb +2 -2
  4. data/lib/eskimo/components/did_you_mean.rb +34 -0
  5. data/lib/eskimo/components/either.rb +30 -0
  6. data/lib/eskimo/components/highlight.rb +31 -0
  7. data/lib/eskimo/components/highlight_column.rb +61 -0
  8. data/lib/eskimo/components/indent.rb +25 -0
  9. data/lib/eskimo/components/line_break.rb +18 -0
  10. data/lib/eskimo/components/soft_break.rb +16 -0
  11. data/lib/eskimo/components/squeeze.rb +43 -0
  12. data/lib/eskimo/components/strip.rb +15 -0
  13. data/lib/eskimo/components/strip_left.rb +15 -0
  14. data/lib/eskimo/components/strip_right.rb +15 -0
  15. data/lib/eskimo/components/style.rb +25 -0
  16. data/lib/eskimo/components/truncate.rb +31 -0
  17. data/lib/eskimo/components/truncate_rear.rb +25 -0
  18. data/lib/eskimo/components/wrap.rb +26 -0
  19. data/lib/eskimo/constants.rb +7 -0
  20. data/lib/eskimo/renderer.rb +15 -17
  21. data/lib/eskimo/version.rb +1 -1
  22. data/spec/component_spec.rb +13 -0
  23. data/spec/components/did_you_mean_spec.rb +21 -0
  24. data/spec/components/either_spec.rb +21 -0
  25. data/spec/components/highlight_column_spec.rb +63 -0
  26. data/spec/components/highlight_spec.rb +25 -0
  27. data/spec/components/indent_spec.rb +15 -0
  28. data/spec/components/line_break_spec.rb +17 -0
  29. data/spec/components/soft_break_spec.rb +17 -0
  30. data/spec/components/squeeze_spec.rb +61 -0
  31. data/spec/components/strip_left_spec.rb +15 -0
  32. data/spec/components/strip_right_spec.rb +15 -0
  33. data/spec/components/strip_spec.rb +15 -0
  34. data/spec/components/style_spec.rb +25 -0
  35. data/spec/components/truncate_rear_spec.rb +35 -0
  36. data/spec/components/truncate_spec.rb +35 -0
  37. data/spec/components/wrap_spec.rb +15 -0
  38. data/spec/spec_helper.rb +8 -0
  39. data/spec/support/component_suite.rb +15 -0
  40. metadata +52 -5
  41. data/lib/eskimo/components.rb +0 -114
  42. data/spec/components_spec.rb +0 -159
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eskimo
4
+ module Components
5
+ # Wrap a block text with newlines at a certain threshold.
6
+ #
7
+ # Wrap.new(width: 3) do
8
+ # "foobar"
9
+ # end
10
+ # # => "foo"
11
+ # # "bar"
12
+ #
13
+ class Wrap < Component
14
+ attr_reader :width
15
+
16
+ def initialize(width: Constants::SCREEN_COLUMNS, &children)
17
+ @width = width
18
+ super
19
+ end
20
+
21
+ def render(**)
22
+ Strings.wrap(super, width)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eskimo
4
+ module Constants
5
+ SCREEN_COLUMNS = [TTY::Screen.width, 72].min
6
+ end
7
+ end
@@ -18,22 +18,23 @@ module Eskimo
18
18
  private
19
19
 
20
20
  def render(*components)
21
- components.select(&NOT_FALSEY).reduce('') do |buf, component|
22
- case component
23
- when String
24
- buf + component
25
- when Array
26
- buf + render(*component)
27
- when Proc
28
- buf + render(component[**@props])
29
- else
30
- if component.respond_to?(:render)
31
- buf + render(component.render(**@props))
32
- else
33
- bail(component)
34
- end
21
+ buf = +''
22
+
23
+ for component in components do
24
+ if component.is_a?(String)
25
+ buf << component
26
+ elsif component.is_a?(Array)
27
+ buf << render(*component)
28
+ elsif component.is_a?(Proc)
29
+ buf << render(component[**@props])
30
+ elsif component.respond_to?(:render)
31
+ buf << render(component.render(**@props))
32
+ elsif component
33
+ bail(component)
35
34
  end
36
35
  end
36
+
37
+ buf
37
38
  end
38
39
 
39
40
  def bail(component)
@@ -41,8 +42,5 @@ module Eskimo
41
42
  "Eskimo: don't know how to render #{component.class} => #{component}"
42
43
  )
43
44
  end
44
-
45
- NOT_FALSEY = ->(x) { !!x }.freeze
46
- private_constant :NOT_FALSEY
47
45
  end
48
46
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eskimo
4
- VERSION = '1.0.0'
4
+ VERSION = '2.0.0'
5
5
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Component do
6
+ it 'accepts a block of children' do
7
+ expect(
8
+ Eskimo::Renderer.new.apply {
9
+ Eskimo::Component.new { ['hello', "\n", 'world!'] }
10
+ }
11
+ ).to eq("hello\nworld!")
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::DidYouMean do
6
+ it 'renders a suggestion' do
7
+ expect(
8
+ renderer.apply {
9
+ described_class.new(dictionary: [ 'abc', 'bca' ], item: 'abd')
10
+ }
11
+ ).to eq("Did you mean? abc")
12
+ end
13
+
14
+ it 'renders nothing if no suggestions were found' do
15
+ expect(
16
+ renderer.apply {
17
+ described_class.new(dictionary: [ 'abc', 'bca' ], item: 'asdf')
18
+ }
19
+ ).to eq("")
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::Either do
6
+ it 'renders the primary component' do
7
+ expect(
8
+ renderer.apply {
9
+ described_class.new(->(**) { false }, "Fallback here")
10
+ }
11
+ ).to eq("Fallback here")
12
+ end
13
+
14
+ it 'renders the fallback if the primary does not' do
15
+ expect(
16
+ renderer.apply {
17
+ described_class.new(->(**) { "Hi!" }, "Fallback here")
18
+ }
19
+ ).to eq("Hi!")
20
+ end
21
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::HighlightColumn do
6
+ it 'highlights the character' do
7
+ style = [ :bold, :red ]
8
+
9
+ expect(
10
+ renderer.apply {
11
+ described_class.new(column: 1, line: 1, style: style) { "number:\nfour" }
12
+ }
13
+ ).to include("f#{pastel.decorate('o', *style)}ur")
14
+ end
15
+
16
+ it 'highlights the first character' do
17
+ style = [ :bold, :red ]
18
+
19
+ expect(
20
+ renderer.apply {
21
+ described_class.new(column: 0, line: 0, style: style) { "four" }
22
+ }
23
+ ).to include("#{pastel.decorate('f', *style)}our")
24
+ end
25
+
26
+ it 'highlights the last character' do
27
+ style = [ :bold, :red ]
28
+
29
+ expect(
30
+ renderer.apply {
31
+ described_class.new(column: 3, line: 0, style: style) { "four" }
32
+ }
33
+ ).to include("fou#{pastel.decorate('r', *style)}")
34
+ end
35
+
36
+ it 'highlights nothing' do
37
+ expect(
38
+ renderer.apply {
39
+ described_class.new(column: 1234, line: 0) { "four" }
40
+ }
41
+ ).to eq("four")
42
+
43
+ expect(
44
+ renderer.apply {
45
+ described_class.new(column: 0, line: 1234) { "four" }
46
+ }
47
+ ).to eq("four")
48
+ end
49
+
50
+ it 'inserts the arrow' do
51
+ expect(
52
+ strip_styles(
53
+ renderer.apply {
54
+ described_class.new(column: 1, line: 0) { "four" }
55
+ }
56
+ )
57
+ ).to eq(
58
+ "four" + "\n" +
59
+ " ^" + "\n" +
60
+ " here" + "\n"
61
+ )
62
+ end
63
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::Highlight do
6
+ it 'styles a substring' do
7
+ style = [ :bold, :red ]
8
+
9
+ expect(
10
+ renderer.apply {
11
+ described_class.new(pattern: /ou/, style: style) { 'four' }
12
+ }
13
+ ).to eq("f#{Pastel.new.decorate('ou', *style)}r")
14
+ end
15
+
16
+ it 'is a no-op if no match' do
17
+ input = 'four'
18
+
19
+ expect(
20
+ renderer.apply {
21
+ described_class.new(pattern: /asdf/) { input }
22
+ }
23
+ ).to eq(input)
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::Indent do
6
+ it 'indents each line' do
7
+ expect(
8
+ renderer.apply do
9
+ ESK::Indent.new(width: 2) do
10
+ "hello\nworld"
11
+ end
12
+ end
13
+ ).to eq(" hello\n world")
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::LineBreak do
6
+ it 'inserts a hard linebreak' do
7
+ expect(
8
+ renderer.apply {
9
+ [
10
+ 'hello',
11
+ ESK::LineBreak.new,
12
+ 'world'
13
+ ]
14
+ }
15
+ ).to eq("hello\n \nworld")
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::SoftBreak do
6
+ it 'inserts a soft linebreak' do
7
+ expect(
8
+ renderer.apply {
9
+ [
10
+ 'hello',
11
+ ESK::SoftBreak.new,
12
+ 'world'
13
+ ]
14
+ }
15
+ ).to eq("hello\nworld")
16
+ end
17
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::Squeeze do
6
+ it 'swallows consecutive newlines' do
7
+ expect(
8
+ renderer.apply do
9
+ ESK::Squeeze.new([
10
+ ESK::SoftBreak.new,
11
+ false && '1',
12
+
13
+ ESK::SoftBreak.new,
14
+ false && '2',
15
+
16
+ ESK::SoftBreak.new,
17
+ '3',
18
+
19
+ ESK::SoftBreak.new,
20
+ '4',
21
+ ])
22
+ end
23
+ ).to eq("\n3\n4")
24
+ end
25
+
26
+ it 'does not swallow consecutive newlines in inner children' do
27
+ expect(
28
+ renderer.apply do
29
+ ESK::Squeeze.new([
30
+ ESK::Indent.new(width: 0) do
31
+ [
32
+ ESK::SoftBreak.new,
33
+ false && '1',
34
+
35
+ ESK::SoftBreak.new,
36
+ false && '2',
37
+
38
+ ESK::SoftBreak.new,
39
+ '3',
40
+
41
+ ESK::SoftBreak.new,
42
+ '4',
43
+ ]
44
+ end
45
+ ])
46
+ end
47
+ ).to eq("\n\n\n3\n4")
48
+ end
49
+
50
+ it 'bails if not given an array of children' do
51
+ expect { ESK::Squeeze.new('') }.to raise_error(ArgumentError,
52
+ "Squeeze works only with an Array of components"
53
+ )
54
+ end
55
+
56
+ it 'bails if given a block' do
57
+ expect { ESK::Squeeze.new([]) {} }.to raise_error(ArgumentError,
58
+ "Squeeze works only with an Array of components"
59
+ )
60
+ end
61
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::StripLeft do
6
+ it 'strips whitespace from the head' do
7
+ expect(
8
+ renderer.apply do
9
+ ESK::StripLeft.new do
10
+ " hello world "
11
+ end
12
+ end
13
+ ).to eq("hello world ")
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::StripRight do
6
+ it 'strips whitespace from the rear' do
7
+ expect(
8
+ renderer.apply do
9
+ ESK::StripRight.new do
10
+ " hello world "
11
+ end
12
+ end
13
+ ).to eq(" hello world")
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::Strip do
6
+ it 'strips surrounding whitespace' do
7
+ expect(
8
+ renderer.apply do
9
+ ESK::Strip.new do
10
+ " hello world "
11
+ end
12
+ end
13
+ ).to eq("hello world")
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::Style do
6
+ it 'applies ANSI styling to text' do
7
+ expect(
8
+ renderer.apply {
9
+ [
10
+ ESK::Style.new([:bold, :green]) { 'hai' },
11
+ ]
12
+ }
13
+ ).to eq("\e[1;32mhai\e[0m")
14
+ end
15
+
16
+ it 'accepts a splat' do
17
+ expect(
18
+ renderer.apply {
19
+ [
20
+ ESK::Style.new(:bold, :green) { 'hai' },
21
+ ]
22
+ }
23
+ ).to eq("\e[1;32mhai\e[0m")
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Eskimo::Components::TruncateRear do
6
+ it 'truncates a string from the rear if it exceeds the length' do
7
+ expect(
8
+ renderer.apply do
9
+ ESK::TruncateRear.new(width: 5) do
10
+ "hello world"
11
+ end
12
+ end
13
+ ).to eq("hello...")
14
+ end
15
+
16
+ it 'reserves chars from the width' do
17
+ expect(
18
+ renderer.apply do
19
+ ESK::TruncateRear.new(width: 10, reserve: 5) do
20
+ ("hello world")
21
+ end
22
+ end
23
+ ).to eq("hello...")
24
+ end
25
+
26
+ it 'is a no-op if text fits' do
27
+ expect(
28
+ renderer.apply do
29
+ ESK::TruncateRear.new(width: 62, reserve: 5) do
30
+ ("hello world")
31
+ end
32
+ end
33
+ ).to eq("hello world")
34
+ end
35
+ end