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 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,7 @@
1
+ module TruncateHtmlHelper
2
+
3
+ def truncate_html(html, options={})
4
+ TruncateHtml::HtmlTruncator.new(html).truncate(options)
5
+ end
6
+
7
+ end
@@ -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
@@ -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,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :truncate_html do
3
+ # # Task goes here
4
+ # 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