Commit 054df415 authored by Yorick Peterse's avatar Yorick Peterse

Optimize CSS expressions produced by Nokogiri

Nokogiri produces inefficient XPath expressions when given CSS
expressions such as "a.gfm". Luckily these expressions can be optimized
quite easily while still achieving the same results.

In the two cases where this optimization is applied the run time has
been reduced from around 170 ms to around 15 ms.
parent d3951dfa
......@@ -10,7 +10,7 @@ module Banzai
#
class RedactorFilter < HTML::Pipeline::Filter
def call
doc.css('a.gfm').each do |node|
Querying.css(doc, 'a.gfm').each do |node|
unless user_can_see_reference?(node)
# The reference should be replaced by the original text,
# which is not always the same as the rendered text.
......
......@@ -16,7 +16,7 @@ module Banzai
end
def call
doc.css('a.gfm').each do |node|
Querying.css(doc, 'a.gfm').each do |node|
gather_references(node)
end
......
module Banzai
module Querying
# Searches a Nokogiri document using a CSS query, optionally optimizing it
# whenever possible.
#
# document - A document/element to search.
# query - The CSS query to use.
#
# Returns a Nokogiri::XML::NodeSet.
def self.css(document, query)
# When using "a.foo" Nokogiri compiles this to "//a[...]" but
# "descendant::a[...]" is quite a bit faster and achieves the same result.
xpath = Nokogiri::CSS.xpath_for(query)[0].gsub(%r{^//}, 'descendant::')
document.xpath(xpath)
end
end
end
require 'spec_helper'
describe Banzai::Querying do
describe '.css' do
it 'optimizes queries for elements with classes' do
document = double(:document)
expect(document).to receive(:xpath).with(/^descendant::a/)
described_class.css(document, 'a.gfm')
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment