diff --git a/README.md b/README.md index 7f10da8..d709294 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,8 @@ The documentation is best viewed [on the documentation website][doc-website]. * {file:CONTRIBUTING Contributing} * {file:changelog Changelog} * {file:migrating\_from\_nokogiri Migrating From Nokogiri} +* {Oga::XML::Parser XML Parser} +* {Oga::XML::SaxParser XML SAX Parser} ## Native Extension Setup diff --git a/lib/oga/xml/sax_parser.rb b/lib/oga/xml/sax_parser.rb index 47a9574..93377cd 100644 --- a/lib/oga/xml/sax_parser.rb +++ b/lib/oga/xml/sax_parser.rb @@ -37,6 +37,26 @@ module Oga # For information on the callback arguments see the documentation of the # corresponding methods in {Oga::XML::Parser}. # + # ## Element Callbacks + # + # The SAX parser changes the behaviour of both `on_element` and + # `after_element`. The latter in the regular parser only takes a + # {Oga::XML::Element} instance. In the SAX parser it will instead take a + # namespace name and the element name. This eases the process of figuring + # out what element a callback is associated with. + # + # An example: + # + # class SaxHandler + # def on_element(namespace, name, attrs = {}) + # # ... + # end + # + # def after_element(namespace, name) + # puts name # => "foo", "bar", etc + # end + # end + # class SaxParser < Parser ## # @param [Object] handler The SAX handler to delegate callbacks to. @@ -52,12 +72,46 @@ module Oga instance_methods.grep(/^(on_|after_)/).each do |method| eval <<-EOF, nil, __FILE__, __LINE__ + 1 def #{method}(*args) - @handler.#{method}(*args) if @handler.respond_to?(:#{method}) + run_callback(:#{method}, *args) return end EOF end + + ## + # Manually overwrite `on_element` so we can ensure that `after_element` + # always receives the namespace and name. + # + # @see [Oga::XML::Parser#on_element] + # @return [Array] + # + def on_element(namespace, name, attrs = {}) + run_callback(:on_element, namespace, name, attrs) + + return namespace, name + end + + ## + # Manually overwrite `after_element` so it can take a namespace and name. + # This differs a bit from the regular `after_element` which only takes an + # {Oga::XML::Element} instance. + # + # @param [Array] namespace_with_name + # + def after_element(namespace_with_name) + run_callback(:after_element, *namespace_with_name) + end + + private + + ## + # @param [Symbol] method + # @param [Array] args + # + def run_callback(method, *args) + @handler.send(method, *args) if @handler.respond_to?(method) + end end # SaxParser end # XML end # Oga diff --git a/spec/oga/xml/sax_parser_spec.rb b/spec/oga/xml/sax_parser_spec.rb index 03686d0..e91333a 100644 --- a/spec/oga/xml/sax_parser_spec.rb +++ b/spec/oga/xml/sax_parser_spec.rb @@ -3,11 +3,16 @@ require 'spec_helper' describe Oga::XML::SaxParser do before do @handler = Class.new do - attr_reader :name + attr_reader :name, :after_namespace, :after_name def on_element(namespace, name, attrs = {}) @name = name end + + def after_element(namespace, name) + @after_namespace = namespace + @after_name = name + end end end @@ -26,6 +31,16 @@ describe Oga::XML::SaxParser do handler.name.should == 'foo' end + example 'always pass element names to after_element' do + handler = @handler.new + parser = described_class.new(handler, '') + + parser.parse + + handler.after_name.should == 'foo' + handler.after_namespace.should == 'namespace' + end + example 'ignore callbacks that are not defined in the handler' do parser = described_class.new(@handler.new, '')