strings 0.1.2 → 0.1.3

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: 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