Fast HTML helpers using Nokogiri—pick and choose what you want
Gem::Specification.new do |s| | |
s.name = 'nokogiri_html_helpers' | |
s.version = '0.1.4' | |
s.platform = Gem::Platform::RUBY | |
s.author = 'Samuel Cochran' | |
s.email = 'sj26@sj26.com' | |
s.homepage = 'https://gist.github.com/sj26/7018858' | |
s.summary = 'Fast HTML helpers using Nokogiri' | |
s.description = s.summary | |
s.license = 'MIT' | |
s.files = ['nokogiri_html_helpers.rb', 'strip_empty_block_html_helper.rb', 'truncate_html_helper.rb'] | |
s.test_files = ['strip_empty_block_html_helper_spec.rb', 'truncate_html_helper_spec.rb'] | |
s.require_path = '.' | |
s.add_dependency('nokogiri', ["~> 1.6.0"]) | |
s.add_development_dependency('rspec', ["~> 2.0"]) | |
end |
module NokogiriHtmlHelpers | |
class Engine < Rails::Engine | |
def find_root_with_flag(*); File.expand_path("..", __FILE__); end | |
self.paths["app/helpers"] = "" | |
end if defined? Rails | |
end |
module StripEmptyBlockHtmlHelper | |
def strip_empty_block_html(html) | |
Nokogiri::HTML::DocumentFragment.parse(html).tap do |fragment| | |
fragment.traverse do |node| | |
if node.element? and node.description and node.description.block? and node.content.empty? | |
node.remove | |
end | |
end | |
end.to_html | |
end | |
end |
require "./strip_empty_block_html_helper" | |
describe StripEmptyBlockHtmlHelper do | |
include StripEmptyBlockHtmlHelper | |
specify "It should remove empty block-level elements" do | |
strip_empty_block_html("<p>Thing</p><p><span><!-- img was here --></span></p><p>Stuff</p>").should == "<p>Thing</p><p>Stuff</p>" | |
end | |
end |
require "nokogiri" | |
module TruncateHtmlHelper | |
def truncate_html(html, options={}, &block) | |
if html | |
length = options.fetch(:length, 30) | |
omission = options.fetch(:omission, "…") | |
omission &&= Nokogiri::HTML::DocumentFragment.parse(omission) | |
remaining = length | |
remaining -= omission.content.length if omission | |
fragment = Nokogiri::HTML::DocumentFragment.parse(html) | |
fragment.traverse do |node| | |
if node.text? or node.cdata? | |
remaining -= node.content.length | |
if remaining < 0 | |
node.content = node.content[0...remaining] | |
end | |
if node.content.empty? | |
node.remove | |
end | |
elsif node.element? and remaining <= 0 and not node.children.any? | |
node.remove | |
end | |
end | |
# If there's a block, use it instead of the omission | |
if block_given? && remaining < 0 | |
omission = Nokogiri::HTML::DocumentFragment.parse(capture(&block)) | |
end | |
# Add the omission only if we have truncated | |
if omission && remaining < 0 | |
last_child = fragment.children.last | |
# If the last element is block, level, tuck the omission inside | |
if last_child.element? and last_child.description and last_child.description.block? | |
last_child << omission | |
else | |
fragment << omission | |
end | |
end | |
fragment.to_html | |
end | |
end | |
end |
require "./truncate_html_helper" | |
describe TruncateHtmlHelper do | |
include TruncateHtmlHelper | |
specify "basic truncation inside block-level element" do | |
truncate_html("<p>A <strong>thing</strong> which gets truncated.</p><p>With trailing elements</p>", length: 8).should == "<p>A <strong>thing</strong>…</p>" | |
end | |
specify "empty elements are preserved, but danglers are removed" do | |
truncate_html("<p>And<br>line<br>breaks<br>are<br>preserved.</p>", length: 8).should == "<p>And<br>line…</p>" | |
end | |
specify "omissions are outside inline elements" do | |
truncate_html("<strong>Thing</strong> and <em>stuff</em>", length: 6).should == "<strong>Thing</strong>…" | |
end | |
specify "default length is 30" do | |
truncate_html("a" * 50).should == "a" * 29 << "…" | |
end | |
specify "alternate omission text" do | |
truncate_html("<div>a<em>b</em>cdef</div>", length: 6, omission: "...").should == "<div>a<em>b</em>c...</div>" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment