Compare commits

..

No commits in common. "master" and "v2.15" have entirely different histories.

36 changed files with 54 additions and 279 deletions

View File

@ -3,7 +3,7 @@
before_script:
- apk add --update ragel build-base
- if [ "$INSTALL_OPENJDK" == "true" ]; then apk add openjdk8; fi
- gem install bundler --no-document
- gem install bundler --no-ri --no-rdoc
- ruby --version
- gem --version
- bundle --version
@ -14,6 +14,10 @@
paths:
- vendor/ruby
Ruby 2.2:
image: "ruby:2.2-alpine"
<<: *defaults
Ruby 2.3:
image: "ruby:2.3-alpine"
<<: *defaults
@ -22,18 +26,6 @@ Ruby 2.4:
image: "ruby:2.4-alpine"
<<: *defaults
Ruby 2.5:
image: "ruby:2.5-alpine"
<<: *defaults
Ruby 2.6:
image: "ruby:2.6-alpine"
<<: *defaults
Ruby 2.7:
image: "ruby:2.7-alpine"
<<: *defaults
JRuby 9.1:
image: "jruby:9.1-alpine"
variables:

View File

@ -3,56 +3,6 @@
This document contains details of the various releases and their release dates.
Dates are in the format `yyyy-mm-dd`.
## 3.4 - 2022-08-02
This release includes a change that when setting the child nodes of a node A,
node A takes ownership over the entire new child tree. See merge request
https://gitlab.com/yorickpeterse/oga/-/merge_requests/194 for more details.
## 3.3 - 2020-07-27
This release adds `to_s` as an alias for `to_xml`, thanks to Roy Zwambag. See
merge request https://gitlab.com/yorickpeterse/oga/-/merge_requests/192 for more
information.
## 3.2 - 2020-01-10
This release fixes a few warnings that would show up when using Oga on Ruby
2.7.0. See https://gitlab.com/yorickpeterse/oga/merge_requests/190 for more
information.
## 3.1 - 2020-01-08
This release fixes a bug in the XML lexer that prevented the parsing of doctypes
using "public" or "system" instead of "PUBLIC"/"SYSTEM". See issue
<https://gitlab.com/yorickpeterse/oga/issues/199> for more information.
## 3.0 - 2019-12-03
This release bumps the Ruby version requirement to Ruby 2.3.0, as we haven't
supported older versions for several years now. We also no longer officially
support Rubinius.
## 2.17 - 2019-12-02
Elements using the default XML namespace can now be queried using XPath queries,
which was broken for quite a while.
See commit <https://gitlab.com/yorickpeterse/oga/commit/95da93949bf613612981f5cd7decc0d2c2a60e15>
for more information.
## 2.16 - 2019-11-29
* XPath namespace aliases can now be used when querying elements using XPath
expressions.
* Several RDOc and RubyGems deprecation warnings have been resolved.
See the following commits for more information:
* <https://gitlab.com/yorickpeterse/oga/commit/d9e7346b60c3afa2b3e83a240f9807c6bb819d48>
* <https://gitlab.com/yorickpeterse/oga/commit/da9721cb34f91527e72b096c6bd6a128e37b1992>
* <https://gitlab.com/yorickpeterse/oga/commit/977bd594c8bfd1a29aeba9d3a4ab7d0ebbc7d11a>
## 2.15 - 2018-04-11
The HTML parser now allows `th` elements to occur in `thead`, `tbody`, and

View File

