From 963c50eca87373bed403358c076b377ad62454ef Mon Sep 17 00:00:00 2001 From: Guirec Corbel Date: Sat, 28 Jul 2012 17:34:36 -0400 Subject: 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. --- actionpack/CHANGELOG.md | 2 + actionpack/lib/action_view/helpers/text_helper.rb | 60 ++++++++++++++++++----- actionpack/test/template/text_helper_test.rb | 13 +++++ 3 files changed, 64 insertions(+), 11 deletions(-) (limited to 'actionpack') 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 :radius option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters # defined in :radius (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+, - # then the :omission 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 :omission option (which defaults to "...") will be prepended/appended accordingly. The + # :separator 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 => ' ') # # => 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
') || 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 -- cgit v1.2.3