strings 0.1.2 → 0.1.3

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: 1f0c1f1d9734d9a20fa740876e47ab98722911deb76c564199562a70ec02459d
4
- data.tar.gz: 625b86584afb6a40eccca247e4bf2caf8ef4fcabc6aaeb38808c20d3566383a0
3
+ metadata.gz: 2c169560954048d363eb109d4c2098c122d2882d3a4f52ba41065cf9ec14411e
4
+ data.tar.gz: 051f5e21eb97227609ccc87f4e46b13c9ab48d9bf181435599872f335756b74e
5
5
  SHA512:
6
- metadata.gz: 278e68155478185904c5ce9348f1d364e78a40108a05c58302f54210a43973e058b4e8ceec7b1b82bc03ee8b67bbf600c065cdb1c8e308fb55a4a42383e6ffb6
7
- data.tar.gz: c3605edfbc62617c18e7de90c1efe773cbee290b335c29fb43dd30164265fcadac5ec4ed58acffd3d9850214ce99629f1a8a58ab401ef3448d8352c21597b566
6
+ metadata.gz: 27f4263a2874d1b3cee622a31254cf63dcd92d88c19208339f984ddbb3193088ff7fab4a5d0768114c262a57c70320c11534ef4e5eb1eb75521e677c58505776
7
+ data.tar.gz: ec8ee3980d43c8a7e7481c5e6b1c3d60d4423e7fa3eaaebffc49b2dfd558a019e073a4267283e1ef6008bf5fa2699ef9e1f86f45e538445f99e337dbccd8ee54
@@ -1,5 +1,10 @@
1
1
  # Change log
2
2
 
3
+ ## [v0.1.3] - 2018-08-28
4
+
5
+ ### Changed
6
+ * Change to extract Strings::ANSI to strings-ansi gem
7
+
3
8
  ## [v0.1.2] - 2018-08-10
4
9
 
5
10
  ### Changed
@@ -14,6 +19,7 @@
14
19
 
15
20
  * Inital implementation and release
16
21
 
17
- [v0.1.2]: https://github.com/piotrmurach/verse/compare/v0.1.1...v0.1.2
18
- [v0.1.1]: https://github.com/piotrmurach/verse/compare/v0.1.0...v0.1.1
19
- [v0.1.0]: https://github.com/piotrmurach/verse/compare/v0.1.0
22
+ [v0.1.3]: https://github.com/piotrmurach/strings/compare/v0.1.1...v0.1.3
23
+ [v0.1.2]: https://github.com/piotrmurach/strings/compare/v0.1.1...v0.1.2
24
+ [v0.1.1]: https://github.com/piotrmurach/strings/compare/v0.1.0...v0.1.1
25
+ [v0.1.0]: https://github.com/piotrmurach/strings/compare/v0.1.0
data/README.md CHANGED
@@ -56,6 +56,7 @@ Or install it yourself as:
56
56
  * [2.6 truncate](#26-truncate)
57
57
  * [2.7 wrap](#27-wrap)
58
58
  * [3. Extending String class](#3-extending-string-class)
59
+ * [4. Utilities](#4-utilities)
59
60
 
60
61
  ## 1. Usage
61
62
 
@@ -367,6 +368,14 @@ require 'strings/extensions'
367
368
  using Strings::Extensions
368
369
  ```
369
370
 
371
+ ## 4. Utilities
372
+
373
+ **Strings** aims to be fleaxible and allow you to choose only the utilities that you need. Currently you can choose from:
374
+
375
+ | Utility | Description | API docs |
376
+ | ------------ | ----------- | -------- |
377
+ | [strings-ansi](https://github.com/piotrmurach/strings-ansi) | Handle ANSI escape codes in strings. | [docs](http://www.rubydoc.info/gems/strings-ansi) |
378
+
370
379
  ## Development
371
380
 
372
381
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'strings-ansi'
4
+
3
5
  require_relative 'strings/align'
4
- require_relative 'strings/ansi'
5
6
  require_relative 'strings/fold'
6
7
  require_relative 'strings/pad'
7
8
  require_relative 'strings/truncate'
@@ -84,8 +85,8 @@ module Strings
84
85
  # @see Strings::ANSI#sanitize
85
86
  #
86
87
  # @api public
87
- def sanitize(text)
88
- ANSI.sanitize(text)
88
+ def sanitize(string)
89
+ ANSI.sanitize(string)
89
90
  end
90
91
  module_function :sanitize
91
92
 
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'strings-ansi'
3
4
  require 'unicode/display_width'
4
5
 
5
- require_relative 'ansi'
6
-
7
6
  module Strings
8
7
  # Responsible for text alignment
9
8
  module Align
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'strings-ansi'
3
4
  require 'unicode/display_width'
4
5
 
5
- require_relative 'ansi'
6
6
  require_relative 'padder'
7
7
 
8
8
  module Strings
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'strings-ansi'
3
4
  require 'unicode/display_width'
4
5
  require 'unicode_utils/each_grapheme'
5
6
 
6
- require_relative 'ansi'
7
-
8
7
  module Strings
9
8
  # A module responsible for text truncation
10
9
  module Truncate
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Strings
2
- VERSION = '0.1.2'.freeze
3
- end
4
+ VERSION = '0.1.3'
5
+ end # Strings
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'strings-ansi'
3
4
  require 'unicode/display_width'
4
5
  require 'unicode_utils/each_grapheme'
5
6
 
6
- require_relative 'ansi'
7
7
  require_relative 'fold'
8
8
 
9
9
  module Strings
@@ -0,0 +1,37 @@
1
+ if ENV['COVERAGE'] || ENV['TRAVIS']
2
+ require 'simplecov'
3
+ require 'coveralls'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
9
+
10
+ SimpleCov.start do
11
+ command_name 'spec'
12
+ add_filter 'spec'
13
+ end
14
+ end
15
+
16
+ require "bundler/setup"
17
+ require "strings"
18
+
19
+ module Helpers
20
+ def unindent(text)
21
+ text.gsub(/^[ \t]*/, '').chomp
22
+ end
23
+ end
24
+
25
+ RSpec.configure do |config|
26
+ config.include(Helpers)
27
+
28
+ # Enable flags like --only-failures and --next-failure
29
+ config.example_status_persistence_file_path = ".rspec_status"
30
+
31
+ # Disable RSpec exposing methods globally on `Module` and `main`
32
+ config.disable_monkey_patching!
33
+
34
+ config.expect_with :rspec do |c|
35
+ c.syntax = :expect
36
+ end
37
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings::Align, '#align_left' do
4
+ it "aligns line to left" do
5
+ text = "the madness of men"
6
+ expect(Strings::Align.align_left(text, 22)).to eq("the madness of men ")
7
+ end
8
+
9
+ it "fills empty" do
10
+ expect(Strings::Align.align_left('', 22)).to eq(" ")
11
+ end
12
+
13
+ it "left justifies string with unicode characters" do
14
+ text = "こんにちは"
15
+ expect(Strings::Align.align(text, 20, direction: :left)).to eq("こんにちは ")
16
+ end
17
+
18
+ it "left justifies string with ansi codes" do
19
+ text = "\e[32mthe madness of men\e[0m"
20
+ expect(Strings::Align.align(text, 22, direction: :left)).to eq("\e[32mthe madness of men\e[0m ")
21
+ end
22
+
23
+ it "aligns multiline text to left" do
24
+ text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
25
+ expect(Strings::Align.align_left(text, 40)).to eq([
26
+ "for there is no folly of the beast \n",
27
+ "of the earth which \n",
28
+ "is not infinitely \n",
29
+ "outdone by the madness of men "
30
+ ].join)
31
+ end
32
+
33
+ it "left justifies multiline utf text" do
34
+ text = "ラドクリフ\n、マラソン五輪\n代表に1万m出\n場にも含み"
35
+ expect(Strings::Align.align_left(text, 20)).to eq([
36
+ "ラドクリフ \n",
37
+ "、マラソン五輪 \n",
38
+ "代表に1万m出 \n",
39
+ "場にも含み "
40
+ ].join)
41
+ end
42
+
43
+ it "left justifies ansi text" do
44
+ text = "for \e[35mthere\e[0m is no folly of the beast\nof the \e[33mearth\e0m which\nis \e[34mnot infinitely\e[0m\n\e[33moutdone\e[0m by the madness of men"
45
+ expect(Strings::Align.align_left(text, 40)).to eq([
46
+ "for \e[35mthere\e[0m is no folly of the beast \n",
47
+ "of the \e[33mearth\e0m which \n",
48
+ "is \e[34mnot infinitely\e[0m \n",
49
+ "\e[33moutdone\e[0m by the madness of men "
50
+ ].join)
51
+ end
52
+
53
+ it "left justifies multiline text with fill of '*'" do
54
+ text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
55
+ expect(Strings::Align.align_left(text, 40, fill: '*')).to eq([
56
+ "for there is no folly of the beast******\n",
57
+ "of the earth which**********************\n",
58
+ "is not infinitely***********************\n",
59
+ "outdone by the madness of men***********"
60
+ ].join)
61
+ end
62
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings::Align, '#align_right' do
4
+ it "aligns line to right" do
5
+ text = "the madness of men"
6
+ expect(Strings::Align.align_right(text, 22)).to eq(" the madness of men")
7
+ end
8
+
9
+ it "fills empty" do
10
+ expect(Strings::Align.align_right('', 22)).to eq(" ")
11
+ end
12
+
13
+ it "aligns string to the right with unicode characters" do
14
+ text = "こんにちは"
15
+ expect(Strings::Align.align(text, 20, direction: :right)).to eq(" こんにちは")
16
+ end
17
+
18
+ it "aligns string to the right with ansi codees" do
19
+ text = "\e[32mthe madness of men\e[0m"
20
+ expect(Strings::Align.align(text, 22, direction: :right)).to eq(" \e[32mthe madness of men\e[0m")
21
+ end
22
+
23
+ it "aligns multiline text to the right" do
24
+ text = "for there is no folly of the beast\n of the earth which\n is not infinitely\n outdone by the madness of men"
25
+ expect(Strings::Align.align_right(text, 40)).to eq([
26
+ " for there is no folly of the beast\n",
27
+ " of the earth which\n",
28
+ " is not infinitely\n",
29
+ " outdone by the madness of men"
30
+ ].join)
31
+ end
32
+
33
+ it "aligns multiline text to right with unicode characters" do
34
+ text = "ラドクリフ\n、マラソン五輪\n代表に1万m出\n場にも含み"
35
+ expect(Strings::Align.align_right(text, 20)).to eq([
36
+ " ラドクリフ\n",
37
+ " 、マラソン五輪\n",
38
+ " 代表に1万m出\n",
39
+ " 場にも含み"
40
+ ].join)
41
+ end
42
+
43
+ it "right justfies ansi text" do
44
+ text = "for \e[35mthere\e[0m is no folly of the beast\nof the \e[33mearth\e0m which\nis \e[34mnot infinitely\e[0m\n\e[33moutdone\e[0m by the madness of men"
45
+ expect(Strings::Align.align_right(text, 40)).to eq([
46
+ " for \e[35mthere\e[0m is no folly of the beast\n",
47
+ " of the \e[33mearth\e0m which\n",
48
+ " is \e[34mnot infinitely\e[0m\n",
49
+ " \e[33moutdone\e[0m by the madness of men"
50
+ ].join)
51
+ end
52
+
53
+ it "right justifies multiline text with fill of '*'" do
54
+ text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
55
+ expect(Strings::Align.align_right(text, 40, fill: '*')).to eq([
56
+ "******for there is no folly of the beast\n",
57
+ "**********************of the earth which\n",
58
+ "***********************is not infinitely\n",
59
+ "***********outdone by the madness of men"
60
+ ].join)
61
+ end
62
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings::Align, '#align' do
4
+ it "doesn't align unrecognized direction" do
5
+ text = "the madness of men"
6
+ expect {
7
+ Strings::Align.align(text, 22, direction: :unknown)
8
+ }.to raise_error(ArgumentError, /Unknown alignment/)
9
+ end
10
+
11
+ it "fills empty" do
12
+ expect(Strings::Align.align('', 22, direction: :center)).to eq(" ")
13
+ end
14
+
15
+ it "centers line" do
16
+ text = "the madness of men"
17
+ expect(Strings::Align.align_center(text, 22)).to eq(" the madness of men ")
18
+ end
19
+
20
+ it "centers unicode characters" do
21
+ text = "こんにちは"
22
+ expect(Strings::Align.align_center(text, 20)).to eq(" こんにちは ")
23
+ end
24
+
25
+ it "centers string with ansi codes" do
26
+ text = "\e[32mthe madness of men\e[0m"
27
+ expect(Strings::Align.align_center(text, 22)).to eq(" \e[32mthe madness of men\e[0m ")
28
+ end
29
+
30
+ it "centers multiline text" do
31
+ text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
32
+ expect(Strings::Align.align_center(text, 40)).to eq([
33
+ " for there is no folly of the beast \n",
34
+ " of the earth which \n",
35
+ " is not infinitely \n",
36
+ " outdone by the madness of men "
37
+ ].join)
38
+ end
39
+
40
+ it "centers multiline unicode text" do
41
+ text = "ラドクリフ\n、マラソン五輪\n代表に1万m出\n場にも含み"
42
+ expect(Strings::Align.align_center(text, 20)).to eq([
43
+ " ラドクリフ \n",
44
+ " 、マラソン五輪 \n",
45
+ " 代表に1万m出 \n",
46
+ " 場にも含み "
47
+ ].join)
48
+ end
49
+
50
+ it "centers text with ansi codes" do
51
+ text = "for \e[35mthere\e[0m is no folly of the beast\nof the \e[33mearth\e0m which\nis \e[34mnot infinitely\e[0m\n\e[33moutdone\e[0m by the madness of men"
52
+ expect(Strings::Align.align_center(text, 40)).to eq([
53
+ " for \e[35mthere\e[0m is no folly of the beast \n",
54
+ " of the \e[33mearth\e0m which \n",
55
+ " is \e[34mnot infinitely\e[0m \n",
56
+ " \e[33moutdone\e[0m by the madness of men "
57
+ ].join)
58
+ end
59
+
60
+ it "centers multiline text with fill character '*'" do
61
+ text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
62
+ expect(Strings::Align.align(text, 40, direction: :center, fill: '*')).to eq([
63
+ "***for there is no folly of the beast***\n",
64
+ "***********of the earth which***********\n",
65
+ "***********is not infinitely************\n",
66
+ "*****outdone by the madness of men******"
67
+ ].join)
68
+ end
69
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings, '#align' do
4
+ it "aligns text" do
5
+ text = "the madness of men"
6
+ expect(Strings.align(text, 22, direction: :center)).to eq(" the madness of men ")
7
+ end
8
+
9
+ it "aligns text to left" do
10
+ text = "the madness of men"
11
+ expect(Strings.align_left(text, 22)).to eq("the madness of men ")
12
+ end
13
+
14
+ it "aligns text to right" do
15
+ text = "the madness of men"
16
+ expect(Strings.align_right(text, 22)).to eq(" the madness of men")
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings, '#ansi?' do
4
+ it "checks if string has any ansi codes" do
5
+ expect(Strings.ansi?("\e[33mfoo\e[0m")).to eq(true)
6
+ end
7
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strings/extensions'
4
+
5
+ using Strings::Extensions
6
+
7
+ RSpec.describe Strings::Extensions do
8
+ let(:text) { "the madness of men" }
9
+
10
+ it "aligns a line in the center" do
11
+ expect(text.align(22, direction: :center)).to eq(" the madness of men ")
12
+ end
13
+
14
+ it "aligns a line to the left" do
15
+ expect(text.align_left(22)).to eq("the madness of men ")
16
+ end
17
+
18
+ it "aligns a line to the right" do
19
+ expect(text.align_right(22)).to eq(" the madness of men")
20
+ end
21
+
22
+ it "checks for ansi" do
23
+ expect("\e[33mfoo\e[0m".ansi?).to eq(true)
24
+ end
25
+
26
+ it "removes ansi codes" do
27
+ expect("\e[33mfoo\e[0m".sanitize).to eq('foo')
28
+ end
29
+
30
+ it "folds a line" do
31
+ expect("foo\n\nbar\n".fold).to eq('foo bar ')
32
+ end
33
+
34
+ it "pads a line" do
35
+ expect(text.pad(1)).to eq([
36
+ " " * (text.size + 2),
37
+ " #{text} ",
38
+ " " * (text.size + 2),
39
+ ].join("\n"))
40
+ end
41
+
42
+ it "truncates a line" do
43
+ text = "the madness of men"
44
+ expect(text.truncate(10)).to eq('the madn…')
45
+ end
46
+
47
+ it "wraps a line" do
48
+ expect('foobar1'.wrap(3)).to eq("foo\nbar\n1")
49
+ end
50
+ end
51
+
@@ -0,0 +1,28 @@
1
+ # unicode: utf-8
2
+ # frozen_string_literals: true
3
+
4
+ RSpec.describe Strings, '#fold' do
5
+ {
6
+ " \n" => ' ',
7
+ "\n " => ' ',
8
+ "\n" => ' ',
9
+ "\n\n\n" => ' ',
10
+ " \n " => ' ',
11
+ " \n \n \n" => ' '
12
+ }.each do |actual, expected|
13
+ it "removes newline '#{actual.gsub(/\n/, '\\n')}' to '#{expected}'" do
14
+ expect(Strings::Fold.fold(actual)).to eq(expected)
15
+ end
16
+ end
17
+
18
+ {
19
+ " \r\n" => ' ',
20
+ "\r\n " => ' ',
21
+ "\r\n" => ' ',
22
+ " \r\n " => ' ',
23
+ }.each do |actual, expected|
24
+ it "squashes '#{actual.gsub(/\r\n/, '\\r\\n')}' to '#{expected}'" do
25
+ expect(Strings::Fold.fold(actual, "\r\n")).to eq(expected)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ # fronzen_string_literal: true
2
+
3
+ RSpec.describe Strings, '#fold' do
4
+ it "folds a multiline text into a single line" do
5
+ expect(Strings.fold("\tfoo \r\n\n\n bar")).to eq(" foo bar")
6
+ end
7
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings::Pad, '#pad' do
4
+ it "pads content with padding as a single value" do
5
+ text = "Ignorance is the parent of fear."
6
+ expect(Strings::Pad.pad(text, 1)).to eq([
7
+ " ",
8
+ " Ignorance is the parent of fear. ",
9
+ " ",
10
+ ].join("\n"))
11
+ end
12
+
13
+ it "pads content with specific padding as an array" do
14
+ text = "Ignorance is the parent of fear."
15
+ expect(Strings::Pad.pad(text, [1,1,1,1])).to eq([
16
+ " ",
17
+ " Ignorance is the parent of fear. ",
18
+ " ",
19
+ ].join("\n"))
20
+ end
21
+
22
+ it "pads with custom character" do
23
+ text = "Ignorance is the parent of fear."
24
+ expect(Strings::Pad.pad(text, [1, 2], fill: "*")).to eq([
25
+ "************************************",
26
+ "**Ignorance is the parent of fear.**",
27
+ "************************************",
28
+ ].join("\n"))
29
+ end
30
+
31
+ it "pads unicode content" do
32
+ text = "ラドクリフ、マラソン"
33
+ expect(Strings::Pad.pad(text, [1,1,1,1])).to eq([
34
+ " ",
35
+ " ラドクリフ、マラソン ",
36
+ " "
37
+ ].join("\n"))
38
+ end
39
+
40
+ it "pads multiline content" do
41
+ text = "It is the easiest thing\nin the world for a man\nto look as if he had \na great secret in him."
42
+ expect(Strings::Pad.pad(text, [1,2])).to eq([
43
+ " ",
44
+ " It is the easiest thing ",
45
+ " in the world for a man ",
46
+ " to look as if he had ",
47
+ " a great secret in him. ",
48
+ " ",
49
+ ].join("\n"))
50
+ end
51
+
52
+ it "pads ANSI codes inside content" do
53
+ text = "It is \e[35mthe easiest\e[0m thing\nin the \e[34mworld\e[0m for a man\nto look as if he had \na great \e[33msecret\e[0m in him."
54
+ expect(Strings::Pad.pad(text, [1,1,1,1])).to eq([
55
+ " ",
56
+ " It is \e[35mthe easiest\e[0m thing ",
57
+ " in the \e[34mworld\e[0m for a man ",
58
+ " to look as if he had ",
59
+ " a great \e[33msecret\e[0m in him. ",
60
+ " ",
61
+ ].join("\n"))
62
+ end
63
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings, '#pad' do
4
+ it "pads text" do
5
+ text = 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
6
+ expect(Strings.pad(text, [1,1,1,1])).to eql([
7
+ ' ',
8
+ ' ラドクリフ、マラソン五輪代表に1万m出場にも含み ',
9
+ ' '
10
+ ].join("\n"))
11
+ end
12
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal:true
2
+
3
+ RSpec.describe Strings::Padder, '#parse' do
4
+ it "parses nil" do
5
+ instance = Strings::Padder.parse(nil)
6
+ expect(instance.padding).to eq([])
7
+ end
8
+
9
+ it 'parses self' do
10
+ value = Strings::Padder.new([])
11
+ instance = Strings::Padder.parse(value)
12
+ expect(instance.padding).to eq([])
13
+ end
14
+
15
+ it "parses digit" do
16
+ instance = Strings::Padder.parse(5)
17
+ expect(instance.padding).to eq([5,5,5,5])
18
+ end
19
+
20
+ it "parses 2-element array" do
21
+ instance = Strings::Padder.parse([2,3])
22
+ expect(instance.padding).to eq([2,3,2,3])
23
+ end
24
+
25
+ it "parses 4-element array" do
26
+ instance = Strings::Padder.parse([1,2,3,4])
27
+ expect(instance.padding).to eq([1,2,3,4])
28
+ end
29
+
30
+ it "fails to parse unknown value" do
31
+ expect {
32
+ Strings::Padder.parse(:unknown)
33
+ }.to raise_error(Strings::Padder::ParseError)
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings, '#sanitize' do
4
+ it "removes ansi codes" do
5
+ expect(Strings.sanitize("\e[33mfoo\e[0m")).to eq('foo')
6
+ end
7
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings::Truncate, '#truncate' do
4
+ let(:text) { 'ラドクリフ、マラソン五輪代表に1万m出場にも含み' }
5
+
6
+ it "doesn't change text for 0 length" do
7
+ expect(Strings::Truncate.truncate(text, 0)).to eq(text)
8
+ end
9
+
10
+ it "doensn't change text for nil length" do
11
+ expect(Strings::Truncate.truncate(text, nil)).to eq(text)
12
+ end
13
+
14
+ it "doesn't change text for equal length" do
15
+ truncation = Strings::Truncate.truncate(text, text.length * 2)
16
+ expect(truncation).to eq(text)
17
+ end
18
+
19
+ it 'truncates text and displays omission' do
20
+ trailing = '…'
21
+ expect(Strings::Truncate.truncate(text, 12)).to eq("ラドクリフ#{trailing}")
22
+ end
23
+
24
+ it "estimates total width correctly " do
25
+ text = '太丸ゴシック体'
26
+ trailing = '…'
27
+ expect(Strings::Truncate.truncate(text, 8)).to eq("太丸ゴ#{trailing}")
28
+ end
29
+
30
+ it "doesn't truncate text when length exceeds content" do
31
+ expect(Strings::Truncate.truncate(text, 100)).to eq(text)
32
+ end
33
+
34
+ it "doesn't truncate whole words" do
35
+ text = "I know not all that may be coming, but be it what it will, I'll go to it laughing."
36
+ trailing = '…'
37
+ truncation = Strings::Truncate.truncate(text, separator: ' ')
38
+ expect(truncation).to eq("I know not all that may be#{trailing}")
39
+ end
40
+
41
+ it 'truncates text with string separator' do
42
+ trailing = '…'
43
+ truncation = Strings::Truncate.truncate(text, 12, separator: '')
44
+ expect(truncation).to eq("ラドクリフ#{trailing}")
45
+ end
46
+
47
+ it 'truncates text with regex separator' do
48
+ trailing = '…'
49
+ truncation = Strings::Truncate.truncate(text, 12, separator: /\s/)
50
+ expect(truncation).to eq("ラドクリフ#{trailing}")
51
+ end
52
+
53
+ it 'truncates text with custom trailing' do
54
+ trailing = '... (see more)'
55
+ truncation = Strings::Truncate.truncate(text, 20, trailing: trailing)
56
+ expect(truncation).to eq("ラド#{trailing}")
57
+ end
58
+
59
+ it 'correctly truncates with ANSI characters' do
60
+ text = "I try \e[34mall things\e[0m, I achieve what I can"
61
+ truncation = Strings::Truncate.truncate(text, 18)
62
+ expect(truncation).to eq("I try \e[34mall things\e[0m…")
63
+ end
64
+
65
+ it "finishes on word boundary" do
66
+ text = "for there is no folly of the beast of the earth"
67
+ truncation = Strings::Truncate.truncate(text, 20, separator: ' ')
68
+ expect(truncation).to eq('for there is no…')
69
+ end
70
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Strings, '#truncate' do
4
+ it "truncates text" do
5
+ text = 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
6
+ expect(Strings.truncate(text, 12)).to eq('ラドクリフ…')
7
+ end
8
+ end
@@ -0,0 +1,155 @@
1
+ # encoding: utf-8
2
+
3
+ RSpec.describe Strings::Wrap, '.wrap' do
4
+ context 'when unicode' do
5
+ let(:text) { 'ラドクリフ、マラソン五輪代表に1万m出場にも含み' }
6
+
7
+ it "doesn't wrap at zero length" do
8
+ expect(Strings::Wrap.wrap(text, 0)).to eq(text)
9
+ end
10
+
11
+ it "doesn't wrap at nil length" do
12
+ expect(Strings::Wrap.wrap(text, nil)).to eq(text)
13
+ end
14
+
15
+ it "doesn't wrap at length exceeding content length" do
16
+ expect(Strings::Wrap.wrap(text, 100)).to eq(text)
17
+ end
18
+
19
+ it "wraps correctly unbreakable words" do
20
+ expect(Strings::Wrap.wrap('foobar1', 3)).to eq([
21
+ "foo",
22
+ "bar",
23
+ "1"
24
+ ].join("\n"))
25
+ end
26
+
27
+ it "collapses multiple line breaks " do
28
+ text = "some \r\n\n\n\nunbreakable\n\n\n\n \r\r\rcontent \t"
29
+ expect(Strings::Wrap.wrap(text, 5)).to eq([
30
+ "some ",
31
+ "unbre",
32
+ "akabl",
33
+ "e",
34
+ " ",
35
+ "conte",
36
+ "nt "
37
+ ].join("\n"))
38
+ end
39
+
40
+ it "preserves newlines" do
41
+ text = "It is not down\n on any map;\n true places never are."
42
+ expect(Strings::Wrap.wrap(text, 10)).to eq([
43
+ "It is not ",
44
+ "down",
45
+ " on any ",
46
+ "map;",
47
+ " true ",
48
+ "places ",
49
+ "never are."
50
+ ].join("\n"))
51
+ end
52
+
53
+ it "wraps ascii text" do
54
+ text = "for there is no folly of the beast of the earth which is not infinitely outdone by the madness of men "
55
+ expect(Strings::Wrap.wrap(text, 16)).to eq([
56
+ "for there is no ",
57
+ "folly of the ",
58
+ "beast of the ",
59
+ "earth which is ",
60
+ "not infinitely ",
61
+ "outdone by the ",
62
+ "madness of men "
63
+ ].join("\n"))
64
+ end
65
+
66
+ it 'wraps at 8 characters' do
67
+ expect(Strings::Wrap.wrap(text, 8)).to eq([
68
+ "ラドクリ",
69
+ "フ、マラ",
70
+ "ソン五輪",
71
+ "代表に1",
72
+ "万m出場",
73
+ "にも含み"
74
+ ].join("\n"))
75
+ end
76
+
77
+ it 'preserves whitespace' do
78
+ text = " As for me, I am tormented with an everlasting itch for things remote. "
79
+ expect(Strings::Wrap.wrap(text, 10)).to eq([
80
+ " As for ",
81
+ "me, I ",
82
+ "am ",
83
+ "tormented ",
84
+ "with an ",
85
+ "e",
86
+ "verlasting",
87
+ " itch ",
88
+ "for ",
89
+ "things ",
90
+ "remote. "
91
+ ].join("\n"))
92
+ end
93
+ end
94
+
95
+ context 'when long text' do
96
+ it "wraps long text at 45 characters" do
97
+ text =
98
+ "What of it, if some old hunks of a sea-captain orders me to get a broom and sweep down the decks? What does that indignity amount to, weighed, I mean, in the scales of the New Testament? Do you think the archangel Gabriel thinks anything the less of me, because I promptly and respectfully obey that old hunks in that particular instance? Who ain't a slave? Tell me that. Well, then, however the old sea-captains may order me about--however they may thump and punch me about, I have the satisfaction of knowing that it is all right;"
99
+ expect(Strings::Wrap.wrap(text, 45)).to eq unindent <<-EOS
100
+ What of it, if some old hunks of a \nsea-captain orders me to get a broom and \n sweep down the decks? What does that \nindignity amount to, weighed, I mean, in the \n scales of the New Testament? Do you think \nthe archangel Gabriel thinks anything the \nless of me, because I promptly and \nrespectfully obey that old hunks in that \nparticular instance? Who ain't a slave? Tell \nme that. Well, then, however the old \nsea-captains may order me about--however \nthey may thump and punch me about, I have \nthe satisfaction of knowing that it is all \nright;
101
+ EOS
102
+ end
103
+ end
104
+
105
+ context 'with newlines' do
106
+ it "preserves newlines for both prefix and postfix" do
107
+ text = "\n\nラドクリフ、マラソン五輪代表に1万m出場にも含み\n\n\n"
108
+ expect(Strings::Wrap.wrap(text, 10)).to eq([
109
+ "\nラドクリフ",
110
+ "、マラソン",
111
+ "五輪代表に",
112
+ "1万m出場に",
113
+ "も含み\n"
114
+ ].join("\n"))
115
+ end
116
+ end
117
+
118
+ context 'with ANSI codes' do
119
+ it "wraps ANSI chars" do
120
+ text = "\e[32;44mIgnorance is the parent of fear.\e[0m"
121
+ expect(Strings::Wrap.wrap(text, 14)).to eq([
122
+ "\e[32;44mIgnorance is \e[0m",
123
+ "\e[32;44mthe parent of \e[0m",
124
+ "\e[32;44mfear.\e[0m",
125
+ ].join("\n"))
126
+ end
127
+
128
+ it "wraps ANSI in the middle of text" do
129
+ text = "Ignorance is the \e[32mparent\e[0m of fear."
130
+ expect(Strings::Wrap.wrap(text, 14)).to eq([
131
+ "Ignorance is ",
132
+ "the \e[32mparent\e[0m of ",
133
+ "fear.",
134
+ ].join("\n"))
135
+ end
136
+
137
+ it "wraps multline ANSI codes" do
138
+ text = "\e32;44mMulti\nLine\nContent.\e[0m"
139
+ expect(Strings::Wrap.wrap(text, 14)).to eq([
140
+ "\e32;44mMulti\e[0m",
141
+ "\e32;44mLine\e[0m",
142
+ "\e32;44mContent.\e[0m",
143
+ ].join("\n"))
144
+ end
145
+
146
+ it "wraps multiple ANSI codes in a single line" do
147
+ text = "Talk \e[32mnot\e[0m to me of \e[33mblasphemy\e[0m, man; I'd \e[35mstrike the sun\e[0m if it insulted me."
148
+ expect(Strings::Wrap.wrap(text, 30)).to eq([
149
+ "Talk \e[32mnot\e[0m to me of \e[33mblasphemy\e[0m, ",
150
+ "man; I'd \e[35mstrike the sun\e[0m if it ",
151
+ "insulted me."
152
+ ].join("\n"))
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ RSpec.describe Strings, '#wrap' do
4
+ it "wraps text" do
5
+ text = 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
6
+ expect(Strings.wrap(text, 8)).to eql([
7
+ "ラドクリ",
8
+ "フ、マラ",
9
+ "ソン五輪",
10
+ "代表に1",
11
+ "万m出場",
12
+ "にも含み"
13
+ ].join("\n"))
14
+ end
15
+ end
@@ -14,13 +14,13 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = ""
15
15
  spec.license = "MIT"
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
- f.match(%r{^(test|spec|features)/})
19
- end
17
+ spec.files = Dir['{lib,spec}/**/*.rb', '{bin,tasks}/*', 'strings.gemspec']
18
+ spec.files += Dir['README.md', 'CHANGELOG.md', 'LICENSE.txt', 'Rakefile']
20
19
  spec.bindir = "exe"
21
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
21
  spec.require_paths = ["lib"]
23
22
 
23
+ spec.add_dependency 'strings-ansi', '~> 0.1.0'
24
24
  spec.add_dependency 'unicode_utils', '~> 1.4.0'
25
25
  spec.add_dependency 'unicode-display_width','~> 1.4.0'
26
26
 
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strings
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-10 00:00:00.000000000 Z
11
+ date: 2018-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: strings-ansi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: unicode_utils
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -88,23 +102,14 @@ executables: []
88
102
  extensions: []
89
103
  extra_rdoc_files: []
90
104
  files:
91
- - ".gitignore"
92
- - ".rspec"
93
- - ".travis.yml"
94
105
  - CHANGELOG.md
95
- - CODE_OF_CONDUCT.md
96
- - Gemfile
97
106
  - LICENSE.txt
98
107
  - README.md
99
108
  - Rakefile
100
- - appveyor.yml
101
- - assets/strings_logo.png
102
- - benchmarks/speed_profile.rb
103
109
  - bin/console
104
110
  - bin/setup
105
111
  - lib/strings.rb
106
112
  - lib/strings/align.rb
107
- - lib/strings/ansi.rb
108
113
  - lib/strings/extensions.rb
109
114
  - lib/strings/fold.rb
110
115
  - lib/strings/pad.rb
@@ -112,6 +117,23 @@ files:
112
117
  - lib/strings/truncate.rb
113
118
  - lib/strings/version.rb
114
119
  - lib/strings/wrap.rb
120
+ - spec/spec_helper.rb
121
+ - spec/unit/align/align_left_spec.rb
122
+ - spec/unit/align/align_right_spec.rb
123
+ - spec/unit/align/align_spec.rb
124
+ - spec/unit/align_spec.rb
125
+ - spec/unit/ansi_spec.rb
126
+ - spec/unit/extensions_spec.rb
127
+ - spec/unit/fold/fold_spec.rb
128
+ - spec/unit/fold_spec.rb
129
+ - spec/unit/pad/pad_spec.rb
130
+ - spec/unit/pad_spec.rb
131
+ - spec/unit/padder/parse_spec.rb
132
+ - spec/unit/sanitize_spec.rb
133
+ - spec/unit/truncate/truncate_spec.rb
134
+ - spec/unit/truncate_spec.rb
135
+ - spec/unit/wrap/wrap_spec.rb
136
+ - spec/unit/wrap_spec.rb
115
137
  - strings.gemspec
116
138
  - tasks/console.rake
117
139
  - tasks/coverage.rake
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
-
11
- # rspec failure tracking
12
- .rspec_status
data/.rspec DELETED
@@ -1,4 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
4
- --warnings
@@ -1,23 +0,0 @@
1
- ---
2
- language: ruby
3
- sudo: false
4
- cache: bundler
5
- before_install: "gem update bundler"
6
- script: "bundle exec rake ci"
7
- rvm:
8
- - 2.0.0
9
- - 2.1.10
10
- - 2.2.9
11
- - 2.3.6
12
- - 2.4.4
13
- - 2.5.1
14
- - ruby-head
15
- - jruby-9.1.5.0
16
- - jruby-head
17
- matrix:
18
- allow_failures:
19
- - rvm: ruby-head
20
- - rvm: jruby-head
21
- fast_finish: true
22
- branches:
23
- only: master
@@ -1,74 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- In the interest of fostering an open and welcoming environment, we as
6
- contributors and maintainers pledge to making participation in our project and
7
- our community a harassment-free experience for everyone, regardless of age, body
8
- size, disability, ethnicity, gender identity and expression, level of experience,
9
- nationality, personal appearance, race, religion, or sexual identity and
10
- orientation.
11
-
12
- ## Our Standards
13
-
14
- Examples of behavior that contributes to creating a positive environment
15
- include:
16
-
17
- * Using welcoming and inclusive language
18
- * Being respectful of differing viewpoints and experiences
19
- * Gracefully accepting constructive criticism
20
- * Focusing on what is best for the community
21
- * Showing empathy towards other community members
22
-
23
- Examples of unacceptable behavior by participants include:
24
-
25
- * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
27
- * Trolling, insulting/derogatory comments, and personal or political attacks
28
- * Public or private harassment
29
- * Publishing others' private information, such as a physical or electronic
30
- address, without explicit permission
31
- * Other conduct which could reasonably be considered inappropriate in a
32
- professional setting
33
-
34
- ## Our Responsibilities
35
-
36
- Project maintainers are responsible for clarifying the standards of acceptable
37
- behavior and are expected to take appropriate and fair corrective action in
38
- response to any instances of unacceptable behavior.
39
-
40
- Project maintainers have the right and responsibility to remove, edit, or
41
- reject comments, commits, code, wiki edits, issues, and other contributions
42
- that are not aligned to this Code of Conduct, or to ban temporarily or
43
- permanently any contributor for other behaviors that they deem inappropriate,
44
- threatening, offensive, or harmful.
45
-
46
- ## Scope
47
-
48
- This Code of Conduct applies both within project spaces and in public spaces
49
- when an individual is representing the project or its community. Examples of
50
- representing a project or community include using an official project e-mail
51
- address, posting via an official social media account, or acting as an appointed
52
- representative at an online or offline event. Representation of a project may be
53
- further defined and clarified by project maintainers.
54
-
55
- ## Enforcement
56
-
57
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at [email]. All
59
- complaints will be reviewed and investigated and will result in a response that
60
- is deemed necessary and appropriate to the circumstances. The project team is
61
- obligated to maintain confidentiality with regard to the reporter of an incident.
62
- Further details of specific enforcement policies may be posted separately.
63
-
64
- Project maintainers who do not follow or enforce the Code of Conduct in good
65
- faith may face temporary or permanent repercussions as determined by other
66
- members of the project's leadership.
67
-
68
- ## Attribution
69
-
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [http://contributor-covenant.org/version/1/4][version]
72
-
73
- [homepage]: http://contributor-covenant.org
74
- [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile DELETED
@@ -1,15 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
-
5
- gemspec
6
-
7
- group :metrics do
8
- gem 'coveralls', '~> 0.8.22'
9
- gem 'simplecov', '~> 0.16.1'
10
- gem 'yardstick', '~> 0.9.9'
11
- end
12
-
13
- group :benchmarks do
14
- gem 'benchmark-ips', '~> 2.7.2'
15
- end
@@ -1,23 +0,0 @@
1
- ---
2
- install:
3
- - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
4
- - ruby --version
5
- - gem --version
6
- - bundle install
7
- build: off
8
- test_script:
9
- - bundle exec rake ci
10
- environment:
11
- matrix:
12
- - ruby_version: "200"
13
- - ruby_version: "200-x64"
14
- - ruby_version: "21"
15
- - ruby_version: "21-x64"
16
- - ruby_version: "22"
17
- - ruby_version: "22-x64"
18
- - ruby_version: "23"
19
- - ruby_version: "23-x64"
20
- - ruby_version: "24"
21
- - ruby_version: "24-x64"
22
- - ruby_version: "25"
23
- - ruby_version: "25-x64"
Binary file
@@ -1,30 +0,0 @@
1
- require 'benchmark/ips'
2
- require 'strings'
3
-
4
- text = "Ignorance is the parent of fear."
5
-
6
- Benchmark.ips do |x|
7
- x.report('wrap') do
8
- Strings::Wrap.wrap(text, 10)
9
- end
10
-
11
- x.report('truncate') do
12
- Strings::Truncate.truncate(text, 10)
13
- end
14
-
15
- x.compare!
16
- end
17
-
18
- # (2017-12-09)
19
- #
20
- # Calculating -------------------------------------
21
- # wrap 186 i/100ms
22
- # truncate 295 i/100ms
23
- # -------------------------------------------------
24
- # wrap 1917.2 (±2.2%) i/s - 9672 in 5.047297s
25
- # truncate 3020.6 (±3.0%) i/s - 15340 in 5.083516s
26
- #
27
- # Comparison:
28
- # truncate: 3020.6 i/s
29
- # wrap: 1917.2 i/s - 1.58x slower
30
- #
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Strings
4
- # Helper functions for handling ANSI escape sequences
5
- module ANSI
6
- # The control sequence indicator
7
- CSI = "\033".freeze
8
-
9
- # The code for reseting styling
10
- RESET = "\e[0m".freeze
11
-
12
- # The regex to match ANSI codes
13
- ANSI_MATCHER = '(\[)?\033(\[)?[;?\d]*[\dA-Za-z]([\];])?'.freeze
14
-
15
- # Remove ANSI characters from the text
16
- #
17
- # @param [String] text
18
- #
19
- # @example
20
- # Strings::ANSI.sanitize("\e[33mfoo\[e0m")
21
- # # => "foo"
22
- #
23
- # @return [String]
24
- #
25
- # @api public
26
- def sanitize(text)
27
- text.gsub(/#{ANSI_MATCHER}/, '')
28
- end
29
- module_function :sanitize
30
-
31
- # Check if string contains ANSI codes
32
- #
33
- # @param [String] string
34
- # the string to check
35
- #
36
- # @example
37
- # Strings::ANSI.ansi?("\e[33mfoo\[e0m")
38
- # # => true
39
- #
40
- # @return [Boolean]
41
- #
42
- # @api public
43
- def ansi?(string)
44
- !!(string =~ /#{ANSI_MATCHER}/)
45
- end
46
- module_function :ansi?
47
-
48
- # Check if string contains only ANSI codes
49
- #
50
- # @param [String] string
51
- # the string to check
52
- #
53
- # @example
54
- # Strings::ANSI.only_ansi?("\e[33mfoo\[e0m")
55
- # # => false
56
- #
57
- # Strings::ANSI.only_ansi?("\e[33m")
58
- # # => false
59
- #
60
- # @return [Boolean]
61
- #
62
- # @api public
63
- def only_ansi?(string)
64
- !!(string =~ /^#{ANSI_MATCHER}$/)
65
- end
66
- module_function :only_ansi?
67
- end # Sanitizer
68
- end # Strings