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 ...@@ -10,7 +10,7 @@ module Banzai
# #
class RedactorFilter < HTML::Pipeline::Filter class RedactorFilter < HTML::Pipeline::Filter
def call def call
doc.css('a.gfm').each do |node| Querying.css(doc, 'a.gfm').each do |node|
unless user_can_see_reference?(node) unless user_can_see_reference?(node)
# The reference should be replaced by the original text, # The reference should be replaced by the original text,
# which is not always the same as the rendered text. # which is not always the same as the rendered text.
......
...@@ -16,7 +16,7 @@ module Banzai ...@@ -16,7 +16,7 @@ module Banzai
end end
def call def call
doc.css('a.gfm').each do |node| Querying.css(doc, 'a.gfm').each do |node|
gather_references(node) gather_references(node)
end 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