Skip to content

Commit d57397c

Browse files
author
David Heinemeier Hansson
committed
Extracted String#truncate from TextHelper#truncate [DHH]
1 parent ea037ff commit d57397c

File tree

4 files changed

+69
-11
lines changed

4 files changed

+69
-11
lines changed

actionpack/lib/action_view/helpers/text_helper.rb

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'active_support/core_ext/object/blank'
2+
require 'active_support/core_ext/string/filters'
23
require 'action_view/helpers/tag_helper'
34

45
module ActionView
@@ -42,17 +43,14 @@ def safe_concat(string)
4243
# ==== Examples
4344
#
4445
# truncate("Once upon a time in a world far far away")
45-
# # => Once upon a time in a world...
46+
# # => Once upon a time in a worl...
4647
#
4748
# truncate("Once upon a time in a world far far away", :separator => ' ')
4849
# # => Once upon a time in a world...
4950
#
5051
# truncate("Once upon a time in a world far far away", :length => 14)
5152
# # => Once upon a...
5253
#
53-
# truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)")
54-
# # => And they found t(clipped)
55-
#
5654
# truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 25)
5755
# # => And they f... (continued)
5856
#
@@ -73,14 +71,10 @@ def truncate(text, *args)
7371
options[:length] = args[0] || 30
7472
options[:omission] = args[1] || "..."
7573
end
76-
options.reverse_merge!(:length => 30, :omission => "...")
7774

78-
if text
79-
l = options[:length] - options[:omission].mb_chars.length
80-
chars = text.mb_chars
81-
stop = options[:separator] ? (chars.rindex(options[:separator].mb_chars, l) || l) : l
82-
(chars.length > options[:length] ? chars[0...stop] + options[:omission] : text).to_s
83-
end
75+
options.reverse_merge!(:length => 30)
76+
77+
text.truncate(options.delete(:length), options) if text
8478
end
8579

8680
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into

activesupport/CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
*Rails 3.0.0 [beta 4/release candidate] (unreleased)*
22

3+
* Extracted String#truncate from TextHelper#truncate [DHH]
4+
35
* Ruby 1.9: support UTF-8 case folding. #4595 [Norman Clarke]
46

57
* Renames Array#rand -> Array#random_element. [Santiago Pastorino, Rizwan Reza]

activesupport/lib/active_support/core_ext/string/filters.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require 'active_support/core_ext/string/multibyte'
2+
13
class String
24
# Returns the string, first removing all whitespace on both ends of
35
# the string, and then changing remaining consecutive whitespace
@@ -17,4 +19,35 @@ def squish!
1719
gsub!(/\s+/, ' ')
1820
self
1921
end
22+
23+
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>.
24+
# The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
25+
# for a total length not exceeding <tt>:length</tt>.
26+
#
27+
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
28+
#
29+
# ==== Examples
30+
#
31+
# "Once upon a time in a world far far away".truncate(30)
32+
# # => Once upon a time in a worl...
33+
#
34+
# "Once upon a time in a world far far away".truncate(30, :separator => ' ')
35+
# # => Once upon a time in a world...
36+
#
37+
# "Once upon a time in a world far far away".truncate(14)
38+
# # => Once upon a...
39+
#
40+
# "And they found that many people were sleeping better.".truncate(25, :omission => "... (continued)")
41+
# # => And they f... (continued)
42+
def truncate(length, options = {})
43+
text = self.dup
44+
options[:omission] ||= "..."
45+
46+
length_with_room_for_omission = length - options[:omission].mb_chars.length
47+
chars = text.mb_chars
48+
stop = options[:separator] ?
49+
(chars.rindex(options[:separator].mb_chars, length_with_room_for_omission) || length_with_room_for_omission) : length_with_room_for_omission
50+
51+
(chars.length > length ? chars[0...stop] + options[:omission] : text).to_s
52+
end
2053
end

activesupport/test/core_ext/string_ext_test.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,35 @@ def test_string_squish
210210
# And changes the original string:
211211
assert_equal original, expected
212212
end
213+
214+
def test_truncate
215+
assert_equal "Hello World!", "Hello World!".truncate(12)
216+
assert_equal "Hello Wor...", "Hello World!!".truncate(12)
217+
end
218+
219+
def test_truncate_with_omission_and_seperator
220+
assert_equal "Hello[...]", "Hello World!".truncate(10, :omission => "[...]")
221+
assert_equal "Hello[...]", "Hello Big World!".truncate(13, :omission => "[...]", :separator => ' ')
222+
assert_equal "Hello Big[...]", "Hello Big World!".truncate(14, :omission => "[...]", :separator => ' ')
223+
assert_equal "Hello Big[...]", "Hello Big World!".truncate(15, :omission => "[...]", :separator => ' ')
224+
end
225+
226+
if RUBY_VERSION < '1.9.0'
227+
def test_truncate_multibyte
228+
with_kcode 'none' do
229+
assert_equal "\354\225\210\353\205\225\355...", "\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224".truncate(10)
230+
end
231+
with_kcode 'u' do
232+
assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...",
233+
"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".truncate(10)
234+
end
235+
end
236+
else
237+
def test_truncate_multibyte
238+
assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
239+
"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8').truncate(10)
240+
end
241+
end
213242
end
214243

215244
class StringBehaviourTest < Test::Unit::TestCase

0 commit comments

Comments
 (0)