Skip to content

Instantly share code, notes, and snippets.

@sj26
Last active December 25, 2015 18:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sj26/7018858 to your computer and use it in GitHub Desktop.
Save sj26/7018858 to your computer and use it in GitHub Desktop.
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, "&hellip;")
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