truncate_html 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/History.txt +2 -2
- data/README.markdown +19 -9
- data/Rakefile +19 -0
- data/VERSION +1 -1
- data/init.rb +6 -0
- data/lib/app/helpers/truncate_html_helper.rb +3 -1
- data/lib/truncate_html.rb +2 -5
- data/lib/truncate_html/configuration.rb +15 -0
- data/lib/truncate_html/html_string.rb +35 -0
- data/lib/truncate_html/html_truncator.rb +43 -50
- data/spec/helpers/truncate_html_helper_spec.rb +25 -100
- data/spec/spec_helper.rb +1 -1
- data/spec/truncate_html/configuration_spec.rb +17 -0
- data/spec/truncate_html/html_string_spec.rb +76 -0
- data/spec/truncate_html/html_truncator_spec.rb +86 -46
- data/truncate_html.gemspec +11 -4
- metadata +8 -2
data/.gitignore
CHANGED
data/History.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
== 0.2.2 2009-12-23
|
2
|
-
* Fix issue #4: Handle case when supplied length is smaller than omission (ghazel)
|
2
|
+
* Fix issue #4: Handle case when supplied length is smaller than omission. (ghazel)
|
3
3
|
|
4
4
|
== 0.2.1 2009-12-18
|
5
5
|
* Fix issue #3: Handle case when input html contins a script tag.
|
@@ -10,7 +10,7 @@
|
|
10
10
|
helper's behavior.
|
11
11
|
|
12
12
|
== 0.1.2 2009-09-25
|
13
|
-
* Fix issue #1: Handle case when input html is nil.
|
13
|
+
* Fix issue #1: Handle case when input html is nil. (bcardarella)
|
14
14
|
|
15
15
|
== 0.1.1 2009-08-25
|
16
16
|
* Fixed issue with regex which would not recognize <a> tags that contain slashes.
|
data/README.markdown
CHANGED
@@ -3,25 +3,26 @@ TruncateHtml
|
|
3
3
|
|
4
4
|
truncate_html is a Rails helper plugin that [cuts off](http://www.youtube.com/watch?v=6XG4DIOA7nU) a string of HTML and takes care of closing any lingering open tags. There are many possible solutions to this. This plugin does not have any dependencies, and does all it's work via [regular expressions](http://xkcd.com/208/).
|
5
5
|
|
6
|
-
The API is very similar to Rails' own truncate method.
|
6
|
+
The API is very similar to Rails' own <code>truncate()</code> method.
|
7
7
|
|
8
8
|
|
9
9
|
Example
|
10
10
|
-------
|
11
11
|
|
12
12
|
some_html = '<ul><li><a href="http://whatever">This is a link</a></li></ul>'
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
=> <ul><li><a href="http://whatever">This is...(continued)</a></li></ul>
|
17
|
-
|
13
|
+
truncate_html(some_html, :length => 15, :omission => '...(continued)')
|
14
|
+
=> <ul><li><a href="http://whatever">This...(continued)</a></li></ul>
|
18
15
|
|
19
16
|
A few notes:
|
20
17
|
|
21
|
-
* It will truncate on a word boundary.
|
22
18
|
* The default options are:
|
23
19
|
* :length => 100
|
24
20
|
* :omission => '...'
|
21
|
+
* By default, it will truncate on word boundry.
|
22
|
+
To truncate the HTML string strictly at the specified length, pass in the `:word_boundry => false` option.
|
23
|
+
* If the input HTML is nil, it will return an empty string.
|
24
|
+
* The omission text's length does count toward the resulting string's length.
|
25
|
+
* `<script>` tags will pass right through - they will not count toward the resulting string's length, or be truncated.
|
25
26
|
|
26
27
|
Installation
|
27
28
|
------------
|
@@ -33,9 +34,9 @@ Add this to your <code>config/environment.rb</code>:
|
|
33
34
|
:source => 'http://gemcutter.org'
|
34
35
|
|
35
36
|
Then either
|
36
|
-
<code>
|
37
|
+
<code>rake gems:install</code>
|
37
38
|
or
|
38
|
-
<code>
|
39
|
+
<code>gem install truncate_html</code>
|
39
40
|
|
40
41
|
#### As a plugin:
|
41
42
|
<code>script/plugin install git://github.com/hgimenez/truncate_html.git</code>
|
@@ -50,4 +51,13 @@ Testing
|
|
50
51
|
|
51
52
|
The plugin is tested using RSpec. [Install it](http://wiki.github.com/dchelimsky/rspec/rails) on your app if you wish to run the tests.
|
52
53
|
|
54
|
+
If you want to hack on this, here's how to set up a development/testing environment:
|
55
|
+
|
56
|
+
$ rails truncate_html_base
|
57
|
+
$ cd truncate_html_base
|
58
|
+
$ git clone git://github.com/hgimenez/truncate_html.git vendor/plugins/truncate_html
|
59
|
+
# install RSpec, follow instructions at http://wiki.github.com/dchelimsky/rspec/rails
|
60
|
+
$ cd vendor/plugins/truncate_html
|
61
|
+
$ rake spec # all green? Go hack
|
62
|
+
|
53
63
|
Copyright (c) 2009 Harold A. Giménez, released under the MIT license
|
data/Rakefile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'spec/rake/spectask'
|
3
3
|
|
4
|
+
|
4
5
|
desc 'Default: run specs.'
|
5
6
|
task :default => :spec
|
6
7
|
|
@@ -16,6 +17,24 @@ Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
16
17
|
spec.rcov = true
|
17
18
|
end
|
18
19
|
|
20
|
+
begin
|
21
|
+
require 'metric_fu'
|
22
|
+
MetricFu::Configuration.run do |config|
|
23
|
+
config.metrics = [:saikuro, :flog, :flay, :reek, :roodi, :rcov]
|
24
|
+
config.graphs = [:flog, :flay, :reek, :roodi, :rcov]
|
25
|
+
config.rcov = { :environment => 'test',
|
26
|
+
:test_files => ['spec/**/*_spec.rb'],
|
27
|
+
:rcov_opts => ["--sort coverage",
|
28
|
+
"--text-coverage",
|
29
|
+
"--profile",
|
30
|
+
"--exclude /gems/,/Library/"]}
|
31
|
+
config.graph_engine = :bluff
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
puts "Install metric_fu to run code metrics"
|
35
|
+
end
|
36
|
+
|
37
|
+
|
19
38
|
begin
|
20
39
|
require 'jeweler'
|
21
40
|
Jeweler::Tasks.new do |gem|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/init.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
module TruncateHtmlHelper
|
2
2
|
|
3
3
|
def truncate_html(html, options={})
|
4
|
-
|
4
|
+
return '' if html.nil?
|
5
|
+
html_string = TruncateHtml::HtmlString.new(html)
|
6
|
+
TruncateHtml::HtmlTruncator.new(html_string).truncate(options)
|
5
7
|
end
|
6
8
|
|
7
9
|
end
|
data/lib/truncate_html.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
|
-
path = File.join(File.dirname(__FILE__), 'app', 'helpers')
|
2
|
-
$LOAD_PATH << path
|
3
|
-
ActiveSupport::Dependencies.load_paths << path
|
4
|
-
ActiveSupport::Dependencies.load_once_paths.delete(path)
|
5
|
-
|
6
1
|
require File.join(File.dirname(__FILE__), 'truncate_html', 'html_truncator')
|
2
|
+
require File.join(File.dirname(__FILE__), 'truncate_html', 'configuration')
|
3
|
+
require File.join(File.dirname(__FILE__), 'app', 'helpers', 'truncate_html_helper')
|
7
4
|
|
8
5
|
ActionView::Base.class_eval do
|
9
6
|
include TruncateHtmlHelper
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module TruncateHtml
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :length, :omission, :word_boundry
|
4
|
+
end
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :configuration
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configure
|
11
|
+
self.configuration ||= Configuration.new
|
12
|
+
yield configuration
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module TruncateHtml
|
2
|
+
class HtmlString < String
|
3
|
+
|
4
|
+
UNPAIRED_TAGS = %w(br hr img)
|
5
|
+
|
6
|
+
def initialize(original_html)
|
7
|
+
super(original_html)
|
8
|
+
end
|
9
|
+
|
10
|
+
def html_tokens
|
11
|
+
scan(/(?:<script.*>.*<\/script>)+|<\/?[^>]+>|[\w\|`~!@#\$%^&*\(\)\-_\+=\[\]{}:;'",\.\/?]+|\s+/).map do
|
12
|
+
|token| token.gsub(
|
13
|
+
#remove newline characters
|
14
|
+
/\n/,''
|
15
|
+
).gsub(
|
16
|
+
#clean out extra consecutive whitespace
|
17
|
+
/\s+/, ' '
|
18
|
+
)
|
19
|
+
end.map { |token| HtmlString.new(token) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def html_tag?
|
23
|
+
self =~ /<\/?[^>]+>/ ? true : false
|
24
|
+
end
|
25
|
+
|
26
|
+
def open_tag?
|
27
|
+
self =~ /<(?!(?:#{UNPAIRED_TAGS.join('|')}|script|\/))[^>]+>/i ? true : false
|
28
|
+
end
|
29
|
+
|
30
|
+
def matching_close_tag
|
31
|
+
gsub(/<(\w+)\s?.*>/, '</\1>').strip
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -1,74 +1,67 @@
|
|
1
1
|
module TruncateHtml
|
2
2
|
class HtmlTruncator
|
3
3
|
|
4
|
-
UNPAIRED_TAGS = %w(br hr img)
|
5
|
-
|
6
4
|
def initialize(original_html)
|
7
|
-
@original_html
|
5
|
+
@original_html = original_html
|
8
6
|
end
|
9
7
|
|
10
8
|
def truncate(options = {})
|
11
|
-
|
12
|
-
options[:
|
13
|
-
options[:
|
14
|
-
@chars_remaining =
|
15
|
-
@open_tags,
|
9
|
+
length = options[:length] || TruncateHtml.configuration.length
|
10
|
+
@omission = options[:omission] || TruncateHtml.configuration.omission
|
11
|
+
@word_boundry = (options.has_key?(:word_boundry) ? options[:word_boundry] : TruncateHtml.configuration.word_boundry)
|
12
|
+
@chars_remaining = length - @omission.length
|
13
|
+
@open_tags, @truncated_html = [], ['']
|
16
14
|
|
17
|
-
html_tokens.each do |
|
15
|
+
@original_html.html_tokens.each do |token|
|
16
|
+
#if truncate_more?(token)
|
18
17
|
if @chars_remaining > 0
|
19
|
-
|
20
|
-
if open_tag?(str)
|
21
|
-
@open_tags << str
|
22
|
-
else
|
23
|
-
open_tags = remove_latest_open_tag(str)
|
24
|
-
end
|
25
|
-
else
|
26
|
-
@chars_remaining -= str.length
|
27
|
-
end
|
28
|
-
result << str
|
18
|
+
process_token(token)
|
29
19
|
else
|
30
|
-
|
31
|
-
@open_tags.reverse_each do |open_tag|
|
32
|
-
result << matching_close_tag(open_tag)
|
33
|
-
end
|
20
|
+
close_open_tags
|
34
21
|
break
|
35
22
|
end
|
36
23
|
end
|
37
|
-
|
24
|
+
@truncated_html.join
|
38
25
|
end
|
39
26
|
|
40
|
-
|
41
|
-
@original_html.scan(/(?:<script.*>.*<\/script>)+|<\/?[^>]+>|[\w\|`~!@#\$%^&*\(\)\-_\+=\[\]{}:;'",\.\/?]+|\s+/).map do
|
42
|
-
|t| t.gsub(
|
43
|
-
#remove newline characters
|
44
|
-
/\n/,''
|
45
|
-
).gsub(
|
46
|
-
#clean out extra consecutive whitespace
|
47
|
-
/\s+/, ' '
|
48
|
-
)
|
49
|
-
end
|
50
|
-
end
|
27
|
+
private
|
51
28
|
|
52
|
-
|
53
|
-
|
54
|
-
|
29
|
+
def process_token(token)
|
30
|
+
append_to_result(token)
|
31
|
+
if token.html_tag?
|
32
|
+
if token.open_tag?
|
33
|
+
@open_tags << token
|
34
|
+
else
|
35
|
+
remove_latest_open_tag(token)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
@chars_remaining -= (@word_boundry ? token.length : token[0, @chars_remaining].length)
|
39
|
+
end
|
40
|
+
end
|
55
41
|
|
56
|
-
|
57
|
-
|
58
|
-
|
42
|
+
def append_to_result(token)
|
43
|
+
if @word_boundry
|
44
|
+
@truncated_html << token
|
45
|
+
else
|
46
|
+
@truncated_html << token[0, @chars_remaining]
|
47
|
+
end
|
48
|
+
end
|
59
49
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
@
|
64
|
-
break
|
50
|
+
def close_open_tags
|
51
|
+
@truncated_html[-1] = @truncated_html[-1].rstrip + @omission
|
52
|
+
@open_tags.reverse_each do |open_tag|
|
53
|
+
@truncated_html << open_tag.matching_close_tag
|
65
54
|
end
|
66
55
|
end
|
67
|
-
end
|
68
56
|
|
69
|
-
|
70
|
-
|
71
|
-
|
57
|
+
def remove_latest_open_tag(close_tag)
|
58
|
+
(0...@open_tags.length).to_a.reverse.each do |index|
|
59
|
+
if @open_tags[index].matching_close_tag == close_tag
|
60
|
+
@open_tags.delete_at(index)
|
61
|
+
break
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
72
65
|
|
73
66
|
end
|
74
67
|
end
|
@@ -2,115 +2,40 @@ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
|
2
2
|
|
3
3
|
include TruncateHtmlHelper
|
4
4
|
|
5
|
+
class Truncator
|
6
|
+
include TruncateHtmlHelper
|
7
|
+
end
|
8
|
+
|
5
9
|
describe TruncateHtmlHelper do
|
6
10
|
|
11
|
+
def truncator
|
12
|
+
@truncator ||= Truncator.new
|
13
|
+
end
|
14
|
+
|
7
15
|
it 'is included in ActionView::Base' do
|
8
16
|
ActionView::Base.included_modules.should include(TruncateHtmlHelper)
|
9
17
|
end
|
10
18
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
truncate_html('a b c', :length => 4, :omission => '...').should == 'a...'
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'supports omissions longer than the maximum length' do
|
18
|
-
lambda { truncate_html('', :length => 1, :omission => '...') }.should_not raise_error
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'returns the omission when the specified length is smaller than the omission' do
|
22
|
-
truncate_html('a b c', :length => 2, :omission => '...').should == '...'
|
23
|
-
end
|
24
|
-
|
25
|
-
context 'the input html is nil' do
|
26
|
-
it 'returns an empty string' do
|
27
|
-
truncate_html(nil).should be_empty
|
28
|
-
truncate_html(nil).should be_kind_of(String)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context 'the input html contains a script tag' do
|
33
|
-
before(:each) do
|
34
|
-
@input_html = "<p>I have a script <script type=text/javascript>document.write('lum dee dum');</script> and more text</p>"
|
35
|
-
@expected_out = "<p>I have a script <script type=text/javascript>document.write('lum dee dum');</script> and...</p>"
|
36
|
-
end
|
37
|
-
it 'treats the script tag as lengthless string' do
|
38
|
-
truncate_html(@input_html, :length => 23).should == @expected_out
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context 'truncating in the middle of a link' do
|
43
|
-
before(:each) do
|
44
|
-
@html = '<div><ul><li>Look at <a href="foo">this</a> link </li></ul></div>'
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'truncates, and closes the <a>, and closes any remaining open tags' do
|
48
|
-
truncate_html(@html, :length => 14).should == '<div><ul><li>Look at <a href="foo">this...</a></li></ul></div>'
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
%w(! @ # $ % ^ & * \( \) - _ + = [ ] { } \ | , . / ?).each do |char|
|
53
|
-
context "when the html has a #{char} character after a closing tag" do
|
54
|
-
before(:each) do
|
55
|
-
@html = "<p>Look at <strong>this</strong>#{char} More words here</p>"
|
56
|
-
end
|
57
|
-
it 'places the punctuation after the tag without any whitespace' do
|
58
|
-
truncate_html(@html, :length => 19).should == "<p>Look at <strong>this</strong>#{char} More...</p>"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
context 'when the html has a non punctuation character after a closing tag' do
|
64
|
-
before(:each) do
|
65
|
-
@html = '<p>Look at <a href="awesomeful.net">this</a> link for randomness</p>'
|
66
|
-
end
|
67
|
-
it 'leaves a whitespace between the closing tag and the following word character' do
|
68
|
-
truncate_html(@html, :length => 21).should == '<p>Look at <a href="awesomeful.net">this</a> link...</p>'
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
#unusual, but just covering my ass
|
73
|
-
context 'when the HTML tags are multiline' do
|
74
|
-
before(:each) do
|
75
|
-
@html = <<-END_HTML
|
76
|
-
<div id="foo"
|
77
|
-
class="bar">
|
78
|
-
This is ugly html.
|
79
|
-
</div>
|
80
|
-
END_HTML
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'recognizes the multiline html properly' do
|
84
|
-
truncate_html(@html, :length => 12).should == ' <div id="foo" class="bar"> This is...</div>'
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
%w(br hr img).each do |unpaired_tag|
|
89
|
-
context "when the html contains a #{unpaired_tag} tag" do
|
19
|
+
before(:each) do
|
20
|
+
@html_truncator_mock = mock(TruncateHtml::HtmlTruncator)
|
21
|
+
end
|
90
22
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
it "does not close the #{unpaired_tag} tag" do
|
97
|
-
truncate_html(@html, :length => 19).should == "<div>Some before. <#{unpaired_tag}>and...</div>"
|
98
|
-
truncate_html(@html_caps, :length => 19).should == "<div>Some before. <#{unpaired_tag.capitalize}>and...</div>"
|
99
|
-
end
|
100
|
-
end
|
23
|
+
it 'creates an instance of HtmlTruncator and calls truncate() on it' do
|
24
|
+
@html_truncator_mock.stub!(:truncate)
|
25
|
+
TruncateHtml::HtmlTruncator.should_receive(:new).and_return(@html_truncator_mock)
|
26
|
+
truncator.truncate_html('foo')
|
27
|
+
end
|
101
28
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
it "does not close the #{unpaired_tag} tag" do
|
108
|
-
truncate_html(@html, :length => 19).should == "<div>Some before. <#{unpaired_tag} />and...</div>"
|
109
|
-
truncate_html(@html_caps, :length => 19).should == "<div>Some before. <#{unpaired_tag.capitalize} />and...</div>"
|
110
|
-
end
|
111
|
-
end
|
29
|
+
it 'calls truncate() on the HtmlTruncator object' do
|
30
|
+
TruncateHtml::HtmlTruncator.stub!(:new).and_return(@html_truncator_mock)
|
31
|
+
@html_truncator_mock.should_receive(:truncate).with({}).once
|
32
|
+
truncator.truncate_html('foo')
|
33
|
+
end
|
112
34
|
|
113
|
-
|
35
|
+
context 'the input html is nil' do
|
36
|
+
it 'returns an empty string' do
|
37
|
+
truncator.truncate_html(nil).should be_empty
|
38
|
+
truncator.truncate_html(nil).should be_kind_of(String)
|
114
39
|
end
|
115
40
|
end
|
116
41
|
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe TruncateHtml::Configuration do
|
4
|
+
|
5
|
+
describe 'self.configure' do
|
6
|
+
|
7
|
+
it 'yields the configuration object' do
|
8
|
+
lambda do
|
9
|
+
TruncateHtml.configure do |config|
|
10
|
+
config.should be_kind_of(TruncateHtml::Configuration)
|
11
|
+
throw :yay_it_yielded
|
12
|
+
end
|
13
|
+
end.should throw_symbol(:yay_it_yielded)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe TruncateHtml::HtmlString do
|
4
|
+
|
5
|
+
def html_string(original_string)
|
6
|
+
TruncateHtml::HtmlString.new(original_string)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#html_tokens' do
|
10
|
+
before(:each) do
|
11
|
+
@html = '<h1>Hi there</h1> <p>This is sweet!</p>'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns each token in the string as an array element removing any consecutive whitespace from the string' do
|
15
|
+
html_string(@html).html_tokens.should == ['<h1>', 'Hi', ' ', 'there', '</h1>', ' ', '<p>', 'This', ' ', 'is', ' ', 'sweet!', '</p>']
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#html_tag?' do
|
21
|
+
|
22
|
+
it 'returns false when the string parameter is not an html tag' do
|
23
|
+
html_string('no tags').html_tag?.should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns true when the string parameter is an html tag' do
|
27
|
+
html_string('<img src="foo">').html_tag?.should be_true
|
28
|
+
html_string('</img>').html_tag?.should be_true
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#open_tag?' do
|
34
|
+
|
35
|
+
it 'returns true if the tag is an open tag' do
|
36
|
+
html_string('<a>').open_tag?.should be_true
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'the tag is an open tag, and has whitespace and html properties' do
|
40
|
+
it 'returns true if it has single quotes' do
|
41
|
+
html_string(" <a href='http://awesomeful.net' >").open_tag?.should be_true
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns true if it has double quotes' do
|
45
|
+
html_string(' <a href="http://awesomeful.net">').open_tag?.should be_true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns false if the tag is a close tag' do
|
50
|
+
html_string('</a>').open_tag?.should be_false
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns false if the string is not an html tag' do
|
54
|
+
html_string('foo bar').open_tag?.should be_false
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'returns false if it is a <script> tag' do
|
58
|
+
html_string('<script>').open_tag?.should be_false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#matching_close_tag' do
|
63
|
+
tag_pairs = { '<a>' => '</a>',
|
64
|
+
' <div>' => '</div>',
|
65
|
+
'<h1>' => '</h1>',
|
66
|
+
'<a href="foo">' => '</a>' }
|
67
|
+
|
68
|
+
tag_pairs.each do |open_tag, close_tag|
|
69
|
+
it "closes a #{open_tag} and returns #{close_tag}" do
|
70
|
+
html_string(open_tag).matching_close_tag.should == close_tag
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -2,73 +2,113 @@ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
|
2
2
|
|
3
3
|
describe TruncateHtml::HtmlTruncator do
|
4
4
|
|
5
|
-
def
|
6
|
-
|
5
|
+
def truncate(html, opts = {})
|
6
|
+
html_string = TruncateHtml::HtmlString.new(html)
|
7
|
+
TruncateHtml::HtmlTruncator.new(html_string).truncate(opts)
|
7
8
|
end
|
8
9
|
|
9
|
-
describe '#
|
10
|
-
before(:each) do
|
11
|
-
@html = '<h1>Hi there</h1> <p>This is sweet!</p>'
|
12
|
-
end
|
10
|
+
describe '#truncate' do
|
13
11
|
|
14
|
-
|
15
|
-
|
12
|
+
context 'when the word_boundry option is set to false' do
|
13
|
+
it 'truncates to the exact length specified' do
|
14
|
+
truncate('<div>123456789</div>', :length => 5, :omission => '', :word_boundry => false).should == '<div>12345</div>'
|
15
|
+
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
describe '#html_tag?' do
|
21
|
-
|
22
|
-
it 'returns false when the string parameter is not an html tag' do
|
23
|
-
truncator.html_tag?('no tags').should be_false
|
18
|
+
it "includes the omission text's length in the returned truncated html" do
|
19
|
+
truncate('a b c', :length => 4, :omission => '...').should == 'a...'
|
24
20
|
end
|
25
21
|
|
26
|
-
it '
|
27
|
-
|
28
|
-
truncator.html_tag?('</img>').should be_true
|
22
|
+
it 'supports omissions longer than the maximum length' do
|
23
|
+
lambda { truncate('', :length => 1, :omission => '...') }.should_not raise_error
|
29
24
|
end
|
30
25
|
|
31
|
-
|
32
|
-
|
33
|
-
describe '#open_tag?' do
|
34
|
-
|
35
|
-
it 'returns true if the tag is an open tag' do
|
36
|
-
truncator.open_tag?('<a>').should be_true
|
26
|
+
it 'returns the omission when the specified length is smaller than the omission' do
|
27
|
+
truncate('a b c', :length => 2, :omission => '...').should == '...'
|
37
28
|
end
|
38
29
|
|
39
|
-
|
40
|
-
|
41
|
-
|
30
|
+
context 'the input html contains a script tag' do
|
31
|
+
before(:each) do
|
32
|
+
@input_html = "<p>I have a script <script type=text/javascript>document.write('lum dee dum');</script> and more text</p>"
|
33
|
+
@expected_out = "<p>I have a script <script type=text/javascript>document.write('lum dee dum');</script> and...</p>"
|
34
|
+
end
|
35
|
+
it 'treats the script tag as lengthless string' do
|
36
|
+
truncate(@input_html, :length => 23).should == @expected_out
|
37
|
+
end
|
42
38
|
end
|
43
39
|
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
context 'truncating in the middle of a link' do
|
41
|
+
before(:each) do
|
42
|
+
@html = '<div><ul><li>Look at <a href="foo">this</a> link </li></ul></div>'
|
43
|
+
end
|
47
44
|
|
48
|
-
|
49
|
-
|
45
|
+
it 'truncates, and closes the <a>, and closes any remaining open tags' do
|
46
|
+
truncate(@html, :length => 14).should == '<div><ul><li>Look at <a href="foo">this...</a></li></ul></div>'
|
47
|
+
end
|
50
48
|
end
|
51
49
|
|
52
|
-
|
53
|
-
|
50
|
+
%w(! @ # $ % ^ & * \( \) - _ + = [ ] { } \ | , . / ?).each do |char|
|
51
|
+
context "when the html has a #{char} character after a closing tag" do
|
52
|
+
before(:each) do
|
53
|
+
@html = "<p>Look at <strong>this</strong>#{char} More words here</p>"
|
54
|
+
end
|
55
|
+
it 'places the punctuation after the tag without any whitespace' do
|
56
|
+
truncate(@html, :length => 19).should == "<p>Look at <strong>this</strong>#{char} More...</p>"
|
57
|
+
end
|
58
|
+
end
|
54
59
|
end
|
55
|
-
end
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
context 'when the html has a non punctuation character after a closing tag' do
|
62
|
+
before(:each) do
|
63
|
+
@html = '<p>Look at <a href="awesomeful.net">this</a> link for randomness</p>'
|
64
|
+
end
|
65
|
+
it 'leaves a whitespace between the closing tag and the following word character' do
|
66
|
+
truncate(@html, :length => 21).should == '<p>Look at <a href="awesomeful.net">this</a> link...</p>'
|
67
|
+
end
|
64
68
|
end
|
65
|
-
end
|
66
69
|
|
67
|
-
|
70
|
+
#unusual, but just covering my ass
|
71
|
+
context 'when the HTML tags are multiline' do
|
72
|
+
before(:each) do
|
73
|
+
@html = <<-END_HTML
|
74
|
+
<div id="foo"
|
75
|
+
class="bar">
|
76
|
+
This is ugly html.
|
77
|
+
</div>
|
78
|
+
END_HTML
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'recognizes the multiline html properly' do
|
82
|
+
truncate(@html, :length => 12).should == ' <div id="foo" class="bar"> This is...</div>'
|
83
|
+
end
|
84
|
+
end
|
68
85
|
|
69
|
-
|
70
|
-
|
71
|
-
|
86
|
+
%w(br hr img).each do |unpaired_tag|
|
87
|
+
context "when the html contains a #{unpaired_tag} tag" do
|
88
|
+
|
89
|
+
context "and the #{unpaired_tag} does not have the closing slash" do
|
90
|
+
before(:each) do
|
91
|
+
@html = "<div>Some before. <#{unpaired_tag}>and some after</div>"
|
92
|
+
@html_caps = "<div>Some before. <#{unpaired_tag.capitalize}>and some after</div>"
|
93
|
+
end
|
94
|
+
it "does not close the #{unpaired_tag} tag" do
|
95
|
+
truncate(@html, :length => 19).should == "<div>Some before. <#{unpaired_tag}>and...</div>"
|
96
|
+
truncate(@html_caps, :length => 19).should == "<div>Some before. <#{unpaired_tag.capitalize}>and...</div>"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "and the #{unpaired_tag} does have the closing slash" do
|
101
|
+
before(:each) do
|
102
|
+
@html = "<div>Some before. <#{unpaired_tag} />and some after</div>"
|
103
|
+
@html_caps = "<div>Some before. <#{unpaired_tag.capitalize} />and some after</div>"
|
104
|
+
end
|
105
|
+
it "does not close the #{unpaired_tag} tag" do
|
106
|
+
truncate(@html, :length => 19).should == "<div>Some before. <#{unpaired_tag} />and...</div>"
|
107
|
+
truncate(@html_caps, :length => 19).should == "<div>Some before. <#{unpaired_tag.capitalize} />and...</div>"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
72
112
|
end
|
73
113
|
end
|
74
114
|
|
data/truncate_html.gemspec
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{truncate_html}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["hgimenez"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2010-02-02}
|
13
13
|
s.description = %q{Truncates html so you don't have to}
|
14
14
|
s.email = %q{harold.gimenez@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -25,9 +25,13 @@ Gem::Specification.new do |s|
|
|
25
25
|
"install.rb",
|
26
26
|
"lib/app/helpers/truncate_html_helper.rb",
|
27
27
|
"lib/truncate_html.rb",
|
28
|
+
"lib/truncate_html/configuration.rb",
|
29
|
+
"lib/truncate_html/html_string.rb",
|
28
30
|
"lib/truncate_html/html_truncator.rb",
|
29
31
|
"spec/helpers/truncate_html_helper_spec.rb",
|
30
32
|
"spec/spec_helper.rb",
|
33
|
+
"spec/truncate_html/configuration_spec.rb",
|
34
|
+
"spec/truncate_html/html_string_spec.rb",
|
31
35
|
"spec/truncate_html/html_truncator_spec.rb",
|
32
36
|
"tasks/truncate_html_tasks.rake",
|
33
37
|
"truncate_html.gemspec",
|
@@ -41,6 +45,8 @@ Gem::Specification.new do |s|
|
|
41
45
|
s.test_files = [
|
42
46
|
"spec/helpers/truncate_html_helper_spec.rb",
|
43
47
|
"spec/spec_helper.rb",
|
48
|
+
"spec/truncate_html/configuration_spec.rb",
|
49
|
+
"spec/truncate_html/html_string_spec.rb",
|
44
50
|
"spec/truncate_html/html_truncator_spec.rb"
|
45
51
|
]
|
46
52
|
|
@@ -54,3 +60,4 @@ Gem::Specification.new do |s|
|
|
54
60
|
else
|
55
61
|
end
|
56
62
|
end
|
63
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: truncate_html
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hgimenez
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-02-02 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -31,9 +31,13 @@ files:
|
|
31
31
|
- install.rb
|
32
32
|
- lib/app/helpers/truncate_html_helper.rb
|
33
33
|
- lib/truncate_html.rb
|
34
|
+
- lib/truncate_html/configuration.rb
|
35
|
+
- lib/truncate_html/html_string.rb
|
34
36
|
- lib/truncate_html/html_truncator.rb
|
35
37
|
- spec/helpers/truncate_html_helper_spec.rb
|
36
38
|
- spec/spec_helper.rb
|
39
|
+
- spec/truncate_html/configuration_spec.rb
|
40
|
+
- spec/truncate_html/html_string_spec.rb
|
37
41
|
- spec/truncate_html/html_truncator_spec.rb
|
38
42
|
- tasks/truncate_html_tasks.rake
|
39
43
|
- truncate_html.gemspec
|
@@ -69,4 +73,6 @@ summary: Uses an API similar to Rails' truncate helper to truncate HTML and clos
|
|
69
73
|
test_files:
|
70
74
|
- spec/helpers/truncate_html_helper_spec.rb
|
71
75
|
- spec/spec_helper.rb
|
76
|
+
- spec/truncate_html/configuration_spec.rb
|
77
|
+
- spec/truncate_html/html_string_spec.rb
|
72
78
|
- spec/truncate_html/html_truncator_spec.rb
|