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.
This commit is contained in:
parent
d3951dfaa1
commit
054df415f9
4 changed files with 33 additions and 2 deletions
|
@ -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
|
||||
|
||||
|
|
18
lib/banzai/querying.rb
Normal file
18
lib/banzai/querying.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
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
|
13
spec/lib/banzai/querying_spec.rb
Normal file
13
spec/lib/banzai/querying_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
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
|
Loading…
Reference in a new issue