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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cc1a8ae257dbf4375ade97c14a82a3c503e6cc77
4
- data.tar.gz: 72b99cd80248381793dffdf17da244262102eeed
3
+ metadata.gz: c721dd40817c43013c05f142361a15244fe44c58
4
+ data.tar.gz: c22b4b32258ea42631c727256702ff01642dd79b
5
5
  SHA512:
6
- metadata.gz: 997f15ac54e59b69109e053efeffcbab9fcfb15f2b4b56fa8f64b1e5c08199594ec0c73ac71c78d42764b73eb2aabf7f82f646ac789cbadfb21e9d7690ec9804
7
- data.tar.gz: ffd2478585b1284f9182df2db36aae47371a6d3f8aabaa33a52cd20098859a1b1e3b68a40add61b4802289135f9b6e0cf17ad0122af8d6a1c1594bdb967668d1
6
+ metadata.gz: 1ce1bd97ac18f78a18540c7b41f65760c71ca383f36a082e728c748957a9ca4e746774cbb8adf6d433ab523b88d226ad2a2ba0bbbfc5808b60508dd0a8142d75
7
+ data.tar.gz: 39d08d89c24374654118ed3e05bff810128d94feefec1f55a49b6390db37dcae4e810e554c31749fce58890a39097227be253332ce4d20b046f8d287d1e3c20a
@@ -0,0 +1,5 @@
1
+ 0.2.0 (Feb 15, 2015)
2
+
3
+ * Add unicode support
4
+ * Change wrap, truncate and align to work with unicode characters
5
+ * Remove padding and indent from Verse::Wrapping
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 align
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 `right`, `left` or `center` methods and passing width you can align the text:
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.right(40) # =>
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
- ### 1.2 truncate
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
- ### 1.3 wrap
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 call `wrap`:
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
- wrapping.wrap(30)
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
@@ -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
@@ -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| line.send(method, width, filler) }
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 :ljust
88
- when :right then :rjust
89
- when :center then :center
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
@@ -57,22 +57,48 @@ module Verse
57
57
  #
58
58
  # @api public
59
59
  def truncate(truncate_at = DEFAULT_LENGTH, options = {})
60
- if text.length <= truncate_at.to_i || truncate_at.to_i.zero?
60
+ if display_width(text) <= truncate_at.to_i || truncate_at.to_i.zero?
61
61
  return text.dup
62
62
  end
63
- trail = options.fetch(:trailing) { trailing }
64
- separation = options.fetch(:separator) { separator }
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
- chars = @sanitizer.sanitize(text).chars.to_a
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
- chars[0, stop || length_without_trailing].join + trail
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
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
2
 
3
3
  module Verse
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.0'
5
5
  end # Verse
@@ -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, options)
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
- # # => "Some\nlongish\ntext"
45
- #
46
- # wrapping.wrap(8, indent: 4)
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, options = {})
42
+ def wrap(wrap_at = DEFAULT_WIDTH)
53
43
  if text.length < wrap_at.to_i || wrap_at.to_i.zero?
54
- return text.dup
44
+ return text
55
45
  end
56
-
57
- indentation = options.fetch(:indent) { indent }
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
- private
66
-
67
- # Calculate string length without color escapes
51
+ # Format paragraph to be maximum of wrap_at length
68
52
  #
69
- # @param [String] 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
- # @api private
72
- def actual_length(string, at)
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 wrap_line(line, at)
84
- wrap_at = actual_length(line, at)
85
- line.strip.gsub(/\n/, ' ').squeeze(' ')
86
- .gsub(/(.{1,#{wrap_at}})(?:\s+|$\n?)|(.{1,#{wrap_at}})/, "\\1\\2\n")
87
- .strip
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
- # Indent string by given value
91
- #
92
- # @param [String] text
93
- #
94
- # @return [String]
112
+ protected
113
+
114
+ # The text to wrap
95
115
  #
96
116
  # @api private
97
- def indent_line(text, indent)
98
- text.split(NEWLINE).each do |line|
99
- line.insert(0, SPACE * indent)
100
- end
101
- end
117
+ attr_reader :text
102
118
 
103
- # Add padding to each line in wrapped text
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 pad_line(text, padding)
112
- return text if text.empty? || padding.empty?
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: '*')
@@ -5,6 +5,6 @@ require 'spec_helper'
5
5
  RSpec.describe Verse, '#truncate' do
6
6
  it "truncates text" do
7
7
  text = 'ラドクリフ、マラソン五輪代表に1万m出場にも含み'
8
- expect(Verse.truncate(text, 10)).to eq('ラドクリフ、マラソ…')
8
+ expect(Verse.truncate(text, 12)).to eq('ラドクリフ…')
9
9
  end
10
10
  end
@@ -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("ラドクリフ、マラソン五#{trailing}")
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: ' ')).to eq("ラドクリフ、マラソン五#{trailing}")
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("ラドクリフ、マラソン五#{trailing}")
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("ラドクリフ、#{trailing}")
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 'This is a bold blue text'
61
+ expect(truncation.truncate).to eq(text)
56
62
  end
57
63
 
58
64
  it "finishes on word boundary" do
@@ -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("ラドクリフ、マラ\nソン五輪代表に1\n万m出場にも含み")
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 'wraps at 8 characters' do
19
+ it "doesn't wrap at length exceeding content length" do
20
20
  wrapping = Verse::Wrapping.new(text)
21
- expect(wrapping.wrap(8)).to eq("ラドクリフ、マラ\nソン五輪代表に1\n万m出場にも含み")
21
+ expect(wrapping.wrap(100)).to eq(text)
22
22
  end
23
23
 
24
- it "doesn't wrap at length exceeding content length" do
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(100)).to eq(text)
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("\n\nラドクリフ、マラソン\n五輪代表に1万m出場\nにも含み\n\n\n")
80
- end
81
-
82
- it "preserves newlines with padding" do
83
- text = "\n\nラドクリフ、マラソン五輪代表に1万m出場にも含み\n\n"
84
- wrapping = Verse::Wrapping.new(text)
85
- expect(wrapping.wrap(10, padding: [1,2,3,4])).to eq("\n\n ラドクリフ、マラソン \n 五輪代表に1万m出場 \n にも含み \n\n")
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
@@ -18,5 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(spec)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
+ spec.add_dependency 'unicode_utils', '~> 1.4.0'
22
+
21
23
  spec.add_development_dependency 'bundler', '~> 1.5'
22
24
  end
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.1.1
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-08 00:00:00.000000000 Z
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