@ -28,7 +28,7 @@ When making changes please stick to the existing style and patterns as this
keeps the codebase consistent. If a certain pattern or style is getting in your
way please open a separate issue about this so it can be discussed.
Every commit and every merge request made is carefully reviewed. Chances are I'll
Every commit and every pull request made is carefully reviewed. Chances are I'll
spend more time reviewing it than the time an author spent on their changes.
This should ensure that Oga's codebase is stable, of high quality and easy to
maintain. As such _please_ take my feedback into consideration (or discuss it in
@ -36,18 +36,18 @@ a civilized manner) instead of just dismissing it with comments such as "But I
fixed the problem so your feedback is irrelevant" or "This is my way of doing
things".
Finally, and this will sound harsh: I will _not_ merge merge requests if the
Finally, and this will sound harsh: I will _not_ merge pull requests if the
author(s) simply disregard the feedback I've given them or if there are other
problems with the merge request. Do not expect me to just blindly accept whatever
problems with the pull request. Do not expect me to just blindly accept whatever
changes are submitted.
Some examples of good merge requests:
Some examples of good pull request:
* https://gitlab.com/yorickpeterse/oga/-/merge_requests/96
* https://gitlab.com/yorickpeterse/oga/-/merge_requests/67
* https://gitlab.com/yorickpeterse/ffi-aspell/-/merge_requests/21
* https://gitlab.com/yorickpeterse/ffi-aspell/-/merge_requests/20
* https://gitlab.com/yorickpeterse/ruby-ll/-/merge_requests/16
* https://gitlab.com/yorickpeterse/oga/pull/96
* https://gitlab.com/yorickpeterse/oga/pull/67
* https://gitlab.com/yorickpeterse/ffi-aspell/pull/21
* https://gitlab.com/yorickpeterse/ffi-aspell/pull/20
* https://gitlab.com/yorickpeterse/ruby-ll/pull/16
## Git
@ -140,14 +140,19 @@ such a case use `describe 'foo'` for class methods and `describe '#foo'` for
instance methods.
Whenever adding new specifications please keep them in the existing style. If
the style is problematic you can open a separate merge request to address it. If
the style is problematic you can open a separate pull request to address it. If
you expect this to be a lot of work you should open an issue first to discuss
things.
## Continuous Integration
Oga is tested using GitLab CI. Merge requests require that all tests pass before
they can be merged.
Two continuous integration services are used to ensure the tests of Oga pass
at all times:
* Travis CI: <https://gitlab.com/yorickpeterse/oga/pipelines>
* AppVeyor (Windows): <https://ci.appveyor.com/project/yorickpeterse/oga>
Pull requests won't be merged if any of the builds fail unless stated otherwise.
## Extension Setup
@ -214,7 +219,7 @@ modify `$LOAD_PATH`, instead run any scripts using `ruby -I lib`.
In case you have any further questions or would like to receive feedback before
submitting a change, feel free to contact me. You can either open an issue,
send a tweet to [@yorickpeterse][twitter] or send an Email to
<yorick@yorickpeterse.com>.
<yorickpeterse@gmail.com>.
[editorconfig]:http://editorconfig.org/
[twitter]: https://twitter.com/yorickpeterse

View File

@ -173,8 +173,8 @@ Querying a document using a namespace:
| Ruby | Required | Recommended |
|:---------|:--------------|:------------|
| MRI | >= 1.9.3 | >= 2.1.2 |
| Rubinius | >= 2.2 | >= 2.2.10 |
| JRuby | >= 1.7 | >= 1.7.12 |
| Rubinius | Not supported | |
| Maglev | Not supported | |
| Topaz | Not supported | |
| mruby | Not supported | |
@ -227,14 +227,15 @@ And if you want to specify an explicit namespace URI, you can use this:
descendant::*[local-name() = "bar" and namespace-uri() = "http://example.com"]
Like Nokogiri, Oga provides a way to create "dynamic" namespaces.
That is, Oga allows one to query the above document as following:
Unlike Nokogiri, Oga does _not_ provide a way to create "dynamic" namespaces.
That is, Nokogiri allows one to query the above document as following:
document = Oga.parse_xml('<root xmlns="http://example.com"><bar>bar</bar></root>')
document = Nokogiri::XML('<root xmlns="http://example.com"><bar>bar</bar></root>')
document.xpath('x:root/x:bar', namespaces: {'x' => 'http://example.com'})
document.xpath('x:root/x:bar', :x => 'http://example.com')
Moreover, because Oga assigns the name "xmlns" to default namespaces you can use
Oga does have a small trick you can use to cut down the size of your XPath
queries. Because Oga assigns the name "xmlns" to default namespaces you can use
this in your XPath queries:
document = Oga.parse_xml('<root xmlns="http://example.com"><bar>bar</bar></root>')
@ -245,6 +246,9 @@ When using this you can still restrict the query to the correct namespace URI:
document.xpath('xmlns:root[namespace-uri() = "http://example.com"]/xmlns:bar')
In the future I might add an API to ease this process, although at this time I
have little interest in providing an API similar to Nokogiri.
## HTML5 Support
Oga fully supports HTML5 including the omission of certain tags. For example,

