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