Permalink
Browse files

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.
  • Loading branch information...
GCorbel committed Jul 28, 2012
1 parent f415475 commit 963c50eca87373bed403358c076b377ad62454ef
View
@@ -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
@@ -126,8 +126,9 @@ def highlight(text, phrases, options = {})
# 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 @@ def highlight(text, phrases, options = {})
#
# 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 @@ def split_paragraphs(text)
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
@@ -303,6 +303,19 @@ def test_excerpt_does_not_modify_the_options_hash
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

0 comments on commit 963c50e

Please sign in to comment.