verse 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +71 -12
- data/lib/verse.rb +6 -0
- data/lib/verse/alignment.rb +37 -4
- data/lib/verse/truncation.rb +34 -8
- data/lib/verse/version.rb +1 -1
- data/lib/verse/wrapping.rb +70 -69
- data/spec/unit/alignment/align_spec.rb +17 -0
- data/spec/unit/alignment/left_spec.rb +17 -0
- data/spec/unit/alignment/right_spec.rb +17 -0
- data/spec/unit/truncate_spec.rb +1 -1
- data/spec/unit/truncation/truncate_spec.rb +12 -6
- data/spec/unit/wrap_spec.rb +8 -1
- data/spec/unit/wrapping/wrap_spec.rb +41 -30
- data/verse.gemspec +2 -0
- metadata +17 -4
- data/spec/unit/wrapping/new_spec.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c721dd40817c43013c05f142361a15244fe44c58
|
4
|
+
data.tar.gz: c22b4b32258ea42631c727256702ff01642dd79b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ce1bd97ac18f78a18540c7b41f65760c71ca383f36a082e728c748957a9ca4e746774cbb8adf6d433ab523b88d226ad2a2ba0bbbfc5808b60508dd0a8142d75
|
7
|
+
data.tar.gz: 39d08d89c24374654118ed3e05bff810128d94feefec1f55a49b6390db37dcae4e810e554c31749fce58890a39097227be253332ce4d20b046f8d287d1e3c20a
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -16,6 +16,8 @@
|
|
16
16
|
## Features
|
17
17
|
|
18
18
|
* No monkey-patching String class
|
19
|
+
* Supports multibyte character encodings such as UTF-8, EUC-JP
|
20
|
+
* Handles languages without whitespaces between words (like Chinese and Japanese)
|
19
21
|
|
20
22
|
## Installation
|
21
23
|
|
@@ -37,11 +39,18 @@ Or install it yourself as:
|
|
37
39
|
$ gem install verse
|
38
40
|
```
|
39
41
|
|
42
|
+
## Contents
|
43
|
+
|
44
|
+
* [1. Usage](#1-usage)
|
45
|
+
* [1.1 Align](#11-align)
|
46
|
+
* [1.2 Truncate](#12-truncate)
|
47
|
+
* [1.3 Wrap](#13-wrap)
|
48
|
+
|
40
49
|
## 1 Usage
|
41
50
|
|
42
|
-
### 1.1
|
51
|
+
### 1.1 Align
|
43
52
|
|
44
|
-
**Verse** allows you to align text:
|
53
|
+
**Verse::Alignment** allows you to align text within a given length:
|
45
54
|
|
46
55
|
```ruby
|
47
56
|
alignment = Verse::Alignment.new "for there is no folly of the beast\n" +
|
@@ -50,19 +59,34 @@ alignment = Verse::Alignment.new "for there is no folly of the beast\n" +
|
|
50
59
|
" outdone by the madness of men"
|
51
60
|
```
|
52
61
|
|
53
|
-
Then using
|
62
|
+
Then using direction out of `:right`, `:left` or `:center` methods and passing width you can align the text:
|
54
63
|
|
55
64
|
```ruby
|
56
|
-
alignemnt.
|
65
|
+
alignemnt.align(40, :right) # =>
|
57
66
|
" for there is no folly of the beast\n" +
|
58
67
|
" of the earth which\n" +
|
59
68
|
" is not infinitely\n" +
|
60
69
|
" outdone by the madness of men"
|
61
70
|
```
|
62
71
|
|
63
|
-
|
72
|
+
Aligning `UTF-8` text is also supported:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
alignment = Verse::Alignment.new "ラドクリフ\n" +
|
76
|
+
"、マラソン五輪\n" +
|
77
|
+
"代表に1万m出\n" +
|
78
|
+
"場にも含み"
|
79
|
+
|
80
|
+
alignment.center(20) # =>
|
81
|
+
" ラドクリフ \n" +
|
82
|
+
" 、マラソン五輪 \n" +
|
83
|
+
" 代表に1万m出 \n" +
|
84
|
+
" 場にも含み "
|
85
|
+
```
|
86
|
+
|
87
|
+
### 1.2 Truncate
|
64
88
|
|
65
|
-
Using **Verse** you can truncate a given text
|
89
|
+
Using **Verse::Truncation** you can truncate a given text after a given length.
|
66
90
|
|
67
91
|
```ruby
|
68
92
|
truncation = Verse::Truncation.new "for there is no folly of the beast of the earth " +
|
@@ -76,7 +100,23 @@ Then to shorten the text to given length call `truncate`:
|
|
76
100
|
truncation.truncate(20) # => "for there is no fol…"
|
77
101
|
```
|
78
102
|
|
79
|
-
|
103
|
+
Pass in `:trailing` (by default `…`) to replace last characters:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
truncation.truncate(22, trailing: '... (see more)')
|
107
|
+
# => "for there...(see more)"
|
108
|
+
```
|
109
|
+
|
110
|
+
You can also specify `UTF-8` text as well:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
truncation = Verse::Truncation.new 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
|
114
|
+
truncation.truncate(12) # => "ラドクリフ…"
|
115
|
+
```
|
116
|
+
|
117
|
+
### 1.3 Wrap
|
118
|
+
|
119
|
+
**Verse::Wrapping** allows you to wrap text into lines no longer than `wrap_at` argument length. The `wrap` method will break either on whitespace character or in case of east Asian characters on character boundaries.
|
80
120
|
|
81
121
|
```ruby
|
82
122
|
wrapping = Verse::Wrapping.new "Think not, is my eleventh commandment; " +
|
@@ -84,13 +124,32 @@ wrapping = Verse::Wrapping.new "Think not, is my eleventh commandment; " +
|
|
84
124
|
|
85
125
|
```
|
86
126
|
|
87
|
-
Then to wrap the text to given length
|
127
|
+
Then to wrap the text to given length do:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
wrapping.wrap(30) # =>
|
131
|
+
"Think not, is my eleventh"
|
132
|
+
"commandment; and sleep when"
|
133
|
+
"you can, is my twelfth."
|
134
|
+
```
|
135
|
+
|
136
|
+
Similarly, to handle `UTF-8` text do:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
wrapping = Verse::Wrapping.new "ラドクリフ、マラソン五輪代表に1万m出場にも含み"
|
140
|
+
wrapping.wrap(8) # =>
|
141
|
+
"ラドクリ"
|
142
|
+
"フ、マラ"
|
143
|
+
"ソン五輪"
|
144
|
+
"代表に1"
|
145
|
+
"万m出場"
|
146
|
+
"にも含み"
|
147
|
+
```
|
148
|
+
|
149
|
+
You can also call `wrap` directly on **Verse**:
|
88
150
|
|
89
151
|
```ruby
|
90
|
-
|
91
|
-
# => "Think not, is my eleventh"
|
92
|
-
"commandment; and sleep when"
|
93
|
-
"you can, is my twelfth."
|
152
|
+
Verse.wrap(text, wrap_at)
|
94
153
|
```
|
95
154
|
|
96
155
|
## Contributing
|
data/lib/verse.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
require 'unicode_utils/display_width'
|
4
|
+
require 'unicode_utils/each_grapheme'
|
5
|
+
|
3
6
|
require 'verse/alignment'
|
4
7
|
require 'verse/sanitizer'
|
5
8
|
require 'verse/truncation'
|
@@ -11,6 +14,9 @@ module Verse
|
|
11
14
|
NEWLINE = "\n".freeze
|
12
15
|
TAB = "\n".freeze
|
13
16
|
|
17
|
+
SPACE_RE = %r{\s+}mo.freeze
|
18
|
+
NEWLINE_RE = %r{\n}o.freeze
|
19
|
+
|
14
20
|
# Align a text to a given direction with the width
|
15
21
|
#
|
16
22
|
# @see Verse::Alignment#align
|
data/lib/verse/alignment.rb
CHANGED
@@ -76,7 +76,7 @@ module Verse
|
|
76
76
|
|
77
77
|
filler = options.fetch(:fill) { fill }
|
78
78
|
method = convert_to_method(direction)
|
79
|
-
process_lines { |line|
|
79
|
+
process_lines { |line| send(method, line, width, filler) }
|
80
80
|
end
|
81
81
|
|
82
82
|
protected
|
@@ -84,9 +84,9 @@ module Verse
|
|
84
84
|
# @api private
|
85
85
|
def convert_to_method(direction)
|
86
86
|
case direction.to_sym
|
87
|
-
when :left then :
|
88
|
-
when :right then :
|
89
|
-
when :center then :
|
87
|
+
when :left then :left_justify
|
88
|
+
when :right then :right_justify
|
89
|
+
when :center then :center_justify
|
90
90
|
else
|
91
91
|
fail ArgumentError, "Unknown alignment `#{direction}`."
|
92
92
|
end
|
@@ -100,6 +100,39 @@ module Verse
|
|
100
100
|
end.join("\n")
|
101
101
|
end
|
102
102
|
|
103
|
+
# @api private
|
104
|
+
def left_justify(text, width, filler)
|
105
|
+
width_diff = width - UnicodeUtils.display_width(text)
|
106
|
+
if width_diff > 0
|
107
|
+
text + filler * width_diff
|
108
|
+
else
|
109
|
+
text
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# @api private
|
114
|
+
def right_justify(text, width, filler)
|
115
|
+
width_diff = width - UnicodeUtils.display_width(text)
|
116
|
+
if width_diff > 0
|
117
|
+
filler * width_diff + text
|
118
|
+
else
|
119
|
+
text
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# @api private
|
124
|
+
def center_justify(text, width, filler)
|
125
|
+
text_width = UnicodeUtils.display_width(text)
|
126
|
+
width_diff = width - text_width
|
127
|
+
if width_diff > 0
|
128
|
+
right_count = (width_diff.to_f / 2).ceil
|
129
|
+
left_count = width_diff - right_count
|
130
|
+
[filler * left_count, text, filler * right_count].join
|
131
|
+
else
|
132
|
+
text
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
103
136
|
attr_reader :text
|
104
137
|
end # Alignment
|
105
138
|
end # Verse
|
data/lib/verse/truncation.rb
CHANGED
@@ -57,22 +57,48 @@ module Verse
|
|
57
57
|
#
|
58
58
|
# @api public
|
59
59
|
def truncate(truncate_at = DEFAULT_LENGTH, options = {})
|
60
|
-
if text
|
60
|
+
if display_width(text) <= truncate_at.to_i || truncate_at.to_i.zero?
|
61
61
|
return text.dup
|
62
62
|
end
|
63
|
-
trail
|
64
|
-
separation
|
63
|
+
trail = options.fetch(:trailing) { trailing }
|
64
|
+
separation = options.fetch(:separator) { separator }
|
65
|
+
sanitized_text = @sanitizer.sanitize(text)
|
66
|
+
width = display_width(sanitized_text)
|
65
67
|
|
66
|
-
|
67
|
-
return chars.join if chars.length <= truncate_at
|
68
|
-
length_without_trailing = truncate_at - trail.chars.to_a.size
|
69
|
-
stop = chars[0, length_without_trailing].rindex(separation)
|
68
|
+
return text if width <= truncate_at
|
70
69
|
|
71
|
-
|
70
|
+
length_without_trailing = truncate_at - display_width(trail)
|
71
|
+
chars = UnicodeUtils.each_grapheme(sanitized_text).to_a
|
72
|
+
stop = chars[0, length_without_trailing].rindex(separation)
|
73
|
+
sliced_chars = chars[0, stop || length_without_trailing]
|
74
|
+
shorten(sliced_chars, length_without_trailing).join + trail
|
72
75
|
end
|
73
76
|
|
74
77
|
protected
|
75
78
|
|
76
79
|
attr_reader :text
|
80
|
+
|
81
|
+
# Perform actual shortening of the text
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
def shorten(chars, length_without_trailing)
|
87
|
+
truncated = []
|
88
|
+
char_width = display_width(chars[0])
|
89
|
+
while length_without_trailing - char_width > 0
|
90
|
+
char = chars.shift
|
91
|
+
break unless char
|
92
|
+
truncated << char
|
93
|
+
char_width = display_width(char)
|
94
|
+
length_without_trailing -= char_width
|
95
|
+
end
|
96
|
+
truncated
|
97
|
+
end
|
98
|
+
|
99
|
+
# @api private
|
100
|
+
def display_width(string)
|
101
|
+
UnicodeUtils.display_width(string)
|
102
|
+
end
|
77
103
|
end # Truncation
|
78
104
|
end # Verse
|
data/lib/verse/version.rb
CHANGED
data/lib/verse/wrapping.rb
CHANGED
@@ -5,24 +5,17 @@ module Verse
|
|
5
5
|
class Wrapping
|
6
6
|
DEFAULT_WIDTH = 80.freeze
|
7
7
|
|
8
|
-
attr_reader :indent
|
9
|
-
|
10
|
-
attr_reader :padding
|
11
|
-
|
12
8
|
# Initialize a Wrapping
|
13
9
|
#
|
14
10
|
# @param [String] text
|
15
11
|
# the text to be wrapped
|
16
12
|
#
|
17
13
|
# @param [Hash] options
|
18
|
-
# @option options [Symbol] :indent the indentation
|
19
14
|
# @option options [Symbol] :padding the desired spacing
|
20
15
|
#
|
21
16
|
# @api public
|
22
17
|
def initialize(text, options = {})
|
23
18
|
@text = text
|
24
|
-
@indent = options.fetch(:indent) { 0 }
|
25
|
-
@padding = options.fetch(:padding) { [] }
|
26
19
|
@line_width = options.fetch(:line_width) { DEFAULT_WIDTH }
|
27
20
|
@sanitizer = Sanitizer.new
|
28
21
|
end
|
@@ -31,7 +24,7 @@ module Verse
|
|
31
24
|
#
|
32
25
|
# @api public
|
33
26
|
def self.wrap(text, wrap_at, options = {})
|
34
|
-
new(text, options).wrap(wrap_at
|
27
|
+
new(text, options).wrap(wrap_at)
|
35
28
|
end
|
36
29
|
|
37
30
|
# Wrap a text into lines no longer than wrap_at length.
|
@@ -41,85 +34,93 @@ module Verse
|
|
41
34
|
# wrapping = Verse::Wrapping.new "Some longish text"
|
42
35
|
#
|
43
36
|
# wrapping.wrap(8)
|
44
|
-
# # =>
|
45
|
-
#
|
46
|
-
#
|
47
|
-
# # => > Some
|
48
|
-
# > longish
|
49
|
-
# > text
|
37
|
+
# # => >Some
|
38
|
+
# >longish
|
39
|
+
# >text
|
50
40
|
#
|
51
41
|
# @api public
|
52
|
-
def wrap(wrap_at = DEFAULT_WIDTH
|
42
|
+
def wrap(wrap_at = DEFAULT_WIDTH)
|
53
43
|
if text.length < wrap_at.to_i || wrap_at.to_i.zero?
|
54
|
-
return text
|
44
|
+
return text
|
55
45
|
end
|
56
|
-
|
57
|
-
|
58
|
-
spacing = options.fetch(:padding) { padding }
|
59
|
-
|
60
|
-
text.split(NEWLINE, -1).map do |line|
|
61
|
-
pad_line(indent_line(wrap_line(line, wrap_at), indentation), spacing)
|
46
|
+
text.split(NEWLINE, -1).map do |paragraph|
|
47
|
+
format_paragraph(paragraph, wrap_at)
|
62
48
|
end * NEWLINE
|
63
49
|
end
|
64
50
|
|
65
|
-
|
66
|
-
|
67
|
-
# Calculate string length without color escapes
|
51
|
+
# Format paragraph to be maximum of wrap_at length
|
68
52
|
#
|
69
|
-
# @param [String]
|
53
|
+
# @param [String] paragraph
|
54
|
+
# the paragraph to format
|
55
|
+
# @param [Integer] wrap_at
|
56
|
+
# the maximum length to wrap the paragraph
|
70
57
|
#
|
71
|
-
# @
|
72
|
-
|
73
|
-
at + (string.length - @sanitizer.sanitize(string).length)
|
74
|
-
end
|
75
|
-
|
76
|
-
# Wrap line at given length
|
77
|
-
#
|
78
|
-
# @param [String] line
|
79
|
-
#
|
80
|
-
# @return [String]
|
58
|
+
# @return [Array[String]]
|
59
|
+
# the wrapped lines
|
81
60
|
#
|
82
61
|
# @api private
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
62
|
+
def format_paragraph(paragraph, wrap_at)
|
63
|
+
cleared_para = paragraph.strip.gsub(NEWLINE_RE, SPACE).squeeze(SPACE)
|
64
|
+
lines = []
|
65
|
+
line = ''
|
66
|
+
word = ''
|
67
|
+
word_length = 0
|
68
|
+
line_length = 0
|
69
|
+
char_length = 0 # visible char length
|
70
|
+
text_length = display_width(cleared_para)
|
71
|
+
total_length = 0
|
72
|
+
UnicodeUtils.each_grapheme(cleared_para) do |char|
|
73
|
+
char_length = display_width(char)
|
74
|
+
total_length += char_length
|
75
|
+
if line_length + word_length + char_length <= wrap_at
|
76
|
+
if SPACE_RE =~ char || total_length == text_length
|
77
|
+
line << word + char
|
78
|
+
line_length += word_length + char_length
|
79
|
+
word = ''
|
80
|
+
word_length = 0
|
81
|
+
else
|
82
|
+
word << char
|
83
|
+
word_length += char_length
|
84
|
+
end
|
85
|
+
next
|
86
|
+
end
|
87
|
+
|
88
|
+
if SPACE_RE =~ char # ends with space
|
89
|
+
lines << line.strip
|
90
|
+
line = ''
|
91
|
+
line_length = 0
|
92
|
+
word = ''
|
93
|
+
word_length = 0
|
94
|
+
elsif word_length + char_length <= wrap_at
|
95
|
+
lines << line.strip
|
96
|
+
line = word + char
|
97
|
+
line_length = word_length + char_length
|
98
|
+
word = ''
|
99
|
+
word_length = 0
|
100
|
+
else # hyphenate word - too long to fit a line
|
101
|
+
lines << word.strip
|
102
|
+
line_length = 0
|
103
|
+
word = char
|
104
|
+
word_length = char_length
|
105
|
+
end
|
106
|
+
end
|
107
|
+
lines << line.strip unless line.empty?
|
108
|
+
lines << word unless word.empty?
|
109
|
+
lines
|
88
110
|
end
|
89
111
|
|
90
|
-
|
91
|
-
|
92
|
-
#
|
93
|
-
#
|
94
|
-
# @return [String]
|
112
|
+
protected
|
113
|
+
|
114
|
+
# The text to wrap
|
95
115
|
#
|
96
116
|
# @api private
|
97
|
-
|
98
|
-
text.split(NEWLINE).each do |line|
|
99
|
-
line.insert(0, SPACE * indent)
|
100
|
-
end
|
101
|
-
end
|
117
|
+
attr_reader :text
|
102
118
|
|
103
|
-
#
|
104
|
-
#
|
105
|
-
# @param [String] text
|
106
|
-
# the wrapped text
|
107
|
-
#
|
108
|
-
# @return [String]
|
119
|
+
# Visible width of string
|
109
120
|
#
|
110
121
|
# @api private
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
padding_left = SPACE * padding[3].to_i
|
115
|
-
padding_right = SPACE * padding[1].to_i
|
116
|
-
text.map! do |part|
|
117
|
-
part.insert(0, padding_left).insert(-1, padding_right)
|
118
|
-
end
|
122
|
+
def display_width(string)
|
123
|
+
UnicodeUtils.display_width(string)
|
119
124
|
end
|
120
|
-
|
121
|
-
protected
|
122
|
-
|
123
|
-
attr_reader :text
|
124
125
|
end # Wrapping
|
125
126
|
end # Verse
|
@@ -18,6 +18,12 @@ RSpec.describe Verse::Alignment, '.align' do
|
|
18
18
|
expect(alignment.center(22)).to eq(" the madness of men ")
|
19
19
|
end
|
20
20
|
|
21
|
+
it "centers utf line" do
|
22
|
+
text = "こんにちは"
|
23
|
+
alignment = Verse::Alignment.new(text)
|
24
|
+
expect(alignment.center(20)).to eq(" こんにちは ")
|
25
|
+
end
|
26
|
+
|
21
27
|
it "centers multiline text" do
|
22
28
|
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"
|
23
29
|
alignment = Verse::Alignment.new(text)
|
@@ -29,6 +35,17 @@ RSpec.describe Verse::Alignment, '.align' do
|
|
29
35
|
].join)
|
30
36
|
end
|
31
37
|
|
38
|
+
it "centers multiline utf text" do
|
39
|
+
text = "ラドクリフ\n、マラソン五輪\n代表に1万m出\n場にも含み"
|
40
|
+
alignment = Verse::Alignment.new(text)
|
41
|
+
expect(alignment.center(20)).to eq([
|
42
|
+
" ラドクリフ \n",
|
43
|
+
" 、マラソン五輪 \n",
|
44
|
+
" 代表に1万m出 \n",
|
45
|
+
" 場にも含み "
|
46
|
+
].join)
|
47
|
+
end
|
48
|
+
|
32
49
|
it "centers multiline text with fill of '*'" do
|
33
50
|
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"
|
34
51
|
alignment = Verse::Alignment.new(text, fill: '*')
|
@@ -9,6 +9,12 @@ RSpec.describe Verse::Alignment, '.left' do
|
|
9
9
|
expect(alignment.left(22)).to eq("the madness of men ")
|
10
10
|
end
|
11
11
|
|
12
|
+
it "left justifies utf line" do
|
13
|
+
text = "こんにちは"
|
14
|
+
alignment = Verse::Alignment.new(text)
|
15
|
+
expect(alignment.align(20, :left)).to eq("こんにちは ")
|
16
|
+
end
|
17
|
+
|
12
18
|
it "aligns multiline text to left" do
|
13
19
|
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"
|
14
20
|
alignment = Verse::Alignment.new(text)
|
@@ -20,6 +26,17 @@ RSpec.describe Verse::Alignment, '.left' do
|
|
20
26
|
].join)
|
21
27
|
end
|
22
28
|
|
29
|
+
it "left justifies multiline utf text" do
|
30
|
+
text = "ラドクリフ\n、マラソン五輪\n代表に1万m出\n場にも含み"
|
31
|
+
alignment = Verse::Alignment.new(text)
|
32
|
+
expect(alignment.left(20)).to eq([
|
33
|
+
"ラドクリフ \n",
|
34
|
+
"、マラソン五輪 \n",
|
35
|
+
"代表に1万m出 \n",
|
36
|
+
"場にも含み "
|
37
|
+
].join)
|
38
|
+
end
|
39
|
+
|
23
40
|
it "centers multiline text with fill of '*'" do
|
24
41
|
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
42
|
alignment = Verse::Alignment.new(text, fill: '*')
|
@@ -9,6 +9,12 @@ RSpec.describe Verse::Alignment, '.right' do
|
|
9
9
|
expect(alignment.right(22)).to eq(" the madness of men")
|
10
10
|
end
|
11
11
|
|
12
|
+
it "right justifies utf line" do
|
13
|
+
text = "こんにちは"
|
14
|
+
alignment = Verse::Alignment.new(text)
|
15
|
+
expect(alignment.align(20, :right)).to eq(" こんにちは")
|
16
|
+
end
|
17
|
+
|
12
18
|
it "aligns multiline text to left" do
|
13
19
|
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"
|
14
20
|
alignment = Verse::Alignment.new(text)
|
@@ -20,6 +26,17 @@ RSpec.describe Verse::Alignment, '.right' do
|
|
20
26
|
].join)
|
21
27
|
end
|
22
28
|
|
29
|
+
it "right justifies multiline utf text" do
|
30
|
+
text = "ラドクリフ\n、マラソン五輪\n代表に1万m出\n場にも含み"
|
31
|
+
alignment = Verse::Alignment.new(text)
|
32
|
+
expect(alignment.right(20)).to eq([
|
33
|
+
" ラドクリフ\n",
|
34
|
+
" 、マラソン五輪\n",
|
35
|
+
" 代表に1万m出\n",
|
36
|
+
" 場にも含み"
|
37
|
+
].join)
|
38
|
+
end
|
39
|
+
|
23
40
|
it "centers multiline text with fill of '*'" do
|
24
41
|
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
42
|
alignment = Verse::Alignment.new(text, fill: '*')
|
data/spec/unit/truncate_spec.rb
CHANGED
@@ -17,13 +17,19 @@ RSpec.describe Verse::Truncation, '.truncate' do
|
|
17
17
|
|
18
18
|
it "doesn't change text for equal length" do
|
19
19
|
truncation = Verse::Truncation.new(text)
|
20
|
-
expect(truncation.truncate(text.length)).to eq(text)
|
20
|
+
expect(truncation.truncate(text.length * 2)).to eq(text)
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'truncates text' do
|
24
24
|
truncation = Verse::Truncation.new(text)
|
25
25
|
trailing = '…'
|
26
|
-
expect(truncation.truncate(12)).to eq("
|
26
|
+
expect(truncation.truncate(12)).to eq("ラドクリフ#{trailing}")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "estimates total width correctly " do
|
30
|
+
truncation = Verse::Truncation.new('太丸ゴシック体')
|
31
|
+
trailing = '…'
|
32
|
+
expect(truncation.truncate(8)).to eq("太丸ゴ#{trailing}")
|
27
33
|
end
|
28
34
|
|
29
35
|
it "doesn't truncate text when length exceeds content" do
|
@@ -34,25 +40,25 @@ RSpec.describe Verse::Truncation, '.truncate' do
|
|
34
40
|
it 'truncates text with string separator' do
|
35
41
|
truncation = Verse::Truncation.new(text)
|
36
42
|
trailing = '…'
|
37
|
-
expect(truncation.truncate(12, separator: '
|
43
|
+
expect(truncation.truncate(12, separator: '')).to eq("ラドクリフ#{trailing}")
|
38
44
|
end
|
39
45
|
|
40
46
|
it 'truncates text with regex separator' do
|
41
47
|
truncation = Verse::Truncation.new(text)
|
42
48
|
trailing = '…'
|
43
|
-
expect(truncation.truncate(12, separator: /\s/)).to eq("
|
49
|
+
expect(truncation.truncate(12, separator: /\s/)).to eq("ラドクリフ#{trailing}")
|
44
50
|
end
|
45
51
|
|
46
52
|
it 'truncates text with custom trailing' do
|
47
53
|
truncation = Verse::Truncation.new(text)
|
48
54
|
trailing = '... (see more)'
|
49
|
-
expect(truncation.truncate(20, trailing: trailing)).to eq("
|
55
|
+
expect(truncation.truncate(20, trailing: trailing)).to eq("ラド#{trailing}")
|
50
56
|
end
|
51
57
|
|
52
58
|
it 'correctly truncates with ANSI characters' do
|
53
59
|
text = "This is a \e[1m\e[34mbold blue text\e[0m"
|
54
60
|
truncation = Verse::Truncation.new(text)
|
55
|
-
expect(truncation.truncate).to eq
|
61
|
+
expect(truncation.truncate).to eq(text)
|
56
62
|
end
|
57
63
|
|
58
64
|
it "finishes on word boundary" do
|
data/spec/unit/wrap_spec.rb
CHANGED
@@ -5,6 +5,13 @@ require 'spec_helper'
|
|
5
5
|
RSpec.describe Verse, '#wrap' do
|
6
6
|
it "wraps text" do
|
7
7
|
text = 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
|
8
|
-
expect(Verse.wrap(text, 8)).to eql(
|
8
|
+
expect(Verse.wrap(text, 8)).to eql([
|
9
|
+
"ラドクリ",
|
10
|
+
"フ、マラ",
|
11
|
+
"ソン五輪",
|
12
|
+
"代表に1",
|
13
|
+
"万m出場",
|
14
|
+
"にも含み"
|
15
|
+
].join("\n"))
|
9
16
|
end
|
10
17
|
end
|
@@ -16,14 +16,44 @@ RSpec.describe Verse::Wrapping, '.wrap' do
|
|
16
16
|
expect(wrapping.wrap(nil)).to eq(text)
|
17
17
|
end
|
18
18
|
|
19
|
-
it '
|
19
|
+
it "doesn't wrap at length exceeding content length" do
|
20
20
|
wrapping = Verse::Wrapping.new(text)
|
21
|
-
expect(wrapping.wrap(
|
21
|
+
expect(wrapping.wrap(100)).to eq(text)
|
22
22
|
end
|
23
23
|
|
24
|
-
it "
|
24
|
+
it "wraps correctly unbreakable words" do
|
25
|
+
wrapping = Verse::Wrapping.new('foobar1')
|
26
|
+
expect(wrapping.wrap(3)).to eq([
|
27
|
+
"foo",
|
28
|
+
"bar",
|
29
|
+
"1"
|
30
|
+
].join("\n"))
|
31
|
+
end
|
32
|
+
|
33
|
+
it "wraps ascii text" do
|
34
|
+
text = "for there is no folly of the beast of the earth which is not infinitely outdone by the madness of men "
|
25
35
|
wrapping = Verse::Wrapping.new(text)
|
26
|
-
expect(wrapping.wrap(
|
36
|
+
expect(wrapping.wrap(16)).to eq([
|
37
|
+
"for there is no",
|
38
|
+
"folly of the",
|
39
|
+
"beast of the",
|
40
|
+
"earth which is",
|
41
|
+
"not infinitely",
|
42
|
+
"outdone by the",
|
43
|
+
"madness of men"
|
44
|
+
].join("\n"))
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'wraps at 8 characters' do
|
48
|
+
wrapping = Verse::Wrapping.new(text)
|
49
|
+
expect(wrapping.wrap(8)).to eq([
|
50
|
+
"ラドクリ",
|
51
|
+
"フ、マラ",
|
52
|
+
"ソン五輪",
|
53
|
+
"代表に1",
|
54
|
+
"万m出場",
|
55
|
+
"にも含み"
|
56
|
+
].join("\n"))
|
27
57
|
end
|
28
58
|
end
|
29
59
|
|
@@ -53,36 +83,17 @@ RSpec.describe Verse::Wrapping, '.wrap' do
|
|
53
83
|
end
|
54
84
|
end
|
55
85
|
|
56
|
-
context 'when indented' do
|
57
|
-
let(:length) { 8 }
|
58
|
-
let(:indent) { 4 }
|
59
|
-
|
60
|
-
it "wraps with indentation" do
|
61
|
-
text = 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
|
62
|
-
wrapping = Verse::Wrapping.new(text)
|
63
|
-
expect(wrapping.wrap(8, indent: 4)).to eq(" ラドクリフ、マラ\n ソン五輪代表に1\n 万m出場にも含み")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context 'with ansi colors' do
|
68
|
-
it "wraps text with ANSII codes" do
|
69
|
-
text = "\[\033[01;32m\]Hey have\[\033[01;34m\]some cake\[\033[00m\]"
|
70
|
-
wrapping = Verse::Wrapping.new(text)
|
71
|
-
expect(wrapping.wrap(8)).to eq("\[\033[01;32m\]Hey have\[\033[01;34m\]some\ncake\[\033[00m\]")
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
86
|
context 'with newlines' do
|
76
87
|
it "preserves newlines for both prefix and postfix" do
|
77
88
|
text = "\n\nラドクリフ、マラソン五輪代表に1万m出場にも含み\n\n\n"
|
78
89
|
wrapping = Verse::Wrapping.new(text)
|
79
|
-
expect(wrapping.wrap(10)).to eq(
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
90
|
+
expect(wrapping.wrap(10)).to eq([
|
91
|
+
"\n\nラドクリフ",
|
92
|
+
"、マラソン",
|
93
|
+
"五輪代表に",
|
94
|
+
"1万m出場に",
|
95
|
+
"も含み\n\n\n"
|
96
|
+
].join("\n"))
|
86
97
|
end
|
87
98
|
end
|
88
99
|
end
|
data/verse.gemspec
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: verse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: unicode_utils
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.4.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.4.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -36,6 +50,7 @@ files:
|
|
36
50
|
- .rspec
|
37
51
|
- .ruby-version
|
38
52
|
- .travis.yml
|
53
|
+
- CHANGELOG.md
|
39
54
|
- Gemfile
|
40
55
|
- LICENSE.txt
|
41
56
|
- README.md
|
@@ -56,7 +71,6 @@ files:
|
|
56
71
|
- spec/unit/truncation/new_spec.rb
|
57
72
|
- spec/unit/truncation/truncate_spec.rb
|
58
73
|
- spec/unit/wrap_spec.rb
|
59
|
-
- spec/unit/wrapping/new_spec.rb
|
60
74
|
- spec/unit/wrapping/wrap_spec.rb
|
61
75
|
- tasks/console.rake
|
62
76
|
- tasks/coverage.rake
|
@@ -98,6 +112,5 @@ test_files:
|
|
98
112
|
- spec/unit/truncation/new_spec.rb
|
99
113
|
- spec/unit/truncation/truncate_spec.rb
|
100
114
|
- spec/unit/wrap_spec.rb
|
101
|
-
- spec/unit/wrapping/new_spec.rb
|
102
115
|
- spec/unit/wrapping/wrap_spec.rb
|
103
116
|
has_rdoc:
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe Verse::Wrapping, '#new' do
|
6
|
-
let(:text) { "There go the ships; there is that Leviathan whom thou hast made to play therein."}
|
7
|
-
|
8
|
-
it "defaults indentation to 0" do
|
9
|
-
wrapping = Verse::Wrapping.new(text)
|
10
|
-
expect(wrapping.indent).to eq(0)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "defaults paddnig to empty array" do
|
14
|
-
wrapping = Verse::Wrapping.new(text)
|
15
|
-
expect(wrapping.padding).to eq([])
|
16
|
-
end
|
17
|
-
|
18
|
-
it "allows to set global indenation" do
|
19
|
-
wrapping = Verse::Wrapping.new(text, indent: 5)
|
20
|
-
expect(wrapping.indent).to eq(5)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "allows to set global padding" do
|
24
|
-
wrapping = Verse::Wrapping.new(text, padding: [1,2,3,4])
|
25
|
-
expect(wrapping.padding).to eq([1,2,3,4])
|
26
|
-
end
|
27
|
-
end
|