truncate_html 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +54 -0
- data/Rakefile +35 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/app/helpers/truncate_html_helper.rb +7 -0
- data/lib/truncate_html.rb +10 -0
- data/lib/truncate_html/html_truncator.rb +76 -0
- data/spec/helpers/truncate_html_helper_spec.rb +88 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/truncate_html/html_truncator_spec.rb +71 -0
- data/tasks/truncate_html_tasks.rake +4 -0
- data/truncate_html.gemspec +51 -0
- data/uninstall.rb +1 -0
- metadata +70 -0
data/README.markdown
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
TruncateHtml
|
2
|
+
============
|
3
|
+
|
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
|
+
|
6
|
+
The API is very similar to Rails' own truncate method.
|
7
|
+
|
8
|
+
|
9
|
+
Example
|
10
|
+
-------
|
11
|
+
|
12
|
+
some_html = '<ul><li><a href="http://whatever">This is a link</a></li></ul>'
|
13
|
+
|
14
|
+
truncate_html(some_html, :length => 5, :omission => '...(continued)')
|
15
|
+
|
16
|
+
=> <ul><li><a href="http://whatever">This is...(continued)</a></li></ul>
|
17
|
+
|
18
|
+
|
19
|
+
A few notes:
|
20
|
+
|
21
|
+
* It will truncate on a word boundary.
|
22
|
+
* The default options are:
|
23
|
+
* :length => 100
|
24
|
+
* :omission => '...'
|
25
|
+
|
26
|
+
Installation
|
27
|
+
------------
|
28
|
+
|
29
|
+
#### As a gem
|
30
|
+
Add this to your <code>config/environment.rb</code>:
|
31
|
+
|
32
|
+
config.gem 'hgimenez-truncate_html',
|
33
|
+
:source => 'http://gems.github.com',
|
34
|
+
:lib => 'truncate_html'
|
35
|
+
|
36
|
+
Then either
|
37
|
+
<code>sudo gem install hgimenez-truncate_html</code>
|
38
|
+
or
|
39
|
+
<code>sudo rake gems:install</code>
|
40
|
+
|
41
|
+
#### As a plugin:
|
42
|
+
<code>script/plugin install git://github.com/hgimenez/truncate_html.git</code>
|
43
|
+
|
44
|
+
Issues
|
45
|
+
------
|
46
|
+
|
47
|
+
Found an issue? Please report it on [Github's issue tracker](http://github.com/hgimenez/truncate_html/issues).
|
48
|
+
|
49
|
+
Testing
|
50
|
+
-------
|
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.
|
53
|
+
|
54
|
+
Copyright (c) 2009 Harold A. Giménez, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
desc 'Default: run specs.'
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
desc 'Run the specs'
|
8
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
9
|
+
t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
|
10
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
11
|
+
end
|
12
|
+
|
13
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
14
|
+
spec.libs << 'lib' << 'spec'
|
15
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
16
|
+
spec.rcov = true
|
17
|
+
end
|
18
|
+
|
19
|
+
begin
|
20
|
+
require 'jeweler'
|
21
|
+
Jeweler::Tasks.new do |gem|
|
22
|
+
gem.name = "truncate_html"
|
23
|
+
gem.summary = %Q{Uses an API similar to Rails' truncate helper to truncate HTML and close any lingering open tags.}
|
24
|
+
gem.description = %Q{Truncates html so you don't have to}
|
25
|
+
gem.email = "harold.gimenez@gmail.com"
|
26
|
+
gem.homepage = "http://github.com/hgimenez/truncate_html"
|
27
|
+
gem.authors = ["hgimenez"]
|
28
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
29
|
+
end
|
30
|
+
|
31
|
+
rescue LoadError
|
32
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
33
|
+
end
|
34
|
+
|
35
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'truncate_html'
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,10 @@
|
|
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
|
+
require File.join(File.dirname(__FILE__), 'truncate_html', 'html_truncator')
|
7
|
+
|
8
|
+
ActionView::Base.class_eval do
|
9
|
+
include TruncateHtmlHelper
|
10
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module TruncateHtml
|
2
|
+
class HtmlTruncator
|
3
|
+
|
4
|
+
UNPAIRED_TAGS = %w(br hr img)
|
5
|
+
|
6
|
+
def initialize(original_html)
|
7
|
+
@original_html = original_html
|
8
|
+
end
|
9
|
+
|
10
|
+
def truncate(options = {})
|
11
|
+
options[:length] ||= 100
|
12
|
+
options[:omission] ||= '...'
|
13
|
+
@chars_remaining = options[:length]
|
14
|
+
@open_tags, result = [], []
|
15
|
+
|
16
|
+
html_tokens.each do |str|
|
17
|
+
if @chars_remaining > 0
|
18
|
+
if html_tag?(str)
|
19
|
+
if open_tag?(str)
|
20
|
+
@open_tags << str
|
21
|
+
else
|
22
|
+
open_tags = remove_latest_open_tag(str)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
@chars_remaining -= str.length
|
26
|
+
end
|
27
|
+
result << str
|
28
|
+
else
|
29
|
+
result[-1] += options[:omission] unless options[:omission].nil?
|
30
|
+
@open_tags.reverse_each do |open_tag|
|
31
|
+
result << matching_close_tag(open_tag)
|
32
|
+
end
|
33
|
+
break
|
34
|
+
end
|
35
|
+
end
|
36
|
+
result.join('')
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
private #############################
|
41
|
+
|
42
|
+
def html_tokens
|
43
|
+
@original_html.scan(/<\/?[^>]+>|[\w\|`~!@#\$%^&*\(\)\-_\+=\[\]{}:;'",\.\/?]+|\s+/).map do
|
44
|
+
|t| t.gsub(
|
45
|
+
#remove newline characters
|
46
|
+
/\n/,''
|
47
|
+
).gsub(
|
48
|
+
#clean out extra consecutive whitespace
|
49
|
+
/\s+/, ' '
|
50
|
+
)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def html_tag?(string)
|
55
|
+
string =~ /<\/?[^>]+>/ ? true : false
|
56
|
+
end
|
57
|
+
|
58
|
+
def open_tag?(html_tag)
|
59
|
+
html_tag =~ /<(?!(?:#{UNPAIRED_TAGS.join('|')}|\/))[^>]+>/i ? true : false
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove_latest_open_tag(close_tag)
|
63
|
+
(0...@open_tags.length).to_a.reverse.each do |i|
|
64
|
+
if matching_close_tag(@open_tags[i]) == close_tag
|
65
|
+
@open_tags.delete_at(i)
|
66
|
+
break
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def matching_close_tag(open_tag)
|
72
|
+
open_tag.gsub(/<(\w+)\s?.*>/, '</\1>').strip
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
include TruncateHtmlHelper
|
4
|
+
|
5
|
+
describe TruncateHtmlHelper do
|
6
|
+
|
7
|
+
it 'should be included in ActionView::Base' do
|
8
|
+
ActionView::Base.included_modules.should include(TruncateHtmlHelper)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#truncate_html' do
|
12
|
+
|
13
|
+
context 'truncating in the middle of a link' do
|
14
|
+
before(:each) do
|
15
|
+
@html = '<div><ul><li>Look at <a href="foo">this</a> link </li></ul></div>'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should truncate, and close the <a>, and close any remaining open tags' do
|
19
|
+
truncate_html(@html, :length => 10).should == '<div><ul><li>Look at <a href="foo">this...</a></li></ul></div>'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
%w(! @ # $ % ^ & * \( \) - _ + = [ ] { } \ | , . / ?).each do |char|
|
24
|
+
context "when the html has a #{char} character after a closing tag" do
|
25
|
+
before(:each) do
|
26
|
+
@html = "<p>Look at <strong>this</strong>#{char} More words here</p>"
|
27
|
+
end
|
28
|
+
it 'should place the punctuation after the tag without any whitespace' do
|
29
|
+
truncate_html(@html, :length => 16).should == "<p>Look at <strong>this</strong>#{char} More...</p>"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when the html has a non punctuation character after a closing tag' do
|
35
|
+
before(:each) do
|
36
|
+
@html = '<p>Look at <a href="awesomeful.net">this</a> link for randomness</p>'
|
37
|
+
end
|
38
|
+
it 'should leave a whitespace between the closing tag and the following word character' do
|
39
|
+
truncate_html(@html, :length => 17).should == '<p>Look at <a href="awesomeful.net">this</a> link...</p>'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
#unusual, but just covering my ass
|
44
|
+
context 'when the HTML tags are multiline' do
|
45
|
+
before(:each) do
|
46
|
+
@html = <<-END_HTML
|
47
|
+
<div id="foo"
|
48
|
+
class="bar">
|
49
|
+
This is ugly html.
|
50
|
+
</div>
|
51
|
+
END_HTML
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should recognize the multiline html properly' do
|
55
|
+
truncate_html(@html, :length => 8).should == ' <div id="foo" class="bar"> This is...</div>'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
%w(br hr img).each do |unpaired_tag|
|
60
|
+
context "when the html contains a #{unpaired_tag} tag" do
|
61
|
+
|
62
|
+
context "and the #{unpaired_tag} does not have the closing slash" do
|
63
|
+
before(:each) do
|
64
|
+
@html = "<div>Some before. <#{unpaired_tag}>and some after</div>"
|
65
|
+
@html_caps = "<div>Some before. <#{unpaired_tag.capitalize}>and some after</div>"
|
66
|
+
end
|
67
|
+
it "should not close the #{unpaired_tag} tag" do
|
68
|
+
truncate_html(@html, :length => 15).should == "<div>Some before. <#{unpaired_tag}>and...</div>"
|
69
|
+
truncate_html(@html_caps, :length => 15).should == "<div>Some before. <#{unpaired_tag.capitalize}>and...</div>"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "and the #{unpaired_tag} does not have the closing slash" do
|
74
|
+
before(:each) do
|
75
|
+
@html = "<div>Some before. <#{unpaired_tag} />and some after</div>"
|
76
|
+
@html_caps = "<div>Some before. <#{unpaired_tag.capitalize} />and some after</div>"
|
77
|
+
end
|
78
|
+
it "should not close the #{unpaired_tag} tag" do
|
79
|
+
truncate_html(@html, :length => 15).should == "<div>Some before. <#{unpaired_tag} />and...</div>"
|
80
|
+
truncate_html(@html_caps, :length => 15).should == "<div>Some before. <#{unpaired_tag.capitalize} />and...</div>"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
begin
|
2
|
+
require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
|
3
|
+
rescue LoadError
|
4
|
+
puts "You need to install rspec in your base app"
|
5
|
+
exit
|
6
|
+
end
|
7
|
+
|
8
|
+
plugin_spec_dir = File.dirname(__FILE__)
|
9
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'app', 'helpers', 'truncate_html_helper')
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe TruncateHtml::HtmlTruncator do
|
4
|
+
|
5
|
+
describe '#html_tokens' do
|
6
|
+
before(:each) do
|
7
|
+
@html = '<h1>Hi there</h1> <p>This is sweet!</p>'
|
8
|
+
@truncator = TruncateHtml::HtmlTruncator.new @html
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should return each token in the string as an array element' do
|
12
|
+
@truncator.send(:html_tokens).should == ['<h1>', 'Hi', ' ', 'there', '</h1>', ' ', '<p>', 'This', ' ', 'is', ' ', 'sweet!', '</p>']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#html_tag?' do
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
@truncator = TruncateHtml::HtmlTruncator.new 'foo'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should be false when the string parameter is not an html tag' do
|
23
|
+
@truncator.send(:html_tag?, 'no tags').should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should be true when the string parameter is an html tag' do
|
27
|
+
@truncator.send(:html_tag?, '<img src="foo">').should be_true
|
28
|
+
@truncator.send(:html_tag?, '</img>').should be_true
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#open_tag?' do
|
34
|
+
|
35
|
+
before(:each) do
|
36
|
+
@truncator = TruncateHtml::HtmlTruncator.new 'foo'
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should be true if the tag is an open tag' do
|
40
|
+
@truncator.send(:open_tag?, '<a>').should be_true
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should be true if the tag is an open tag, and has whitespace and html properties with either single or double quotes' do
|
44
|
+
@truncator.send(:open_tag?, ' <a href="http://awesomeful.net">').should be_true
|
45
|
+
@truncator.send(:open_tag?, " <a href='http://awesomeful.net' >").should be_true
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should be false if the tag is a close tag' do
|
49
|
+
@truncator.send(:open_tag?, '</a>').should be_false
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should be false if the string is not an html tag' do
|
53
|
+
@truncator.send(:open_tag?, 'foo bar').should be_false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#matching_close_tag' do
|
58
|
+
|
59
|
+
before(:each) do
|
60
|
+
@truncator = TruncateHtml::HtmlTruncator.new 'foo'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should close a tag given an open tag' do
|
64
|
+
@truncator.send(:matching_close_tag, '<a>').should == '</a>'
|
65
|
+
@truncator.send(:matching_close_tag, ' <div>').should == '</div>'
|
66
|
+
@truncator.send(:matching_close_tag, '<h1>').should == '</h1>'
|
67
|
+
@truncator.send(:matching_close_tag, '<a href="foo">').should == '</a>'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{truncate_html}
|
5
|
+
s.version = "0.1.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["hgimenez"]
|
9
|
+
s.date = %q{2009-08-25}
|
10
|
+
s.description = %q{Truncates html so you don't have to}
|
11
|
+
s.email = %q{harold.gimenez@gmail.com}
|
12
|
+
s.extra_rdoc_files = [
|
13
|
+
"README.markdown"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
"README.markdown",
|
17
|
+
"Rakefile",
|
18
|
+
"VERSION",
|
19
|
+
"init.rb",
|
20
|
+
"install.rb",
|
21
|
+
"lib/app/helpers/truncate_html_helper.rb",
|
22
|
+
"lib/truncate_html.rb",
|
23
|
+
"lib/truncate_html/html_truncator.rb",
|
24
|
+
"spec/helpers/truncate_html_helper_spec.rb",
|
25
|
+
"spec/spec_helper.rb",
|
26
|
+
"spec/truncate_html/html_truncator_spec.rb",
|
27
|
+
"tasks/truncate_html_tasks.rake",
|
28
|
+
"truncate_html.gemspec",
|
29
|
+
"uninstall.rb"
|
30
|
+
]
|
31
|
+
s.homepage = %q{http://github.com/hgimenez/truncate_html}
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubygems_version = %q{1.3.5}
|
35
|
+
s.summary = %q{Uses an API similar to Rails' truncate helper to truncate HTML and close any lingering open tags.}
|
36
|
+
s.test_files = [
|
37
|
+
"spec/helpers/truncate_html_helper_spec.rb",
|
38
|
+
"spec/spec_helper.rb",
|
39
|
+
"spec/truncate_html/html_truncator_spec.rb"
|
40
|
+
]
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
44
|
+
s.specification_version = 3
|
45
|
+
|
46
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
47
|
+
else
|
48
|
+
end
|
49
|
+
else
|
50
|
+
end
|
51
|
+
end
|
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: truncate_html
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- hgimenez
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-25 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Truncates html so you don't have to
|
17
|
+
email: harold.gimenez@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.markdown
|
24
|
+
files:
|
25
|
+
- README.markdown
|
26
|
+
- Rakefile
|
27
|
+
- VERSION
|
28
|
+
- init.rb
|
29
|
+
- install.rb
|
30
|
+
- lib/app/helpers/truncate_html_helper.rb
|
31
|
+
- lib/truncate_html.rb
|
32
|
+
- lib/truncate_html/html_truncator.rb
|
33
|
+
- spec/helpers/truncate_html_helper_spec.rb
|
34
|
+
- spec/spec_helper.rb
|
35
|
+
- spec/truncate_html/html_truncator_spec.rb
|
36
|
+
- tasks/truncate_html_tasks.rake
|
37
|
+
- truncate_html.gemspec
|
38
|
+
- uninstall.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/hgimenez/truncate_html
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --charset=UTF-8
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.3.5
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Uses an API similar to Rails' truncate helper to truncate HTML and close any lingering open tags.
|
67
|
+
test_files:
|
68
|
+
- spec/helpers/truncate_html_helper_spec.rb
|
69
|
+
- spec/spec_helper.rb
|
70
|
+
- spec/truncate_html/html_truncator_spec.rb
|