View File

@ -1 +0,0 @@
241e4861fb8cdb8576b72672a2ad1d59e0f72333eb203d19b8922e15091a3470d0150d417f78d2394e2c9140fa7c9d87508acc51907537f57813b8c23272922e

View File

@ -1 +0,0 @@
5a2abc35e0696adf408f1d517865e49d511b26e39c0fe6a1f299baf77563327661498f3e1d70e20feb118810eb6457649706dc4fc3e8c45868d4b3d0ef56bfc8

View File

@ -1 +0,0 @@
4e35c653ef64ebfbbee7a933923e9cf53e988028e53cb1535127962d249501881ce35e5c7375b98e43e220b7561961a9d35fe15caf20b263b20367660a59b3eb

View File

@ -1 +0,0 @@
9cbb14e1abea3ebec3b7e9051bff5cae466cc4e608df6aa7826add38bcdb5b406cc8090405e63128a6902b24a64082ef5b9d1a36970c399ef4c941f63f2ee305

View File

@ -1 +0,0 @@
b64906a38edefb346c2ba9770336cb69f424e0776690932fea524f014dda00d8fe1b13b69fff1f01ca75c8f5107b92056c25f8ae7ee4aeb83770dc03b5d482c0

View File

@ -1 +0,0 @@
56eea6f76968afb2916e73d729a9c94dcacfb1cccc6fa0ef27888e6e8006a80cda9279db4b040be81b33ee354916e49e437ce0189ce79bdbab7c0f54203b9f2e

View File

@ -1 +0,0 @@
a2246547f87d1901e280d9df915bf41a6b78ac14805c5c0f471f5dd1cf617f1e5b3e4aa05e58ae5aa816e188456ccd3638e44a7aba41ed2dfb942f509b2093af

View File

@ -1 +0,0 @@
deb03862d5263b2cb47169267aa37c41be82fcb01d048d840506d2c8924fb0bba6a4407401052e8a5f42d053c2bb3410701316fe21570b35a2981850dc05a481

View File

@ -1 +0,0 @@
b0c3740f08d33f5b9a76c6532de749b8004fb591e3bd3b745e8c57a3bbc5b3d6be4c9a02432fc32f4ae3ca53b488dc409243af91e43646ba3f486fec4738911e

View File

@ -1 +0,0 @@
27f941862134b9e5fc46b33d8dd642a7c816f8b0bb6448789c5f23e9da42abdc72b19713985cf81257074931a4f51e014aa551e09bcb7858cde9987ea17aca75

View File

@ -1 +0,0 @@
c5521d5bc9e025fadfb4e0719c8ff0fa103dd7c184cdf4c60154a1bb5f7d71c9d807a84e76a21b13665de1bbe54f9ba23f6e479650b1bd497302f86ff2af8bbf

View File

@ -1 +0,0 @@
be44f4fb2f5f821306556b965a928e42753a57e489516654bdda74662058510cdc9885b50f6170f23762309f3a7c94791d8db71180272961341c917ffc3560e4

View File

@ -1 +0,0 @@
eef134163a86451be4a5ec72b262fec6a1dad10613e0d4002142b09e02cb444cc25ce018cdd62a870b266fc8dd390ba4fe110e07a2c41f50a3d8abdcc69b5dec

View File

