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