aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuirec Corbel <guirec.corbel@gmail.com>2012-07-28 17:34:36 -0400
committerGCorbel <guirec.corbel@gmail.com>2012-09-08 07:24:17 -0400
commit963c50eca87373bed403358c076b377ad62454ef (patch)
tree7a1d91408fdc3435b8a164250d6bfb4b4e5031c0
parentf415475621c79cbc2d93e1ecf10805a4100a5d43 (diff)
downloadrails-963c50eca87373bed403358c076b377ad62454ef.tar.gz
rails-963c50eca87373bed403358c076b377ad62454ef.tar.bz2
rails-963c50eca87373bed403358c076b377ad62454ef.zip
Add a separation option for the excerpt function
The separation option enable to keep entire words, lines or anything. To split by line, like github, we can set the separation option as \n. To split by word, like google, we can set the separation option as " ". The radius option represent the number of lines or words we want to have in the result. The default behaviour is the same. If we don't set the separation option, it split the text any where.
-rw-r--r--actionpack/CHANGELOG.md2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb60
-rw-r--r--actionpack/test/template/text_helper_test.rb13
3 files changed, 64 insertions, 11 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 1232370439..4cf653722f 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* Add `separation` option for `ActionView::Helpers::TextHelper.excerpt`. *Guirec Corbel*
+
* Added controller-level etag additions that will be part of the action etag computation *Jeremy Kemper/DHH*
class InvoicesController < ApplicationController
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 0f599d5f41..069969a710 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -126,8 +126,9 @@ module ActionView
# Extracts an excerpt from +text+ that matches the first instance of +phrase+.
# The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
# defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
- # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The resulting string
- # will be stripped in any case. If the +phrase+ isn't found, nil is returned.
+ # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The
+ # <tt>:separator</tt> enable to choose the delimation. The resulting string will be stripped in any case. If the +phrase+
+ # isn't found, nil is returned.
#
# excerpt('This is an example', 'an', :radius => 5)
# # => ...s is an exam...
@@ -143,21 +144,37 @@ module ActionView
#
# excerpt('This is also an example', 'an', :radius => 8, :omission => '<chop> ')
# # => <chop> is also an example
+ #
+ # excerpt('This is a very beautiful morning', 'very', :separator => ' ', :radius => 1)
+ # # => ...a very beautiful...
def excerpt(text, phrase, options = {})
return unless text && phrase
- radius = options.fetch(:radius, 100)
- omission = options.fetch(:omission, "...")
+ radius = options.fetch(:radius, 100)
+ omission = options.fetch(:omission, "...")
+ separator = options.fetch(:separator, "")
+
+ phrase = Regexp.escape(phrase)
+ regex = /#{phrase}/i
+
+ return unless matches = text.match(regex)
+ phrase = matches[0]
+
+ text.split(separator).each do |value|
+ if value.match(regex)
+ regex = phrase = value
+ break
+ end
+ end
- phrase = Regexp.escape(phrase)
- return unless found_pos = text =~ /(#{phrase})/i
+ first_part, second_part = text.split(regex, 2)
- start_pos = [ found_pos - radius, 0 ].max
- end_pos = [ [ found_pos + phrase.length + radius - 1, 0].max, text.length ].min
+ options = options.merge(:part_position => :first)
+ prefix, first_part = cut_part(first_part, options)
- prefix = start_pos > 0 ? omission : ""
- postfix = end_pos < text.length - 1 ? omission : ""
+ options = options.merge(:part_position => :second)
+ postfix, second_part = cut_part(second_part, options)
- prefix + text[start_pos..end_pos].strip + postfix
+ prefix + (first_part + separator + phrase + separator + second_part).strip + postfix
end
# Attempts to pluralize the +singular+ word unless +count+ is 1. If
@@ -402,6 +419,27 @@ module ActionView
t.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') || t
end
end
+
+ def cut_part(part, options)
+ radius = options.fetch(:radius, 100)
+ omission = options.fetch(:omission, "...")
+ separator = options.fetch(:separator, "")
+ part_position = options.fetch(:part_position)
+
+ return "", "" unless part
+
+ part = part.split(separator)
+ part.delete("")
+ affix = part.size > radius ? omission : ""
+ part = if part_position == :first
+ drop_index = [part.length - radius, 0].max
+ part.drop(drop_index).join(separator)
+ else
+ part.first(radius).join(separator)
+ end
+
+ return affix, part
+ end
end
end
end
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index c0f694b2bf..4525efe73c 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -303,6 +303,19 @@ class TextHelperTest < ActionView::TestCase
assert_equal options, passed_options
end
+ def test_excerpt_with_separator
+ options = { :separator => ' ', :radius => 1 }
+ assert_equal('...a very beautiful...', excerpt('This is a very beautiful morning', 'very', options))
+ assert_equal('This is...', excerpt('This is a very beautiful morning', 'this', options))
+ assert_equal('...beautiful morning', excerpt('This is a very beautiful morning', 'morning', options))
+
+ options = { :separator => "\n", :radius => 0 }
+ assert_equal("...very long...", excerpt("my very\nvery\nvery long\nstring", 'long', options))
+
+ options = { :separator => "\n", :radius => 1 }
+ assert_equal("...very\nvery long\nstring", excerpt("my very\nvery\nvery long\nstring", 'long', options))
+ end
+
def test_word_wrap
assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", :line_width => 15))
end