@ -1 +0,0 @@
2ba0fdbfa3fa15b8d1ce5df4df4cfb3813f34399c93517f98ec8da1e82ff3cdc6e3543bf017b8246daa8b2521a64af92a9438a404b69da0f319272c510961314

View File

@ -289,7 +289,7 @@
# Machine for processing doctypes. Doctype values such as the public
# and system IDs are treated as T_STRING tokens.
doctype := |*
'PUBLIC'i | 'SYSTEM'i => {
'PUBLIC' | 'SYSTEM' => {
callback(id_on_doctype_type, data, encoding, ts, te);
};

View File

@ -1,3 +1,3 @@
module Oga
VERSION = '3.4'
VERSION = '2.15'
end # Oga

View File

@ -46,8 +46,6 @@ module Oga
# @param [Oga::XML::NodeSet|Array] nodes
def children=(nodes)
if nodes.is_a?(NodeSet)
nodes.owner = self
nodes.take_ownership_on_nodes
@children = nodes
else
@children = NodeSet.new(nodes, self)

View File

@ -49,8 +49,6 @@ module Oga
# @param [Oga::XML::NodeSet|Array] nodes
def children=(nodes)
if nodes.is_a?(NodeSet)
nodes.owner = self
nodes.take_ownership_on_nodes
@children = nodes
else
@children = NodeSet.new(nodes, self)

View File

@ -42,7 +42,11 @@ module Oga
@owner = owner
@existing = {}
take_ownership_on_nodes
@nodes.each_with_index do |node, index|
mark_existing(node)
take_ownership(node, index) if @owner
end
end
# Yields the supplied block for every node.
@ -285,14 +289,6 @@ module Oga
"NodeSet(#{values})"
end
def take_ownership_on_nodes
@nodes.each_with_index do |node, index|
mark_existing(node)
take_ownership(node, index) if @owner
end
end
private
# Takes ownership of the given node. This only occurs when the current

View File

@ -10,7 +10,6 @@ module Oga
# document = Oga.parse_xml <<-EOF
# <people>
# <person age="25">Alice</person>
# <ns:person xmlns:ns="http://example.net">Bob</ns:person>
# </people>
# EOF
#
@ -26,23 +25,15 @@ module Oga
#
# document.xpath('people/person[@age = $age]', 'age' => 25)
#
# Using namespace aliases:
#
# namespaces = {'example' => 'http://example.net'}
# document.xpath('people/example:person', namespaces: namespaces)
#
# @param [String] expression The XPath expression to run.
#
# @param [Hash] variables Variables to bind. The keys of this Hash should
# be String values.
#
# @param [Hash] namespaces Namespace aliases. The keys of this Hash should
# be String values.
#
# @return [Oga::XML::NodeSet]
def xpath(expression, variables = {}, namespaces: nil)
def xpath(expression, variables = {})
ast = XPath::Parser.parse_with_cache(expression)
block = XPath::Compiler.compile_with_cache(ast, namespaces: namespaces)
block = XPath::Compiler.compile_with_cache(ast)
block.call(self, variables)
end
@ -63,8 +54,8 @@ module Oga
#
# @see [#xpath]
# @return [Oga::XML::Node|Oga::XML::Attribute]
def at_xpath(*args, namespaces: nil)
result = xpath(*args, namespaces: namespaces)
def at_xpath(*args)
result = xpath(*args)
result.is_a?(XML::NodeSet) ? result.first : result
end

View File

@ -7,8 +7,6 @@ module Oga
def to_xml
Generator.new(self).to_xml
end
alias_method :to_s, :to_xml
end
end
end

View File

@ -42,16 +42,12 @@ module Oga
# Compiles and caches an AST.
#
# @see [#compile]
def self.compile_with_cache(ast, namespaces: nil)
cache_key = namespaces ? [ast, namespaces] : ast
CACHE.get_or_set(cache_key) { new(namespaces: namespaces).compile(ast) }
def self.compile_with_cache(ast)
CACHE.get_or_set(ast) { new.compile(ast) }
end
# @param [Hash] namespaces
def initialize(namespaces: nil)
def initialize
reset
@namespaces = namespaces
end
# Resets the internal state.
@ -1389,23 +1385,7 @@ module Oga
end
if ns and ns != STAR
if @namespaces
ns_uri = @namespaces[ns]
ns_match =
if ns_uri
input.namespace.and(input.namespace.uri.eq(string(ns_uri)))
else
self.false
end
else
ns_match =
if ns == XML::Element::XMLNS_PREFIX
input
else
input.namespace_name.eq(string(ns))
end
end
ns_match = input.namespace_name.eq(string(ns))
condition = condition ? condition.and(ns_match) : ns_match
end

Binary file not shown.

View File

@ -29,6 +29,7 @@ Gem::Specification.new do |s|
s.extensions = ['ext/c/extconf.rb']
end
s.has_rdoc = 'yard'
s.required_ruby_version = '>= 1.9.3'
s.add_dependency 'ast'

View File

@ -22,7 +22,6 @@ describe Oga::XML::Document do
document.children = [child]
expect(document.children[0]).to eq(child)
expect(document.children[0].parent).to eq(document)
end
it 'sets the child nodes using a NodeSet' do
@ -32,7 +31,6 @@ describe Oga::XML::Document do
document.children = Oga::XML::NodeSet.new([child])
expect(document.children[0]).to eq(child)
expect(document.children[0].parent).to eq(document)
end
end

View File

@ -168,16 +168,5 @@ describe Oga::XML::Generator do
end
end
end
describe 'using an Element with replaced children' do
it 'returns a string' do
element = Oga::XML::Element.new(name: 'foo')
element.children = Oga::XML::Parser.new('<bar></bar>').parse.children
output = described_class.new(element).to_xml
expect(output).to eq('<foo><bar /></foo>')
end
end
end
end

View File

@ -108,7 +108,7 @@ describe Oga::XML::Lexer do
# Technically not valid, put in place to make sure that the Ragel rules are
# not too greedy.
it 'lexes an inline doctype followed by a system ID' do
it 'lexes an inline doftype followed by a system ID' do
expect(lex('<!DOCTYPE html [<!ELEMENT foo>] "foo">')).to eq([
[:T_DOCTYPE_START, nil, 1],
[:T_DOCTYPE_NAME, 'html', 1],
@ -119,31 +119,5 @@ describe Oga::XML::Lexer do
[:T_DOCTYPE_END, nil, 1]
])
end
it 'does not care about the casing when using a public doctype' do
expect(lex('<!DoCtYpE HtMl PuBlIc [<!ELEMENT foo>] "foo">')).to eq([
[:T_DOCTYPE_START, nil, 1],
[:T_DOCTYPE_NAME, 'HtMl', 1],
[:T_DOCTYPE_TYPE, 'PuBlIc', 1],
[:T_DOCTYPE_INLINE, '<!ELEMENT foo>', 1],
[:T_STRING_DQUOTE, nil, 1],
[:T_STRING_BODY, 'foo', 1],
[:T_STRING_DQUOTE, nil, 1],
[:T_DOCTYPE_END, nil, 1]
])
end
it 'does not care about the casing when using a system doctype' do
expect(lex('<!DoCtYpE HtMl SyStEm [<!ELEMENT foo>] "foo">')).to eq([
[:T_DOCTYPE_START, nil, 1],
[:T_DOCTYPE_NAME, 'HtMl', 1],
[:T_DOCTYPE_TYPE, 'SyStEm', 1],
[:T_DOCTYPE_INLINE, '<!ELEMENT foo>', 1],
[:T_STRING_DQUOTE, nil, 1],
[:T_STRING_BODY, 'foo', 1],
[:T_STRING_DQUOTE, nil, 1],
[:T_DOCTYPE_END, nil, 1]
])
end
end
end

View File

@ -3,10 +3,6 @@ require 'spec_helper'
describe Oga::XML::Querying do
before do
@document = parse('<a>foo</a>')
@document2 = parse('<a xmlns:x="y"><x:b>bar</x:b></a>')
@namespaces = {
"n" => "y"
}
end
describe '#xpath' do
@ -19,11 +15,7 @@ describe Oga::XML::Querying do
end
it 'evaluates an expression using a variable' do
expect(@document.xpath('$number', {'number' => 10})).to eq(10)
end
it 'respects custom namespace aliases' do
expect(@document2.xpath('a/n:b', namespaces: @namespaces)[0].text).to eq('bar')
expect(@document.xpath('$number', 'number' => 10)).to eq(10)
end
end
@ -37,11 +29,7 @@ describe Oga::XML::Querying do
end
it 'evaluates an expression using a variable' do
expect(@document.at_xpath('$number', {'number' => 10})).to eq(10)
end
it 'respects custom namespace aliases' do
expect(@document2.at_xpath('a/n:b', namespaces: @namespaces).text).to eq('bar')
expect(@document.at_xpath('$number', 'number' => 10)).to eq(10)
end
end

View File

@ -1,11 +0,0 @@
require 'spec_helper'
describe Oga::XML::ToXML do
describe '#to_s' do
it 'is an alias of to_xml' do
node = Oga::XML::Element.new(name: 'foo')
expect(node.method(:to_s)).to eq(node.method(:to_xml))
end
end
end

View File

@ -1,40 +0,0 @@
require 'spec_helper'
describe Oga::XPath::Compiler do
before do
@document = parse('<root xmlns:x="y"><x:a></x:a><b x:num="10"></b></root>')
@root = @document.children[0]
@a = @root.children[0]
@b = @root.children[1]
@attr = @b.attributes[0]
@namespaces = {
"n" => "y"
}
end
describe 'with custom namespace aliases' do
it 'uses aliases when querying an element' do
expect(evaluate_xpath(@document, 'root/n:a', namespaces: @namespaces)).to eq(node_set(@a))
end
it "doesn't use namespaces in XPath expression when querying an element" do
expect(evaluate_xpath(@document, 'root/x:a', namespaces: @namespaces)).to eq(node_set)
end
it 'uses aliases when querying an attribute' do
expect(evaluate_xpath(@document, 'root/b/@n:num', namespaces: @namespaces)).to eq(node_set(@attr))
end
it "doesn't use namespaces in XPath expression when querying an attribute" do
expect(evaluate_xpath(@document, 'root/b/@x:num', namespaces: @namespaces)).to eq(node_set)
end
it 'uses aliases when querying an element with a namespaced attribute' do
expect(evaluate_xpath(@document, 'root/b[@n:num]', namespaces: @namespaces)).to eq(node_set(@b))
end
it "doesn't use namespaces in XPath expression when querying an element with a namespaced attribute" do
expect(evaluate_xpath(@document, 'root/b[@x:num]', namespaces: @namespaces)).to eq(node_set)
end
end
end

View File

@ -68,26 +68,4 @@ describe Oga::XPath::Compiler do
end
end
end
describe 'querying elements with a default namespace' do
before do
@document = parse('<a xmlns="n" xmlns:ns1="x">Foo<b></b><b></b><ns1:c></ns1:c></a>')
@a1 = @document.children[0]
@b1 = @a1.children[1]
@b2 = @a1.children[2]
end
describe '/xmlns:a' do
it 'returns a NodeSet' do
expect(evaluate_xpath(@document)).to eq(node_set(@a1))
end
end
describe '//xmlns:b' do
it 'returns a NodeSet' do
expect(evaluate_xpath(@document)).to eq(node_set(@b1, @b2))
end
end
end
end

View File

@ -5,9 +5,9 @@ module Oga
# @param [String] xpath
# @return [Oga::XML::NodeSet]
#
def evaluate_xpath(document, xpath = self.class.description, namespaces: nil)
def evaluate_xpath(document, xpath = self.class.description)
ast = parse_xpath(xpath)
compiler = Oga::XPath::Compiler.new(namespaces: namespaces)
compiler = Oga::XPath::Compiler.new
block = compiler.compile(ast)
block.call(document)