eskimo 1.0.0 → 2.0.0

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