butter 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/butter.gemspec +24 -0
- data/lib/butter.rb +132 -0
- data/lib/butter/version.rb +3 -0
- metadata +117 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/butter.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path("../lib/butter/version", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "butter"
|
6
|
+
s.version = Butter::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = "Jonathan Martin"
|
9
|
+
s.email = "me@nybblr.com"
|
10
|
+
s.homepage = "http://rubygems.org/gems/butter"
|
11
|
+
s.summary = "Ever need to shorten some HTML formatted content? Maybe you're trying to show snippets from last week's blog post, or get tired of really long comments on your blog. This gem gives you access to a simple truncation method that uses Nokogiri to truncate in an HTML friendly manner."
|
12
|
+
s.description = "Easy HTML string truncation, done with proper markup in mind."
|
13
|
+
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "butter"
|
16
|
+
|
17
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
18
|
+
s.add_dependency "htmlentities"
|
19
|
+
s.add_dependency "nokogiri"
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
23
|
+
s.require_path = 'lib'
|
24
|
+
end
|
data/lib/butter.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'htmlentities'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Butter
|
5
|
+
# == Info ===========================================================
|
6
|
+
# This gem was developed by Jonathan Martin for use on his personal blog, nybblr.com; the original source was coded by Eleo from https://gist.github.com/101410. My intent was to take his excellent implementation and clean it up a bit for Rails usage with more options and extension as a string method. Thanks Eleo!
|
7
|
+
|
8
|
+
# == Usage ==========================================================
|
9
|
+
# "<p>An HTML <i>string</i></p>".truncate_html 2, :tail => "..."
|
10
|
+
# => "<p>An HTML...</p>"
|
11
|
+
#
|
12
|
+
# "<p>An HTML <i>string</i></p>".truncate_html 2, :tail => " →"
|
13
|
+
# => "<p>An HTML →</p>"
|
14
|
+
#
|
15
|
+
# "<p>An HTML <i>string</i></p>".truncate_html 2, :strip_html => true
|
16
|
+
# => "An HTML..."
|
17
|
+
|
18
|
+
def truncate_html(num_words = 30, opts = {})
|
19
|
+
opts = { :word_cut => true, :tail => "…", :strip_html => false }.merge(opts)
|
20
|
+
tail = HTMLEntities.new.decode opts[:tail]
|
21
|
+
|
22
|
+
doc = Nokogiri::HTML(self)
|
23
|
+
|
24
|
+
current = doc.children.first
|
25
|
+
count = 0
|
26
|
+
|
27
|
+
while true
|
28
|
+
# we found a text node
|
29
|
+
if current.is_a?(Nokogiri::XML::Text)
|
30
|
+
count += current.text.split.length
|
31
|
+
# we reached our limit, let's get outta here!
|
32
|
+
break if count > num_words
|
33
|
+
previous = current
|
34
|
+
end
|
35
|
+
|
36
|
+
if current.children.length > 0
|
37
|
+
# this node has children, can't be a text node,
|
38
|
+
# lets descend and look for text nodes
|
39
|
+
current = current.children.first
|
40
|
+
elsif !current.next.nil?
|
41
|
+
#this has no children, but has a sibling, let's check it out
|
42
|
+
current = current.next
|
43
|
+
else
|
44
|
+
# we are the last child, we need to ascend until we are
|
45
|
+
# either done or find a sibling to continue on to
|
46
|
+
n = current
|
47
|
+
while !n.is_a?(Nokogiri::HTML::Document) and n.parent.next.nil?
|
48
|
+
n = n.parent
|
49
|
+
end
|
50
|
+
|
51
|
+
# we've reached the top and found no more text nodes, break
|
52
|
+
if n.is_a?(Nokogiri::HTML::Document)
|
53
|
+
break;
|
54
|
+
else
|
55
|
+
current = n.parent.next
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if count >= num_words
|
61
|
+
unless count == num_words
|
62
|
+
new_content = current.text.split
|
63
|
+
|
64
|
+
# If we're here, the last text node we counted eclipsed the number of words
|
65
|
+
# that we want, so we need to cut down on words. The easiest way to think about
|
66
|
+
# this is that without this node we'd have fewer words than the limit, so all
|
67
|
+
# the previous words plus a limited number of words from this node are needed.
|
68
|
+
# We simply need to figure out how many words are needed and grab that many.
|
69
|
+
# Then we need to -subtract- an index, because the first word would be index zero.
|
70
|
+
|
71
|
+
# For example, given:
|
72
|
+
# <p>Testing this HTML truncater.</p><p>To see if its working.</p>
|
73
|
+
# Let's say I want 6 words. The correct returned string would be:
|
74
|
+
# <p>Testing this HTML truncater.</p><p>To see...</p>
|
75
|
+
# All the words in both paragraphs = 9
|
76
|
+
# The last paragraph is the one that breaks the limit. How many words would we
|
77
|
+
# have without it? 4. But we want up to 6, so we might as well get that many.
|
78
|
+
# 6 - 4 = 2, so we get 2 words from this node, but words #1-2 are indices #0-1, so
|
79
|
+
# we subtract 1. If this gives us -1, we want nothing from this node. So go back to
|
80
|
+
# the previous node instead.
|
81
|
+
index = num_words-(count-new_content.length)-1
|
82
|
+
if index >= 0
|
83
|
+
new_content = new_content[0..index]
|
84
|
+
current.content = new_content.join(' ') + tail
|
85
|
+
else
|
86
|
+
current = previous
|
87
|
+
current.content = current.content + tail
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# remove everything else
|
92
|
+
while !current.is_a?(Nokogiri::HTML::Document)
|
93
|
+
while !current.next.nil?
|
94
|
+
current.next.remove
|
95
|
+
end
|
96
|
+
current = current.parent
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# now we grab the html and not the text.
|
101
|
+
# we do first because nokogiri adds html and body tags
|
102
|
+
# which we don't want
|
103
|
+
|
104
|
+
# Strip out the unwanted <p> tag that gets added, if it is present. This is mostly for the sake of markup, since extra <p> tags can throw off styling. In the future, this will need to see if the original code was already wrapped in a plain <p> tag.
|
105
|
+
|
106
|
+
root = doc.root.children.first
|
107
|
+
only_child = root.children.first
|
108
|
+
|
109
|
+
if root.children.size == 1 and only_child.name == "p" and only_child.attributes.empty?
|
110
|
+
truncated = only_child
|
111
|
+
else
|
112
|
+
truncated = root
|
113
|
+
end
|
114
|
+
|
115
|
+
if opts[:strip_html] == true
|
116
|
+
truncated = truncated.inner_text
|
117
|
+
else
|
118
|
+
truncated = truncated.inner_html
|
119
|
+
end
|
120
|
+
|
121
|
+
if html_safe?
|
122
|
+
truncated.html_safe
|
123
|
+
else
|
124
|
+
truncated
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Add method to String class
|
130
|
+
class String
|
131
|
+
include Butter
|
132
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: butter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jonathan Martin
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-26 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: bundler
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 0
|
34
|
+
version: 1.0.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: htmlentities
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: nokogiri
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
type: :runtime
|
64
|
+
version_requirements: *id003
|
65
|
+
description: Easy HTML string truncation, done with proper markup in mind.
|
66
|
+
email: me@nybblr.com
|
67
|
+
executables: []
|
68
|
+
|
69
|
+
extensions: []
|
70
|
+
|
71
|
+
extra_rdoc_files: []
|
72
|
+
|
73
|
+
files:
|
74
|
+
- .gitignore
|
75
|
+
- Gemfile
|
76
|
+
- Rakefile
|
77
|
+
- butter.gemspec
|
78
|
+
- lib/butter.rb
|
79
|
+
- lib/butter/version.rb
|
80
|
+
has_rdoc: true
|
81
|
+
homepage: http://rubygems.org/gems/butter
|
82
|
+
licenses: []
|
83
|
+
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 3
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 23
|
104
|
+
segments:
|
105
|
+
- 1
|
106
|
+
- 3
|
107
|
+
- 6
|
108
|
+
version: 1.3.6
|
109
|
+
requirements: []
|
110
|
+
|
111
|
+
rubyforge_project: butter
|
112
|
+
rubygems_version: 1.3.7
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: Ever need to shorten some HTML formatted content? Maybe you're trying to show snippets from last week's blog post, or get tired of really long comments on your blog. This gem gives you access to a simple truncation method that uses Nokogiri to truncate in an HTML friendly manner.
|
116
|
+
test_files: []
|
117
|
+
|