Compare commits

..

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

299 changed files with 2469 additions and 3168 deletions

View File

@ -1,41 +0,0 @@
---
.defaults: &defaults
before_script:
- apk add --update ragel build-base
- if [ "$INSTALL_OPENJDK" == "true" ]; then apk add openjdk8; fi
- gem install bundler --no-document
- ruby --version
- gem --version
- bundle --version
- bundle install --path vendor --retry=3
script:
- bundle exec rake
cache:
paths:
- vendor/ruby
Ruby 2.3:
image: "ruby:2.3-alpine"
<<: *defaults
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:
INSTALL_OPENJDK: "true"
<<: *defaults

54
.travis.yml Normal file
View File

@ -0,0 +1,54 @@
---
language: ruby
script: bundle exec rake
sudo: false
addons:
apt:
packages:
- ragel
before_install:
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install ragel; fi
install:
- bundle install --retry=3
rvm:
- jruby
- 1.9.3
- 2.0.0
- 2.1
- 2.2
- 2.3.1
- rbx
matrix:
allow_failures:
- rvm: rbx
os: osx
exclude:
# Binaries for these rubies aren't available on OS X :<
- rvm: 2.2
os: osx
- rvm: 2.3.1
os: osx
- rvm: jruby
os: osx
fast_finish: true
notifications:
email:
recipients:
- yorickpeterse@gmail.com
on_success: change
on_failure: change
cache: bundler
os:
- linux
- osx

View File

@ -3,165 +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
`tfoot` elements. See issue <https://gitlab.com/yorickpeterse/oga/issues/190>
for more information.
## 2.14 - 2018-01-30
Various methods that yield a block now return an Enumerator when no block is
provided. See merge request
<https://gitlab.com/yorickpeterse/oga/merge_requests/184> for more information.
## 2.13 - 2018-01-05
Leading and trailing whitespace is now removed from CSS selectors. See
<https://gitlab.com/yorickpeterse/oga/merge_requests/183> for more information.
## 2.12 - 2017-12-29
Element start tags containing other start tags (e.g. `<script<script>`) are now
parsed correctly.
See f574197ea657cf09405336ca618a22e32c94d0d0 for more information.
## 2.11 - 2017-09-07
Various Ruby warnings have been resolved by Loic Nageleisen. See pull request
<https://gitlab.com/yorickpeterse/oga/pull/180> for more information.
## 2.10 - 2017-04-18
### Fix `Element#attribute` for HTML documents when using Symbol arguments
You can now pass a Symbol to `Oga::XML::Element#attribute` for both XML and HTML
documents, previously this only worked for XML documents. See
[PR #174](https://gitlab.com/yorickpeterse/oga/pull/174) for more information.
## 2.9 - 2017-02-10
### Closing tags for HTML void elements
Certain HTML elements such as `<img>` and `<link>` (called "void elements" in
Oga) are now closed using a `>` tag instead of `/>`. In other words, instead of
outputting `<img src="..." />` Oga now outputs `<img src="...">`.
### Doctypes are now Nodes
Each Doctype now inherits from `Oga::XML::Node`. This makes it possible to parse
documents where a doctype is located in a child node. However, in these cases
Oga will _not_ populate `Oga::XML::Document#doctype` as this can not be done in
an efficient way.
## 2.8 - 2017-01-04
Ruby 2.4 deprecates Fixnum in favour of Integer, producing warnings whenever
Fixnum is used. Oga 2.8 contains a fix contributed by Po Shan Cheah to remove
these deprecation warnings. See commit c75ca96d229a50b369e16057622255a674f2cabc
for more information.
## 2.7 - 2016-09-27
### Closing Elements When Generating XML
When generating XML Oga now properly closes elements with siblings but without
children. See commit e0e0687dc29427c854c9fa6d3c19cee1c04f92c7 for more
information.
### Newlines After Doctypes
When generating XML a newline would be inserted after a doctype. If another
newline would follow in a text node this would lead to multiple newlines being
present. Oga now ensures there is only 1 newline following a doctype. See commit
e0e0687dc29427c854c9fa6d3c19cee1c04f92c7 for more information.
### Processing Instructions With Namespace Prefixes
The XML lexer now supports processing instructions containing namespace prefixes
such as `<?xml:foo ?>`. See commit 01fa1513f4bd6f194bf6d1ca17e510003fa23312 for
more information.
### XML Declarations Are Now Processing Instructions
The class `Oga::XML::XmlDeclaration` now extends
`Oga::XML::ProcessingInstruction`. This allows documents to contain XML
declarations in nested elements, instead of only allowing this at the root of
the document. See commit 116b9b0ceb6feab4daa0bb417302590fba948bef for more
information.
### Aliases For Getting & Setting Attributes
The methods `Oga::XML::Element#get` and `Oga::XML::Element#set` are now aliased
as `#[]` and `#[]=` respectively. See d40baf0c724a3874f43100fbefa775cfb8dcacda
for more information and thanks to Scott Wheeler for contributing the patch.
## 2.6 - 2016-09-10
This release fixes a bug in the XML generation code that would cause it to get
stuck in the generation loop. See issue
<https://gitlab.com/yorickpeterse/oga/issues/161> and commit
38284278d542640c3d8300ef15890af93b6df779 for more information.
## 2.5 - 2016-09-06
This release fixes a bug in the XML parser that would prevent it from parsing
doctypes that contain a mixture of public/system IDs, a name, and inline rules.
See issue <https://gitlab.com/yorickpeterse/oga/issues/159> and commit
68f1f9f660b90a43d22c8514e8cbf53f7ca0097d for more information.
## 2.4 - 2016-09-04
### Serialising Large Documents
@ -170,7 +11,7 @@ Oga can now serialise large documents without causing the call stack to overflow
thanks to the new `Oga::XML::Generator` class. This class can generate XML
without using a stack at all.
See issue <https://gitlab.com/yorickpeterse/oga/issues/158> and commit
See issue <https://github.com/YorickPeterse/oga/issues/158> and commit
dd138981f68a606eff5d5a01e990f04398087dc4 for more information.
### Faster retrieval of previous/next nodes
@ -188,14 +29,14 @@ See commit 5a58b1413767fed4518e8a67c4eb432a31592660 for more information.
Thanks to various changes provided by Erik Michaels-Ober Oga can now be used to
parse XML input from a pipe (as returned by for example `IO.pipe`). See the
following pull request for more information:
<https://gitlab.com/yorickpeterse/oga/pull/154>.
<https://github.com/YorickPeterse/oga/pull/154>.
## 2.2 - 2016-02-23
### XPath support for nested pipe operators
Nested pipe operators such as `a | b | c` are now supported as XPath
expressions. See issue <https://gitlab.com/yorickpeterse/oga/issues/149> and
expressions. See issue <https://github.com/YorickPeterse/oga/issues/149> and
commit 6d3c5c2ce93cbce337338bdc1a4971da72517038 for more information.
## 2.1 - 2016-02-09
@ -204,7 +45,7 @@ commit 6d3c5c2ce93cbce337338bdc1a4971da72517038 for more information.
Decoding of invalid XML/HTML entities now results in these entities being
preserved as-is, instead of raising an EncodingError in certain places. See
<https://gitlab.com/yorickpeterse/oga/issues/143> and commit
<https://github.com/YorickPeterse/oga/issues/143> and commit
5bfc2d50f2a3d387cb9fc28826d1f3d5a2d9d224 for more information.
### New Versioning Format
@ -302,8 +143,8 @@ new compiler setup, how it works, how it performs, etc.
In the mean time, see the following issues/pull requests for more information:
* <https://gitlab.com/yorickpeterse/oga/issues/102>
* <https://gitlab.com/yorickpeterse/oga/pull/138>
* <https://github.com/YorickPeterse/oga/issues/102>
* <https://github.com/YorickPeterse/oga/pull/138>
### Escaping of characters in CSS expressions
@ -312,14 +153,14 @@ namespace. This can be done by escaping the dot using a backslash. For example:
Oga.parse_xml('<foo.bar />').css('foo\.bar') # => NodeSet(Element(name: "foo.bar"))
See issue <https://gitlab.com/yorickpeterse/oga/issues/124> for more
See issue <https://github.com/YorickPeterse/oga/issues/124> for more
information.
### Support for the CSS :not() pseudo class
CSS expressions can now use the `:not()` pseudo class.
See issue <https://gitlab.com/yorickpeterse/oga/issues/125> for more
See issue <https://github.com/YorickPeterse/oga/issues/125> for more
information.
### Improved parsing of CSS expressions
@ -329,8 +170,8 @@ these would result in parser errors.
See the following issues for more information:
* <https://gitlab.com/yorickpeterse/oga/issues/126>
* <https://gitlab.com/yorickpeterse/oga/issues/131>
* <https://github.com/YorickPeterse/oga/issues/126>
* <https://github.com/YorickPeterse/oga/issues/131>
### Unicode support for CSS/XPath
@ -338,7 +179,7 @@ CSS and XPath expressions can now contain Unicode characters, previously only
ASCII characters were allowed for identifiers (node tests, attribute names,
etc).
See issue <https://gitlab.com/yorickpeterse/oga/issues/140> for more
See issue <https://github.com/YorickPeterse/oga/issues/140> for more
information.
## 1.2.3 - 2015-08-19
@ -386,8 +227,8 @@ Jakub Pawlowicz improved the process of decoding XML/HTML entities so that it
handles unrecognized entities better. Previously Oga would raise an error when
trying to decode entities such as `&#TAB;` instead of just leaving them as-is.
See issue <https://gitlab.com/yorickpeterse/oga/issues/118> and pull request
<https://gitlab.com/yorickpeterse/oga/pull/122> for more information.
See issue <https://github.com/YorickPeterse/oga/issues/118> and pull request
<https://github.com/YorickPeterse/oga/pull/122> for more information.
## 1.2.0 - 2015-06-30
@ -446,7 +287,7 @@ replaced with a Text node). For example:
Thanks to Tero Tasanen for adding this.
See commit 0b4791b277abf492ae0feb1c467dfc03aef4f2ec and
<https://gitlab.com/yorickpeterse/oga/pull/116> for more information.
<https://github.com/YorickPeterse/oga/pull/116> for more information.
### Encoding quotes in attribute values
@ -601,8 +442,8 @@ See the following commits for more information:
The following issues are also worth checking out:
* https://gitlab.com/yorickpeterse/oga/issues/101
* https://gitlab.com/yorickpeterse/oga/issues/99
* https://github.com/YorickPeterse/oga/issues/101
* https://github.com/YorickPeterse/oga/issues/99
### Handling of invalid XML/HTML
@ -679,7 +520,7 @@ And so is this:
<a href=foo/bar>Foo/bar</a>
See GitLab issue <https://gitlab.com/yorickpeterse/oga/issues/94> and the
See Github issue <https://github.com/YorickPeterse/oga/issues/94> and the
following commits for more information:
* bc9b9bc9537d9dc614b47323e0a6727a4ec2dd04
@ -703,7 +544,7 @@ The XML lexer has been tweaked so it can handle multi-line CDATA tags, comments
and processing instructions, both when using a String and IO (or similar) as
input.
See GitLab issue <https://gitlab.com/yorickpeterse/oga/issues/93> and the
See Github issue <https://github.com/YorickPeterse/oga/issues/93> and the
following commits for more information:
* b2ea20ba615953254554565e0c8b11587ac4f59c
@ -819,7 +660,7 @@ like the other callbacks.
### Parser rewritten using ruby-ll
The XML, CSS and XPath parsers have been re-written using ruby-ll
(<https://gitlab.com/yorickpeterse/ruby-ll>). While Racc served its purpose
(<https://github.com/yorickpeterse/ruby-ll>). While Racc served its purpose
(until now) it has three main problems:
1. Performance is not as good as it should be.
@ -832,7 +673,7 @@ ruby-ll parsers. These parsers are LL(1) parsers which makes them a lot easier
to debug. Performance is currently a tiny bit faster than the old Racc parsers,
but this will be improved in the coming releases of both Oga and ruby-ll.
See pull request <https://gitlab.com/yorickpeterse/oga/pull/78> for more
See pull request <https://github.com/YorickPeterse/oga/pull/78> for more
information.
### Lazy decoding of XML/HTML entities
@ -878,7 +719,7 @@ documents _don't_ have their contents converted, ensuring proper Javascript
syntax upon output.
See commit 874d7124af540f0bc78e6c586868bbffb4310c5d and issue
<https://gitlab.com/yorickpeterse/oga/issues/79> for more information.
<https://github.com/YorickPeterse/oga/issues/79> for more information.
### Proper lexing support for script tags
@ -886,7 +727,7 @@ When lexing HTML documents the XML lexer is now capable of lexing the contents
of `<script>` tags properly. Previously input such as `<script>x >y</script>`
would result in incorrect tokens being emitted. See commit
ba2177e2cfda958ea12c5b04dbf60907aaa8816d and issue
<https://gitlab.com/yorickpeterse/oga/issues/70> for more information.
<https://github.com/YorickPeterse/oga/issues/70> for more information.
### Element Inner Text
@ -894,7 +735,7 @@ When setting the inner text of an element using `Oga::XML::Element#inner_text=`
_all_ child nodes of the element are now removed first, instead of only text
nodes being removed.
See <https://gitlab.com/yorickpeterse/oga/issues/64> for more information.
See <https://github.com/YorickPeterse/oga/issues/64> for more information.
### Support for extra XML entities
@ -952,14 +793,14 @@ perhaps other libraries) the parser _does not_ output XPath expressions as a
String or a CSS specific AST. Instead it directly emits an XPath AST. This
allows the resulting AST to be directly evaluated by `Oga::XPath::Evaluator`.
See <https://gitlab.com/yorickpeterse/oga/issues/11> for more information.
See <https://github.com/YorickPeterse/oga/issues/11> for more information.
### Mutli-line Attribute Support
Oga can now lex/parse elements that have attributes with newlines in them.
Previously this would trigger memory allocation errors.
See <https://gitlab.com/yorickpeterse/oga/issues/58> for more information.
See <https://github.com/YorickPeterse/oga/issues/58> for more information.
### SAX after_element
@ -967,7 +808,7 @@ The `after_element` method in the SAX parsing API now always takes two
arguments: the namespace name and element name. Previously this method would
always receive a single nil value as its argument, which is rather pointless.
See <https://gitlab.com/yorickpeterse/oga/issues/54> for more information.
See <https://github.com/YorickPeterse/oga/issues/54> for more information.
### XPath Grouping
@ -987,7 +828,7 @@ This can be used to download and parse XML files on the fly. For example:
document = Oga.parse_xml(enum)
See <https://gitlab.com/yorickpeterse/oga/issues/48> for more information.
See <https://github.com/YorickPeterse/oga/issues/48> for more information.
### Removing Attributes
@ -1021,7 +862,7 @@ the usage of the default `Object#==` method.
XML entities such as `&amp;` and `&lt;` are now encoded/decoded by the lexer,
string and text nodes.
See <https://gitlab.com/yorickpeterse/oga/issues/49> for more information.
See <https://github.com/YorickPeterse/oga/issues/49> for more information.
### General
@ -1044,7 +885,7 @@ improved by removing String allocations that were not needed.
## 0.1.3 - 2014-09-24
This release fixes a problem with serializing attributes using the namespace
prefix "xmlns". See <https://gitlab.com/yorickpeterse/oga/issues/47> for more
prefix "xmlns". See <https://github.com/YorickPeterse/oga/issues/47> for more
information.
## 0.1.2 - 2014-09-23

View File

@ -6,9 +6,9 @@ one should follow.
## Code of Conduct
The code of conduct ("CoC") can be found in the file "CODE_OF_CONDUCT.md".
Everybody participating in this project must adhere to the rules and guidelines
stated in this CoC.
The code of conduct ("CoC") can be found in the file "COC.md". Everybody
participating in this project must adhere to the rules and guidelines stated in
this CoC.
## General
@ -20,7 +20,7 @@ stated in this CoC.
## Submitting Changes
Before making any big changes it's best to open a GitLab issue to discuss the
Before making any big changes it's best to open a Github issue to discuss the
matter, this saves you from potentially spending hours on something that might
ultimately be rejected.
@ -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://github.com/YorickPeterse/oga/pull/96
* https://github.com/YorickPeterse/oga/pull/67
* https://github.com/YorickPeterse/ffi-aspell/pull/21
* https://github.com/YorickPeterse/ffi-aspell/pull/20
* https://github.com/YorickPeterse/ruby-ll/pull/16
## Git
@ -75,29 +75,36 @@ Use spaces for indentation, tabs are not accepted. The usage of spaces ensures
the indentation is identical no matter what program or system is used to view
the source code.
Hard wrap lines at roughly 80 characters per line. Most modern editors can
easily handle this. For example, in Vim you can select text in visual mode
(using `v`) and press `gq` to automatically re-wrap the selected text.
Hard wrap lines at 80 characters per line. Most modern editors can easily handle
this, if not you should get a better editor. For example, in Vim you can select
text in visual mode (using `v`) and press `gq` to automatically re-wrap the
selected text.
It's OK if a line is a few characters longer than 80 but _please_ keep it as
close to 80 characters as possible. Typically I do this when wrapping the line
results in several extra lines without it being much more readable.
I often have multiple windows vertically next to each other and 80 characters
per line is the only setup that lets me do so, even on smaller screen
resolutions. For example, my typical setup is 1 file browser and two vertical
windows. Using 80 characters per line ensures all code fits in that space along
with some slight padding to make reading more pleasant.
To make this process easier Oga comes with an [EditorConfig][editorconfig]
configuration file. If your editor supports this it will automatically apply
various settings for you.
the required settings for you.
## Hacking on Oga
Before you start hacking on Oga make sure the following libraries/tools are
installed:
* Ragel 6.x (6.10 recommended), Ragel 7.x is not supported
* Ragel 6.x (6.9 recommended)
* gunzip (to unpack the fixtures)
* javac (only when using JRuby)
Assuming you have the above tools installed and a local Git clone of Oga, first
you'll need to install the required Gems:
Assuming you have the above tools installed and a local Git clone of Oga, lets
install the required Gems:
bundle install
@ -105,8 +112,7 @@ Next up, compile the required files and run the tests:
rake
If you just want to generate various files (e.g. the C extension), run the
following instead:
You can compile the various parsers/extensions by running:
rake generate
@ -122,10 +128,19 @@ benchmark is just a matter of running a Ruby script, for example:
## Tests
Tests are written using RSpec and use the "expect" syntax. Specification blocks
should be written using `it`, grouping should be done using `describe`.
Specification descriptions should be meaningful and human-friendly English. For
example:
Tests are written using RSpec and use the "should" syntax instead of the
"expect" syntax (for as long as RSpec keeps supporting this). This means that
assertions are written as following:
some_object.should == some_value
instead of this:
expect(some_object).to eq(some_value)
Specification blocks should be written using `it`, grouping should be done using
`describe`. Specification descriptions should be meaningful and human friendly
English. For example:
describe Oga::XML::Entities do
describe 'decode' do
@ -139,15 +154,40 @@ Typically the top-level `describe` block is used to describe a method name. In
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
you expect this to be a lot of work you should open an issue first to discuss
things.
Do not use `let` for creating data re-used between specifications, instead use
a `before` block that sets an instance variable. In other words, use this:
before do
@number = 10
end
instead of this:
let(:number) { 10 }
Instance variables stand out much more and they don't require one to also
understand what exactly `let` does which in turn simplifies the process of
reading and writing specifications.
Whenever adding new specifications please keep them in the existing style unless
I indicate otherwise. There's nothing more annoying than inconsistent
specifications.
If you insist on changing the structure/style of specifications please open an
issue and ask about it _before_ making any changes. I am very picky about how I
want things and it would be a shame for somebody to spend hours working on
something that isn't going to be merged in any way.
## 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://travis-ci.org/YorickPeterse/oga>
* AppVeyor (Windows): <https://ci.appveyor.com/project/YorickPeterse/oga>
Please note that I will not accept patches that break any tests unless stated
otherwise.
## Extension Setup
@ -214,7 +254,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

@ -2,6 +2,10 @@ source 'https://rubygems.org'
gemspec
platforms :mingw_19, :ruby_19 do
gem 'json', '~> 1.8'
end
group :benchmarking do
gem 'ox', :platforms => [:mri, :rbx]
gem 'nokogiri'

View File

@ -1,9 +1,5 @@
# Oga
**NOTE:** my spare time is limited which means I am unable to dedicate a lot of
time on Oga. If you're interested in contributing to FOSS, please take a look at
the open issues and submit a pull request to address them where possible.
Oga is an XML/HTML parser written in Ruby. It provides an easy to use API for
parsing, modifying and querying documents (using XPath expressions). Oga does
not require system libraries such as libxml, making it easier and faster to
@ -173,8 +169,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 +223,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 +242,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,
@ -266,7 +266,7 @@ well as complicating the parsing internals of Oga. As a result I have decided
that Oga _does not_ insert these tags when left out.
A more in depth explanation can be found here:
<https://gitlab.com/yorickpeterse/oga/issues/98#note_45443992>
<https://github.com/YorickPeterse/oga/issues/98#issuecomment-96833066>.
## Documentation
@ -287,7 +287,7 @@ Currently there are a few existing parser out there, the most famous one being
The sad truth is that these existing libraries are problematic in their own
ways. Nokogiri for example is extremely unstable on Rubinius. On MRI it works
because of the non concurrent nature of MRI, on JRuby it works because it's
because of the non conccurent nature of MRI, on JRuby it works because it's
implemented as Java. Nokogiri also uses libxml2 which is a massive beast of a
library, is not thread-safe and problematic to install on certain platforms
(apparently). I don't want to compile libxml2 every time I install Nokogiri

View File

@ -1,32 +1,38 @@
---
image: Visual Studio 2017
version: "{build}"
install:
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-ragel"
- SET PATH=C:\msys64\mingw64\bin;%PATH%
# Binary taken from http://w858rkbfg.homepage.t-online.de/index.php/software/ragel-windows/,
# rehosted on AWS so it doesn't randomly vanish.
- appveyor DownloadFile http://downloads.yorickpeterse.com/files/ragel-68-visualstudio2012.7z -FileName C:\ragel.7z
- 7z e C:\ragel.7z -oC:\ragel -y > nul
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
- SET PATH=C:\ragel;%PATH%
- ruby --version
- gem --version
- appveyor DownloadFile https://rubygems.org/downloads/bundler-1.9.0.gem -FileName bundler-1.9.0.gem
- gem install bundler-1.9.0.gem --local --quiet --no-ri --no-rdoc
- bundle install --retry 3
build: off
before_test:
- ragel --version
- ruby --version
- gem --version
- bundle --version
test_script:
- rake
environment:
matrix:
- ruby_version: "193"
- ruby_version: "200"
- ruby_version: "21"
- ruby_version: "21-x64"
- ruby_version: "22"
- ruby_version: "22-x64"
- ruby_version: "23"
- ruby_version: "23-x64"
- ruby_version: "24"
- ruby_version: "24-x64"
skip_tags: true
notifications:
-
provider: Email
on_build_success: false
on_build_failure: false
on_build_status_changed: true

View File

@ -1 +0,0 @@
bac38b7526a1c7460175933942f9c6e7655807b5250c7032b2be8633f1d616a9285084a57e1ee3a4b6a05d15ae1490b09313a827f85fd8ebcb46a7c65d2175c0

View File

@ -1 +0,0 @@
84f6fe917bf8e335b391f0817cab9fb07b8c9ad2c3618ce530b3443d0c6e8e31f47cccc039f376a1b3d329a5cc0d3a84664f3e72a7c85b31b5b02b6db5c8360f

View File

@ -1 +0,0 @@
e89ef454241cad99049fa3767eb1c76ab7e1e10a0943e6448ffc7cfc70a251f7a72a3cd61f0664c35a54791752960b6432ddd857d56d11594b6272bff44d405b

View File

@ -1 +0,0 @@
d6d3f1f133c9f93bd9cc36b7748c5b8a223d53daacb0ed0c9784fb0aac08d483c558208ddda1fc6b7b544a638a4cd89f791d397c712f622b7096d0b99ab7ec79

View File

@ -1 +0,0 @@
5389228f4c92851e74396e507c37b0fbe8659522fad0932954a710640268cd51926185b0d4c7887b6d5c13f11af0dbde45fb43d0dd1adce5d8688f050d2de1ed

View File

@ -1 +0,0 @@
ab7a64c63bf2f03ecfe42f45199c943d60240eed0ee57f71e1d05fe8d29264c942f12bba8c34d3e3c22f4cb502611d3c65bda17a8c4afec9a593599537c143b6

View File

@ -1 +0,0 @@
a5492d3bfb21fad9060412e4189b2f08c179840eae5bacee2580795e5ce6bf9aa10d7e95c1e7729259c708f25051cf4df7d48261a05fbda8c39c5879656135c9

View File

@ -1 +0,0 @@
5acda689eff5c8f6ac03f97189f9906305576ab213276f317e5136476751905a0748be7058eca808d80bbdbe50475024c40400c135946c56fd2e0b958a8b4037

View File

@ -1 +0,0 @@
565cc42a3d776ead3bae1423af8b24279ef53f4ebdee7812bd2c842cf5660f0f6a8ac0054ed9dee37493f42335e0ab529c5aae965e218e49201c9408358ddb4c

View File

@ -1 +0,0 @@
05cb13515c8b4e99b0c59fb7d362b7245ad97eb4a9243c6a4c9eb8c89bd2a1db19b6951a7cd6e7484493df31aa49b8e0ce64c1aa15ecd3dafd4aa6fc5578918b

View File

@ -1 +0,0 @@
beaa3a5d8c5b64288f83f141daece073b3601ff631dc33a4a7d268fcf38b5a667c44c3ed67964baa6e250501b3b965df3b6ecfeee9a407a063981e91474baef9

View File

@ -1 +0,0 @@
b5071a0cd5d2d1bf79577ed1235ac9233de5cbbffc5c47a5ba375152dbf11fc6f1c8c66253d986288817862915df350aad7a7760ec8c536b479c9d5850a34cbf

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 @@
885d4a6155a93d50bff733c778886feb1a514ccf4bd937eabd022439622c75b83b00bd9994c974c9733e97f38b6dd02d882dc994868ca7bb90f03133cad70255

View File

@ -1 +0,0 @@
c129448eeabb128fd223833197efeb057fb03ccd33434c897bb5e27ceceee0a32cfd6f8abe2a7ab8512c0edadad99cedc994332262cf569a623eea9fc0287473

View File

@ -1 +0,0 @@
b107dec6aff28640a307ca02661abd5a29a5f0c369422c116bad7a198b11eb9aa9cab90bfe874a0c9fd4391343d4b5589fcbf5b5a0ff557ab520fd83725de9e8

View File

@ -1 +0,0 @@
8c888218a3da5a639df19e042a3e66bea2d5eec5c660bb4f13982eaff0626f80ab2840d0f58bba2a1fe5dce12563b5f73a9f7b15f23b05ff5c87fe4b7f064621

View File

@ -1 +0,0 @@
9bfe28b9b7b971fd80f405ad12c7c58cc94152b85ed29d4dc1e50525d220e1bc0a42fa76083fc0637a8c799f31c2c3599664df68af3dd2ed2c8ed3ae20b5b6a2

View File

@ -1 +0,0 @@
1a00cd24864ce2777a02f2c1964ca636654d860e61a0be7437bdcc8bf1afe393138ced1913117eb58ac10bd25f306fda16e0ae2f737486414522102642ac03e5

View File

@ -1 +0,0 @@
e948ef42c95ca8c0a8b1fe7f765c75b2be048ccd154cdafd49610e7c80c44a7592152bdc02cb37f466b62be99299b20879392518c98d4f94a0bad121d5844c44

View File

@ -1 +0,0 @@
59f3805163a97f3b06c13c9c46691bb34c106a4cdac3d04f57aaf93c64de74016b9485eff81d9461f21162887d1f114e6fa22bcfc0c36eb57d4ca93716ff001f

View File

@ -1 +0,0 @@
2839f9bf005a0875d87d0b6796aab4c4e6591ff1a02e8032ce9bc06d340db9fde3ac32d79f8ae19c9fd7cac8a4b3c50a9a5840b72e7912f205dfd12320466c41

View File

@ -1 +0,0 @@
c60b30f7b83b21916ed54ce37d5bed734f7e5807576ce2fb94e5ac577a479fa5a9f19594d44587b635727c6c9f88d0fc75c945d22f32342df6e159878b2c950d

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

@ -2,6 +2,8 @@ body
{
font-size: 14px;
line-height: 1.6;
margin: 0 auto;
max-width: 960px;
}
p code, dd code, li code

View File

@ -1,7 +1,7 @@
require 'mkmf'
if RbConfig::CONFIG['CC'] =~ /clang|gcc/
$CFLAGS << ' -pedantic -Wno-implicit-fallthrough'
$CFLAGS << ' -pedantic'
end
if ENV['DEBUG']

View File

@ -161,7 +161,7 @@
# instruction.
#
proc_ins_start = '<?' identifier (':' identifier)?;
proc_ins_start = '<?' identifier;
proc_ins_end = '?>';
# Everything except "?" OR a single "?"
@ -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);
};
@ -389,7 +389,6 @@
element_start = '<' ident_char;
element_end = '</';
element_start_pattern = '<' identifier (':' identifier)?;
# Machine used for lexing the name/namespace of an element.
element_name := |*
@ -552,7 +551,6 @@
# Machine used for processing the contents of an XML element's starting tag.
element_head := |*
newline => advance_newline;
element_start_pattern;
# Attribute names and namespaces.
identifier ':' => {
@ -580,7 +578,6 @@
# tag.
html_element_head := |*
newline => advance_newline;
element_start_pattern;
html_identifier => {
callback(id_on_attribute, data, encoding, ts, te);

View File

@ -35,8 +35,8 @@ require 'oga/xml/character_node'
require 'oga/xml/text'
require 'oga/xml/comment'
require 'oga/xml/cdata'
require 'oga/xml/processing_instruction'
require 'oga/xml/xml_declaration'
require 'oga/xml/processing_instruction'
require 'oga/xml/doctype'
require 'oga/xml/namespace'
require 'oga/xml/default_namespace'

View File

@ -23,7 +23,7 @@ module Oga
# @param [String] data The data to lex.
def initialize(data)
@data = data.strip
@data = data
end
# Gathers all the tokens for the input and returns them as an Array.

View File

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

View File

@ -34,11 +34,10 @@ module Oga
# @option options [String] :value
# @option options [Oga::XML::Element] :element
def initialize(options = {})
@name = options[:name]
@value = options[:value]
@element = options[:element]
@decoded = false
@namespace = nil
@name = options[:name]
@value = options[:value]
@element = options[:element]
@namespace_name = options[:namespace_name]
end
@ -99,14 +98,12 @@ module Oga
end
# @see [Oga::XML::Node#each_ancestor]
def each_ancestor
return to_enum(:each_ancestor) unless block_given?
def each_ancestor(&block)
return unless element
yield element
element.each_ancestor { |ancestor| yield ancestor }
element.each_ancestor(&block)
end
private

View File

@ -1,7 +1,9 @@
module Oga
module XML
# Class used for storing information about Doctypes.
class Doctype < Node
class Doctype
include ToXML
# The name of the doctype (e.g. "HTML").
# @return [String]
attr_accessor :name

View File

@ -7,11 +7,6 @@ module Oga
include Traversal
include ToXML
# The doctype of the document.
#
# When parsing a document this attribute will be set automatically if a
# doctype resides at the root of the document.
#
# @return [Oga::XML::Doctype]
attr_accessor :doctype
@ -46,8 +41,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

@ -34,11 +34,10 @@ module Oga
def initialize(options = {})
super
@name = options[:name]
@namespace_name = options[:namespace_name]
@attributes = options[:attributes] || []
@namespaces = options[:namespaces] || {}
@available_namespaces = nil
@name = options[:name]
@namespace_name = options[:namespace_name]
@attributes = options[:attributes] || []
@namespaces = options[:namespaces] || {}
link_attributes
register_namespaces_from_attributes
@ -65,14 +64,14 @@ module Oga
#
# @return [Oga::XML::Attribute]
def attribute(name)
name_str, ns = if html?
[name.to_s, nil]
if html?
ns = nil
else
split_name(name)
name, ns = split_name(name)
end
attributes.each do |attr|
return attr if attribute_matches?(attr, ns, name_str)
return attr if attribute_matches?(attr, ns, name)
end
return
@ -92,8 +91,6 @@ module Oga
found ? found.value : nil
end
alias_method :[], :get
# Adds a new attribute to the element.
#
# @param [Oga::XML::Attribute] attribute
@ -116,10 +113,14 @@ module Oga
if found
found.value = value
else
name_str, ns = split_name(name)
if name.include?(':')
ns, name = name.split(':')
else
ns = nil
end
attr = Attribute.new(
:name => name_str,
:name => name,
:namespace_name => ns,
:value => value
)
@ -128,8 +129,6 @@ module Oga
end
end
alias_method :[]=, :set
# Removes an attribute from the element.
#
# @param [String] name The name (optionally including namespace prefix)

View File

@ -13,12 +13,12 @@ module Oga
#
# @private
class Generator
# @param [Oga::XML::Document|Oga::XML::Node] root The node to serialise.
# @param [Oga::XML::Document|Oga::XML::Node] start The node to serialise.
def initialize(root)
@start = root
if @start.respond_to?(:html?)
@html_mode = @start.html?
if @start.respond_to?(:root_node)
@html_mode = @start.root_node.html?
else
@html_mode = false
end
@ -48,14 +48,12 @@ module Oga
callback = :on_comment
when Oga::XML::Attribute
callback = :on_attribute
when Oga::XML::XmlDeclaration
# This must come before ProcessingInstruction since XmlDeclaration
# extends ProcessingInstruction.
callback = :on_xml_declaration
when Oga::XML::ProcessingInstruction
callback = :on_processing_instruction
when Oga::XML::Doctype
callback = :on_doctype
when Oga::XML::XmlDeclaration
callback = :on_xml_declaration
when Oga::XML::Document
callback = :on_document
children = true
@ -67,24 +65,13 @@ module Oga
if child_node = children && current.children[0]
current = child_node
elsif current == @start
# When we have reached the root node we should not process
# any of its siblings. If we did we'd include XML in the
# output from elements no part of the root node.
after_element(current, output) if current.is_a?(Element)
break
else
# Make sure to always close the current element before
# moving to any siblings.
after_element(current, output) if current.is_a?(Element)
until next_node = current.is_a?(Node) && current.next
if current.is_a?(Node) && current != @start
current = current.parent
end
after_element(current, output) if current.is_a?(Element)
send(:after_element, current, output) if current.is_a?(Element)
break if current == @start
end
@ -125,7 +112,7 @@ module Oga
end
# @param [Oga::XML::Element] element
# @param [String] output The content of the element.
# @param [String] body The content of the element.
def on_element(element, output)
name = element.expanded_name
attrs = ''
@ -136,9 +123,7 @@ module Oga
end
if self_closing?(element)
closing_tag = html_void_element?(element) ? '>' : ' />'
output << "<#{name}#{attrs}#{closing_tag}"
output << "<#{name}#{attrs} />"
else
output << "<#{name}#{attrs}>"
end
@ -171,7 +156,7 @@ module Oga
output << '>'
end
# @param [Oga::XML::Document] doc
# @param [Oga::XML::Document] node
# @param [String] output
def on_document(doc, output)
if doc.xml_declaration
@ -183,14 +168,6 @@ module Oga
on_doctype(doc.doctype, output)
output << "\n"
end
first_child = doc.children[0]
# Prevent excessive newlines in case the next node is a newline text
# node.
if first_child.is_a?(Text) && first_child.text.start_with?("\r\n", "\n")
output.chomp!
end
end
# @param [Oga::XML::XmlDeclaration] node
@ -216,10 +193,6 @@ module Oga
element.children.empty?
end
end
def html_void_element?(element)
@html_mode && HTML_VOID_ELEMENTS.allow?(element.name)
end
end
end
end

View File

@ -58,11 +58,7 @@ module Oga
HTML_SCRIPT_ELEMENTS = Whitelist.new(%w{script template})
# The elements that may occur in a thead, tbody, or tfoot.
#
# Technically "th" is not allowed per the HTML5 spec, but it's so commonly
# used in these elements that we allow it anyway.
HTML_TABLE_ROW_ELEMENTS = Whitelist.new(%w{tr th}) + HTML_SCRIPT_ELEMENTS
HTML_TABLE_ROW_ELEMENTS = Whitelist.new(%w{tr}) + HTML_SCRIPT_ELEMENTS
# Elements that should be closed automatically before a new opening tag is
# processed.

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)
@ -182,8 +180,6 @@ module Oga
#
# @yieldparam [Oga::XML::Node]
def each_ancestor
return to_enum(:each_ancestor) unless block_given?
node = parent
while node.is_a?(XML::Element)

View File

@ -42,15 +42,17 @@ 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.
#
# @yieldparam [Oga::XML::Node]
def each
return to_enum(:each) unless block_given?
@nodes.each { |node| yield node }
end
@ -285,14 +287,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

@ -60,20 +60,35 @@ expression
# <!DOCTYPE html [ ... ]>
doctype
= T_DOCTYPE_START T_DOCTYPE_NAME T_DOCTYPE_TYPE? string? string? doctype_inline T_DOCTYPE_END
= T_DOCTYPE_START T_DOCTYPE_NAME doctype_follow
{
name = val[1]
follow = val[2]
on_doctype(
:name => val[1],
:type => val[2],
:public_id => val[3],
:system_id => val[4],
:inline_rules => val[5]
:name => name,
:type => follow[0],
:public_id => follow[1],
:system_id => follow[2],
:inline_rules => follow[3]
)
}
;
# Returns: [T_DOCTYPE_TYPE, string, string, doctype_inline]
doctype_follow
= T_DOCTYPE_END { [] }
| T_DOCTYPE_TYPE doctype_types { [val[0], *val[1]] }
| doctype_inline T_DOCTYPE_END { [nil, nil, nil, val[0]] }
;
doctype_inline
= T_DOCTYPE_INLINE* { val[0].inject(:+) }
= T_DOCTYPE_INLINE+ { val[0].inject(:+) }
;
doctype_types
= string string? T_DOCTYPE_END { [val[0], val[1]] }
| T_DOCTYPE_END { nil }
;
# CDATA tags

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

@ -74,7 +74,18 @@ module Oga
super(*args)
end
# Manually define `on_element` so we can ensure that `after_element`
# Delegate all callbacks to the handler object.
instance_methods.grep(/^(on_|after_)/).each do |method|
eval <<-EOF, nil, __FILE__, __LINE__ + 1
def #{method}(*args)
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]
@ -85,7 +96,7 @@ module Oga
[namespace, name]
end
# Manually define `after_element` so it can take a namespace and name.
# 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.
#
@ -96,7 +107,7 @@ module Oga
return
end
# Manually define this method since for this one we _do_ want the
# Manually overwrite this method since for this one we _do_ want the
# return value so it can be passed to `on_element`.
#
# @see [Oga::XML::Parser#on_attribute]
@ -146,21 +157,6 @@ module Oga
return
end
# Delegate remaining callbacks to the handler object.
existing_methods = instance_methods(false)
instance_methods.grep(/^(on_|after_)/).each do |method|
next if existing_methods.include?(method)
eval <<-EOF, nil, __FILE__, __LINE__ + 1
def #{method}(*args)
run_callback(:#{method}, *args)
return
end
EOF
end
private
# @return [TrueClass|FalseClass]

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

@ -27,8 +27,6 @@ module Oga
#
# @yieldparam [Oga::XML::Node] The current node.
def each_node
return to_enum(:each_node) unless block_given?
visit = children.to_a.reverse
until visit.empty?

View File

@ -1,7 +1,9 @@
module Oga
module XML
# Class containing information about an XML declaration tag.
class XmlDeclaration < ProcessingInstruction
class XmlDeclaration
include ToXML
# @return [String]
attr_accessor :version
@ -18,12 +20,9 @@ module Oga
# @option options [String] :encoding
# @option options [String] :standalone
def initialize(options = {})
super
@version = options[:version] || '1.0'
@encoding = options[:encoding] || 'UTF-8'
@standalone = options[:standalone]
@name = 'xml'
end
# @return [String]

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

View File

@ -76,7 +76,7 @@ module Oga
if value.is_a?(Float)
bool = !value.nan? && !value.zero?
elsif value.is_a?(Integer)
elsif value.is_a?(Fixnum)
bool = !value.zero?
elsif value.respond_to?(:empty?)
bool = !value.empty?

Binary file not shown.

View File

@ -6,7 +6,7 @@ Gem::Specification.new do |s|
s.authors = ['Yorick Peterse']
s.email = 'yorickpeterse@gmail.com'
s.summary = 'Oga is an XML/HTML parser written in Ruby.'
s.homepage = 'https://gitlab.com/yorickpeterse/oga/'
s.homepage = 'https://github.com/yorickpeterse/oga/'
s.description = s.summary
s.license = 'MPL-2.0'
@ -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

@ -14,15 +14,15 @@ describe Oga::Blacklist do
it 'returns true for a name not in the list' do
list = described_class.new(%w{foo})
expect(list.allow?('bar')).to eq(true)
expect(list.allow?('BAR')).to eq(true)
list.allow?('bar').should == true
list.allow?('BAR').should == true
end
it 'returns false for a name in the list' do
list = described_class.new(%w{foo})
expect(list.allow?('foo')).to eq(false)
expect(list.allow?('FOO')).to eq(false)
list.allow?('foo').should == false
list.allow?('FOO').should == false
end
end
@ -32,8 +32,8 @@ describe Oga::Blacklist do
list2 = described_class.new(%w{bar})
list3 = list1 + list2
expect(list3).to be_an_instance_of(described_class)
expect(list3.names.to_a).to eq(%w{foo FOO bar BAR})
list3.should be_an_instance_of(described_class)
list3.names.to_a.should == %w{foo FOO bar BAR}
end
end
end

View File

@ -10,15 +10,15 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing direct child nodes' do
expect(evaluate_css(@document, 'root > a')).to eq(node_set(@a1))
evaluate_css(@document, 'root > a').should == node_set(@a1)
end
it 'returns a node set containing direct child nodes relative to a node' do
expect(evaluate_css(@a1, '> a')).to eq(@a1.children)
evaluate_css(@a1, '> a').should == @a1.children
end
it 'returns an empty node set for non matching child nodes' do
expect(evaluate_css(@document, '> a')).to eq(node_set)
evaluate_css(@document, '> a').should == node_set
end
end
@ -31,15 +31,15 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing following siblings' do
expect(evaluate_css(@document, 'root a + b')).to eq(node_set(@b1))
evaluate_css(@document, 'root a + b').should == node_set(@b1)
end
it 'returns a node set containing following siblings relatie to a node' do
expect(evaluate_css(@b1, '+ b')).to eq(node_set(@b2))
evaluate_css(@b1, '+ b').should == node_set(@b2)
end
it 'returns an empty node set for non matching following siblings' do
expect(evaluate_css(@document, 'root a + c')).to eq(node_set)
evaluate_css(@document, 'root a + c').should == node_set
end
end
@ -52,15 +52,15 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing following siblings' do
expect(evaluate_css(@document, 'root a ~ b')).to eq(node_set(@b1, @b2))
evaluate_css(@document, 'root a ~ b').should == node_set(@b1, @b2)
end
it 'returns a node set containing following siblings relative to a node' do
expect(evaluate_css(@b1, '~ b')).to eq(node_set(@b2))
evaluate_css(@b1, '~ b').should == node_set(@b2)
end
it 'returns an empty node set for non matching following siblings' do
expect(evaluate_css(@document, 'root a ~ c')).to eq(node_set)
evaluate_css(@document, 'root a ~ c').should == node_set
end
end
end

View File

@ -5,25 +5,25 @@ describe 'CSS selector evaluation' do
it 'returns a node set containing a node with a single class' do
document = parse('<x class="foo" />')
expect(evaluate_css(document, '.foo')).to eq(document.children)
evaluate_css(document, '.foo').should == document.children
end
it 'returns a node set containing a node having one of two classes' do
document = parse('<x class="foo bar" />')
expect(evaluate_css(document, '.foo')).to eq(document.children)
evaluate_css(document, '.foo').should == document.children
end
it 'returns a node set containing a node having both classes' do
document = parse('<x class="foo bar" />')
expect(evaluate_css(document, '.foo.bar')).to eq(document.children)
evaluate_css(document, '.foo.bar').should == document.children
end
it 'returns an empty node set for non matching classes' do
document = parse('<x class="bar" />')
expect(evaluate_css(document, '.foo')).to eq(node_set)
evaluate_css(document, '.foo').should == node_set
end
end
end

View File

@ -7,11 +7,11 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing a node with a single ID' do
expect(evaluate_css(@document, '#foo')).to eq(@document.children)
evaluate_css(@document, '#foo').should == @document.children
end
it 'returns an empty node set for non matching IDs' do
expect(evaluate_css(@document, '#bar')).to eq(node_set)
evaluate_css(@document, '#bar').should == node_set
end
end
end

View File

@ -8,11 +8,11 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing nodes with matching attributes' do
expect(evaluate_css(@document, 'x[a = "b"]')).to eq(@document.children)
evaluate_css(@document, 'x[a = "b"]').should == @document.children
end
it 'returns an empty node set for non matching attribute values' do
expect(evaluate_css(@document, 'x[a = "c"]')).to eq(node_set)
evaluate_css(@document, 'x[a = "c"]').should == node_set
end
end
@ -20,19 +20,19 @@ describe 'CSS selector evaluation' do
it 'returns a node set containing nodes with matching attributes' do
document = parse('<x a="1 2 3" />')
expect(evaluate_css(document, 'x[a ~= "2"]')).to eq(document.children)
evaluate_css(document, 'x[a ~= "2"]').should == document.children
end
it 'returns a node set containing nodes with single attribute values' do
document = parse('<x a="1" />')
expect(evaluate_css(document, 'x[a ~= "1"]')).to eq(document.children)
evaluate_css(document, 'x[a ~= "1"]').should == document.children
end
it 'returns an empty node set for non matching attributes' do
document = parse('<x a="1 2 3" />')
expect(evaluate_css(document, 'x[a ~= "4"]')).to eq(node_set)
evaluate_css(document, 'x[a ~= "4"]').should == node_set
end
end
@ -42,11 +42,11 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing nodes with matching attributes' do
expect(evaluate_css(@document, 'x[a ^= "fo"]')).to eq(@document.children)
evaluate_css(@document, 'x[a ^= "fo"]').should == @document.children
end
it 'returns an empty node set for non matching attributes' do
expect(evaluate_css(@document, 'x[a ^= "bar"]')).to eq(node_set)
evaluate_css(@document, 'x[a ^= "bar"]').should == node_set
end
end
@ -56,11 +56,11 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing nodes with matching attributes' do
expect(evaluate_css(@document, 'x[a $= "oo"]')).to eq(@document.children)
evaluate_css(@document, 'x[a $= "oo"]').should == @document.children
end
it 'returns an empty node set for non matching attributes' do
expect(evaluate_css(@document, 'x[a $= "x"]')).to eq(node_set)
evaluate_css(@document, 'x[a $= "x"]').should == node_set
end
end
@ -70,11 +70,11 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing nodes with matching attributes' do
expect(evaluate_css(@document, 'x[a *= "o"]')).to eq(@document.children)
evaluate_css(@document, 'x[a *= "o"]').should == @document.children
end
it 'returns an empty node set for non matching attributes' do
expect(evaluate_css(@document, 'x[a *= "x"]')).to eq(node_set)
evaluate_css(@document, 'x[a *= "x"]').should == node_set
end
end
@ -82,19 +82,19 @@ describe 'CSS selector evaluation' do
it 'returns a node set containing nodes with matching attributes' do
document = parse('<x a="foo-bar" />')
expect(evaluate_css(document, 'x[a |= "foo"]')).to eq(document.children)
evaluate_css(document, 'x[a |= "foo"]').should == document.children
end
it 'returns a node set containing nodes with single attribute values' do
document = parse('<x a="foo" />')
expect(evaluate_css(document, 'x[a |= "foo"]')).to eq(document.children)
evaluate_css(document, 'x[a |= "foo"]').should == document.children
end
it 'returns an empty node set for non matching attributes' do
document = parse('<x a="bar" />')
expect(evaluate_css(document, 'x[a |= "foo"]')).to eq(node_set)
evaluate_css(document, 'x[a |= "foo"]').should == node_set
end
end
end

View File

@ -12,23 +12,23 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing the root node' do
expect(evaluate_css(@document, 'a')).to eq(node_set(@a1))
evaluate_css(@document, 'a').should == node_set(@a1)
end
it 'returns a node set containing nested nodes' do
expect(evaluate_css(@document, 'a b')).to eq(node_set(@b1, @b2))
evaluate_css(@document, 'a b').should == node_set(@b1, @b2)
end
it 'returns a node set containing the union of multiple paths' do
expect(evaluate_css(@document, 'b, ns1|c')).to eq(node_set(@b1, @b2, @c1))
evaluate_css(@document, 'b, ns1|c').should == node_set(@b1, @b2, @c1)
end
it 'returns a node set containing namespaced nodes' do
expect(evaluate_css(@document, 'a ns1|c')).to eq(node_set(@c1))
evaluate_css(@document, 'a ns1|c').should == node_set(@c1)
end
it 'returns a node set containing wildcard nodes' do
expect(evaluate_css(@document, 'a *')).to eq(node_set(@b1, @b2, @c1))
evaluate_css(@document, 'a *').should == node_set(@b1, @b2, @c1)
end
it 'returns a node set containing nodes with namespace wildcards' do
@ -36,11 +36,11 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing nodes with a namespace name and name wildcard' do
expect(evaluate_css(@document, 'a ns1|*')).to eq(node_set(@c1))
evaluate_css(@document, 'a ns1|*').should == node_set(@c1)
end
it 'returns a node set containing nodes using full wildcards' do
expect(evaluate_css(@document, 'a *|*')).to eq(node_set(@b1, @b2, @c1))
evaluate_css(@document, 'a *|*').should == node_set(@b1, @b2, @c1)
end
end
end

View File

@ -9,11 +9,11 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing nodes with an attribute' do
expect(evaluate_css(@document, 'root a[class]')).to eq(node_set(@a1))
evaluate_css(@document, 'root a[class]').should == node_set(@a1)
end
it 'returns a node set containing nodes with a matching attribute value' do
expect(evaluate_css(@document, 'root a[class="foo"]')).to eq(node_set(@a1))
evaluate_css(@document, 'root a[class="foo"]').should == node_set(@a1)
end
end
end

View File

@ -10,15 +10,15 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing empty nodes' do
expect(evaluate_css(@document, 'root :empty')).to eq(node_set(@a1))
evaluate_css(@document, 'root :empty').should == node_set(@a1)
end
it 'returns a node set containing empty nodes with a node test' do
expect(evaluate_css(@document, 'root a:empty')).to eq(node_set(@a1))
evaluate_css(@document, 'root a:empty').should == node_set(@a1)
end
it 'returns an empty node set containing non empty nodes' do
expect(evaluate_css(@document, 'root b:empty')).to eq(node_set)
evaluate_css(@document, 'root b:empty').should == node_set
end
end
end

View File

@ -10,15 +10,15 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing the first child node' do
expect(evaluate_css(@document, 'root :first-child')).to eq(node_set(@a1))
evaluate_css(@document, 'root :first-child').should == node_set(@a1)
end
it 'returns a node set containing the first child node with a node test' do
expect(evaluate_css(@document, 'root a:first-child')).to eq(node_set(@a1))
evaluate_css(@document, 'root a:first-child').should == node_set(@a1)
end
it 'returns an empty node set for non first-child nodes' do
expect(evaluate_css(@document, 'root b:first-child')).to eq(node_set)
evaluate_css(@document, 'root b:first-child').should == node_set
end
end
end

View File

@ -18,8 +18,8 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing all first <a> nodes' do
expect(evaluate_css(@document, 'root a:first-of-type'))
.to eq(node_set(@a1, @a3))
evaluate_css(@document, 'root a:first-of-type')
.should == node_set(@a1, @a3)
end
end
end

View File

@ -10,15 +10,15 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing the last child node' do
expect(evaluate_css(@document, 'root :last-child')).to eq(node_set(@b1))
evaluate_css(@document, 'root :last-child').should == node_set(@b1)
end
it 'returns a node set containing the last child node with a node test' do
expect(evaluate_css(@document, 'root b:last-child')).to eq(node_set(@b1))
evaluate_css(@document, 'root b:last-child').should == node_set(@b1)
end
it 'returns an empty node set for non last-child nodes' do
expect(evaluate_css(@document, 'root a:last-child')).to eq(node_set)
evaluate_css(@document, 'root a:last-child').should == node_set
end
end
end

View File

@ -18,8 +18,8 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing all last <a> nodes' do
expect(evaluate_css(@document, 'root a:last-of-type'))
.to eq(node_set(@a2, @a4))
evaluate_css(@document, 'root a:last-of-type')
.should == node_set(@a2, @a4)
end
end
end

View File

@ -13,36 +13,36 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing the first child node' do
expect(evaluate_css(@document, 'root :nth-child(1)')).to eq(node_set(@a1))
evaluate_css(@document, 'root :nth-child(1)').should == node_set(@a1)
end
it 'returns a node set containing even nodes' do
expect(evaluate_css(@document, 'root :nth-child(even)'))
.to eq(node_set(@a2, @a4))
evaluate_css(@document, 'root :nth-child(even)')
.should == node_set(@a2, @a4)
end
it 'returns a node set containing odd nodes' do
expect(evaluate_css(@document, 'root :nth-child(odd)'))
.to eq(node_set(@a1, @a3))
evaluate_css(@document, 'root :nth-child(odd)')
.should == node_set(@a1, @a3)
end
it 'returns a node set containing every 2 nodes starting at node 2' do
expect(evaluate_css(@document, 'root :nth-child(2n+2)'))
.to eq(node_set(@a2, @a4))
evaluate_css(@document, 'root :nth-child(2n+2)')
.should == node_set(@a2, @a4)
end
it 'returns a node set containing all nodes' do
expect(evaluate_css(@document, 'root :nth-child(n)')).to eq(@root.children)
evaluate_css(@document, 'root :nth-child(n)').should == @root.children
end
it 'returns a node set containing the first two nodes' do
expect(evaluate_css(@document, 'root :nth-child(-n+2)'))
.to eq(node_set(@a1, @a2))
evaluate_css(@document, 'root :nth-child(-n+2)')
.should == node_set(@a1, @a2)
end
it 'returns a node set containing all nodes starting at node 2' do
expect(evaluate_css(@document, 'root :nth-child(n+2)'))
.to eq(node_set(@a2, @a3, @a4))
evaluate_css(@document, 'root :nth-child(n+2)')
.should == node_set(@a2, @a3, @a4)
end
end
end

View File

@ -13,37 +13,37 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing the last child node' do
expect(evaluate_css(@document, 'root :nth-last-child(1)')).to eq(node_set(@a4))
evaluate_css(@document, 'root :nth-last-child(1)').should == node_set(@a4)
end
it 'returns a node set containing even nodes' do
expect(evaluate_css(@document, 'root :nth-last-child(even)'))
.to eq(node_set(@a1, @a3))
evaluate_css(@document, 'root :nth-last-child(even)')
.should == node_set(@a1, @a3)
end
it 'returns a node set containing odd nodes' do
expect(evaluate_css(@document, 'root :nth-last-child(odd)'))
.to eq(node_set(@a2, @a4))
evaluate_css(@document, 'root :nth-last-child(odd)')
.should == node_set(@a2, @a4)
end
it 'returns a node set containing every 2 nodes starting at node 3' do
expect(evaluate_css(@document, 'root :nth-last-child(2n+2)'))
.to eq(node_set(@a1, @a3))
evaluate_css(@document, 'root :nth-last-child(2n+2)')
.should == node_set(@a1, @a3)
end
it 'returns a node set containing all nodes' do
expect(evaluate_css(@document, 'root :nth-last-child(n)'))
.to eq(@root.children)
evaluate_css(@document, 'root :nth-last-child(n)')
.should == @root.children
end
it 'returns a node set containing the first two nodes' do
expect(evaluate_css(@document, 'root :nth-last-child(-n+2)'))
.to eq(node_set(@a3, @a4))
evaluate_css(@document, 'root :nth-last-child(-n+2)')
.should == node_set(@a3, @a4)
end
it 'returns a node set containing all nodes starting at node 2' do
expect(evaluate_css(@document, 'root :nth-last-child(n+2)'))
.to eq(node_set(@a1, @a2, @a3))
evaluate_css(@document, 'root :nth-last-child(n+2)')
.should == node_set(@a1, @a2, @a3)
end
end
end

View File

@ -22,38 +22,38 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing the first child node' do
expect(evaluate_css(@document, 'root a:nth-last-of-type(1)'))
.to eq(node_set(@a3, @a4))
evaluate_css(@document, 'root a:nth-last-of-type(1)')
.should == node_set(@a3, @a4)
end
it 'returns a node set containing even nodes' do
expect(evaluate_css(@document, 'root a:nth-last-of-type(even)'))
.to eq(node_set(@a2))
evaluate_css(@document, 'root a:nth-last-of-type(even)')
.should == node_set(@a2)
end
it 'returns a node set containing odd nodes' do
expect(evaluate_css(@document, 'root a:nth-last-of-type(odd)'))
.to eq(node_set(@a1, @a3, @a4))
evaluate_css(@document, 'root a:nth-last-of-type(odd)')
.should == node_set(@a1, @a3, @a4)
end
it 'returns a node set containing every 2 nodes starting at node 2' do
expect(evaluate_css(@document, 'root a:nth-last-of-type(2n+2)'))
.to eq(node_set(@a2))
evaluate_css(@document, 'root a:nth-last-of-type(2n+2)')
.should == node_set(@a2)
end
it 'returns a node set containing all nodes' do
expect(evaluate_css(@document, 'root a:nth-last-of-type(n)'))
.to eq(node_set(@a1, @a2, @a3, @a4))
evaluate_css(@document, 'root a:nth-last-of-type(n)')
.should == node_set(@a1, @a2, @a3, @a4)
end
it 'returns a node set containing the first two nodes' do
expect(evaluate_css(@document, 'root a:nth-last-of-type(-n+2)'))
.to eq(node_set(@a2, @a3, @a4))
evaluate_css(@document, 'root a:nth-last-of-type(-n+2)')
.should == node_set(@a2, @a3, @a4)
end
it 'returns a node set containing all nodes starting at node 2' do
expect(evaluate_css(@document, 'root a:nth-last-of-type(n+2)'))
.to eq(node_set(@a1, @a2))
evaluate_css(@document, 'root a:nth-last-of-type(n+2)')
.should == node_set(@a1, @a2)
end
end
end

View File

@ -22,38 +22,38 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing the first child node' do
expect(evaluate_css(@document, 'root a:nth-of-type(1)'))
.to eq(node_set(@a1, @a4))
evaluate_css(@document, 'root a:nth-of-type(1)')
.should == node_set(@a1, @a4)
end
it 'returns a node set containing even nodes' do
expect(evaluate_css(@document, 'root a:nth-of-type(even)'))
.to eq(node_set(@a2))
evaluate_css(@document, 'root a:nth-of-type(even)')
.should == node_set(@a2)
end
it 'returns a node set containing odd nodes' do
expect(evaluate_css(@document, 'root a:nth-of-type(odd)'))
.to eq(node_set(@a1, @a3, @a4))
evaluate_css(@document, 'root a:nth-of-type(odd)')
.should == node_set(@a1, @a3, @a4)
end
it 'returns a node set containing every 2 nodes starting at node 2' do
expect(evaluate_css(@document, 'root a:nth-of-type(2n+2)'))
.to eq(node_set(@a2))
evaluate_css(@document, 'root a:nth-of-type(2n+2)')
.should == node_set(@a2)
end
it 'returns a node set containing all nodes' do
expect(evaluate_css(@document, 'root a:nth-of-type(n)'))
.to eq(node_set(@a1, @a2, @a3, @a4))
evaluate_css(@document, 'root a:nth-of-type(n)')
.should == node_set(@a1, @a2, @a3, @a4)
end
it 'returns a node set containing the first two nodes' do
expect(evaluate_css(@document, 'root a:nth-of-type(-n+2)'))
.to eq(node_set(@a1, @a2, @a4))
evaluate_css(@document, 'root a:nth-of-type(-n+2)')
.should == node_set(@a1, @a2, @a4)
end
it 'returns a node set containing all nodes starting at node 2' do
expect(evaluate_css(@document, 'root a:nth-of-type(n+2)'))
.to eq(node_set(@a2, @a3))
evaluate_css(@document, 'root a:nth-of-type(n+2)')
.should == node_set(@a2, @a3)
end
end
end

View File

@ -11,11 +11,11 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing the first <a> node' do
expect(evaluate_css(@document, 'root a:nth(1)')).to eq(node_set(@a1))
evaluate_css(@document, 'root a:nth(1)').should == node_set(@a1)
end
it 'returns a node set containing the second <a> node' do
expect(evaluate_css(@document, 'root a:nth(2)')).to eq(node_set(@a2))
evaluate_css(@document, 'root a:nth(2)').should == node_set(@a2)
end
end
end

View File

@ -11,7 +11,7 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing <c> nodes' do
expect(evaluate_css(@document, 'root :only-child')).to eq(node_set(@c1, @c2))
evaluate_css(@document, 'root :only-child').should == node_set(@c1, @c2)
end
end
end

View File

@ -11,7 +11,7 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing <c> nodes' do
expect(evaluate_css(@document, 'root a :only-of-type')).to eq(node_set(@c1))
evaluate_css(@document, 'root a :only-of-type').should == node_set(@c1)
end
end
end

View File

@ -7,7 +7,7 @@ describe 'CSS selector evaluation' do
end
it 'returns a node set containing the root node' do
expect(evaluate_css(@document, ':root')).to eq(@document.children)
evaluate_css(@document, ':root').should == @document.children
end
end
end

View File

@ -3,63 +3,63 @@ require 'spec_helper'
describe Oga::CSS::Lexer do
describe 'axes' do
it 'lexes the > axis' do
expect(lex_css('>')).to eq([[:T_GREATER, nil]])
lex_css('>').should == [[:T_GREATER, nil]]
end
it 'lexes the expression "> y"' do
expect(lex_css('> y')).to eq([[:T_GREATER, nil], [:T_IDENT, 'y']])
lex_css('> y').should == [[:T_GREATER, nil], [:T_IDENT, 'y']]
end
it 'lexes the expression "x > y"' do
expect(lex_css('x > y')).to eq([
lex_css('x > y').should == [
[:T_IDENT, 'x'],
[:T_GREATER, nil],
[:T_IDENT, 'y']
])
]
end
it 'lexes the expression "x>y"' do
expect(lex_css('x>y')).to eq(lex_css('x > y'))
lex_css('x>y').should == lex_css('x > y')
end
it 'lexes the + axis' do
expect(lex_css('+')).to eq([[:T_PLUS, nil]])
lex_css('+').should == [[:T_PLUS, nil]]
end
it 'lexes the expression "+ y"' do
expect(lex_css('+ y')).to eq([[:T_PLUS, nil], [:T_IDENT, 'y']])
lex_css('+ y').should == [[:T_PLUS, nil], [:T_IDENT, 'y']]
end
it 'lexes the expression "x + y"' do
expect(lex_css('x + y')).to eq([
lex_css('x + y').should == [
[:T_IDENT, 'x'],
[:T_PLUS, nil],
[:T_IDENT, 'y']
])
]
end
it 'lexes the expression "x+y"' do
expect(lex_css('x+y')).to eq(lex_css('x + y'))
lex_css('x+y').should == lex_css('x + y')
end
it 'lexes the ~ axis' do
expect(lex_css('~')).to eq([[:T_TILDE, nil]])
lex_css('~').should == [[:T_TILDE, nil]]
end
it 'lexes the expression "~ y"' do
expect(lex_css('~ y')).to eq([[:T_TILDE, nil], [:T_IDENT, 'y']])
lex_css('~ y').should == [[:T_TILDE, nil], [:T_IDENT, 'y']]
end
it 'lexes the expression "x ~ y"' do
expect(lex_css('x ~ y')).to eq([
lex_css('x ~ y').should == [
[:T_IDENT, 'x'],
[:T_TILDE, nil],
[:T_IDENT, 'y']
])
]
end
it 'lexes the expression "x~y"' do
expect(lex_css('x~y')).to eq(lex_css('x ~ y'))
lex_css('x~y').should == lex_css('x ~ y')
end
end
end

View File

@ -3,19 +3,19 @@ require 'spec_helper'
describe Oga::CSS::Lexer do
describe 'namespaces' do
it 'lexes a path containing a namespace name' do
expect(lex_css('foo|bar')).to eq([
lex_css('foo|bar').should == [
[:T_IDENT, 'foo'],
[:T_PIPE, nil],
[:T_IDENT, 'bar']
])
]
end
it 'lexes a path containing a namespace wildcard' do
expect(lex_css('*|foo')).to eq([
lex_css('*|foo').should == [
[:T_IDENT, '*'],
[:T_PIPE, nil],
[:T_IDENT, 'foo']
])
]
end
end
end

View File

@ -3,87 +3,87 @@ require 'spec_helper'
describe Oga::CSS::Lexer do
describe 'operators' do
it 'lexes the = operator' do
expect(lex_css('[foo="bar"]')).to eq([
lex_css('[foo="bar"]').should == [
[:T_LBRACK, nil],
[:T_IDENT, 'foo'],
[:T_EQ, nil],
[:T_STRING, 'bar'],
[:T_RBRACK, nil]
])
]
end
it 'lexes the ~= operator' do
expect(lex_css('[foo~="bar"]')).to eq([
lex_css('[foo~="bar"]').should == [
[:T_LBRACK, nil],
[:T_IDENT, 'foo'],
[:T_SPACE_IN, nil],
[:T_STRING, 'bar'],
[:T_RBRACK, nil]
])
]
end
it 'lexes the ^= operator' do
expect(lex_css('[foo^="bar"]')).to eq([
lex_css('[foo^="bar"]').should == [
[:T_LBRACK, nil],
[:T_IDENT, 'foo'],
[:T_STARTS_WITH, nil],
[:T_STRING, 'bar'],
[:T_RBRACK, nil]
])
]
end
it 'lexes the $= operator' do
expect(lex_css('[foo$="bar"]')).to eq([
lex_css('[foo$="bar"]').should == [
[:T_LBRACK, nil],
[:T_IDENT, 'foo'],
[:T_ENDS_WITH, nil],
[:T_STRING, 'bar'],
[:T_RBRACK, nil],
])
]
end
it 'lexes the *= operator' do
expect(lex_css('[foo*="bar"]')).to eq([
lex_css('[foo*="bar"]').should == [
[:T_LBRACK, nil],
[:T_IDENT, 'foo'],
[:T_IN, nil],
[:T_STRING, 'bar'],
[:T_RBRACK, nil]
])
]
end
it 'lexes the |= operator' do
expect(lex_css('[foo|="bar"]')).to eq([
lex_css('[foo|="bar"]').should == [
[:T_LBRACK, nil],
[:T_IDENT, 'foo'],
[:T_HYPHEN_IN, nil],
[:T_STRING, 'bar'],
[:T_RBRACK, nil]
])
]
end
it 'lexes the = operator surrounded by whitespace' do
expect(lex_css('[foo = "bar"]')).to eq(lex_css('[foo="bar"]'))
lex_css('[foo = "bar"]').should == lex_css('[foo="bar"]')
end
it 'lexes the ~= operator surrounded by whitespace' do
expect(lex_css('[foo ~= "bar"]')).to eq(lex_css('[foo~="bar"]'))
lex_css('[foo ~= "bar"]').should == lex_css('[foo~="bar"]')
end
it 'lexes the ^= operator surrounded by whitespace' do
expect(lex_css('[foo ^= "bar"]')).to eq(lex_css('[foo^="bar"]'))
lex_css('[foo ^= "bar"]').should == lex_css('[foo^="bar"]')
end
it 'lexes the $= operator surrounded by whitespace' do
expect(lex_css('[foo $= "bar"]')).to eq(lex_css('[foo$="bar"]'))
lex_css('[foo $= "bar"]').should == lex_css('[foo$="bar"]')
end
it 'lexes the *= operator surrounded by whitespace' do
expect(lex_css('[foo *= "bar"]')).to eq(lex_css('[foo*="bar"]'))
lex_css('[foo *= "bar"]').should == lex_css('[foo*="bar"]')
end
it 'lexes the |= operator surrounded by whitespace' do
expect(lex_css('[foo |= "bar"]')).to eq(lex_css('[foo|="bar"]'))
lex_css('[foo |= "bar"]').should == lex_css('[foo|="bar"]')
end
end
end

View File

@ -5,73 +5,73 @@ require 'spec_helper'
describe Oga::CSS::Lexer do
describe 'paths' do
it 'lexes a simple path' do
expect(lex_css('h3')).to eq([[:T_IDENT, 'h3']])
lex_css('h3').should == [[:T_IDENT, 'h3']]
end
it 'lexes a path with Unicode characters' do
expect(lex_css('áâã')).to eq([[:T_IDENT, 'áâã']])
lex_css('áâã').should == [[:T_IDENT, 'áâã']]
end
it 'lexes a path with Unicode and ASCII characters' do
expect(lex_css('áâãfoo')).to eq([[:T_IDENT, 'áâãfoo']])
lex_css('áâãfoo').should == [[:T_IDENT, 'áâãfoo']]
end
it 'lexes a simple path starting with an underscore' do
expect(lex_css('_h3')).to eq([[:T_IDENT, '_h3']])
lex_css('_h3').should == [[:T_IDENT, '_h3']]
end
it 'lexes a path with an escaped identifier' do
expect(lex_css('foo\.bar\.baz')).to eq([[:T_IDENT, 'foo.bar.baz']])
lex_css('foo\.bar\.baz').should == [[:T_IDENT, 'foo.bar.baz']]
end
it 'lexes a path with an escaped identifier followed by another identifier' do
expect(lex_css('foo\.bar baz')).to eq([
lex_css('foo\.bar baz').should == [
[:T_IDENT, 'foo.bar'],
[:T_SPACE, nil],
[:T_IDENT, 'baz']
])
]
end
it 'lexes a path with two members' do
expect(lex_css('div h3')).to eq([
lex_css('div h3').should == [
[:T_IDENT, 'div'],
[:T_SPACE, nil],
[:T_IDENT, 'h3']
])
]
end
it 'lexes a path with two members separated by multiple spaces' do
expect(lex_css('div h3')).to eq([
lex_css('div h3').should == [
[:T_IDENT, 'div'],
[:T_SPACE, nil],
[:T_IDENT, 'h3']
])
]
end
it 'lexes two paths' do
expect(lex_css('foo, bar')).to eq([
lex_css('foo, bar').should == [
[:T_IDENT, 'foo'],
[:T_COMMA, nil],
[:T_IDENT, 'bar']
])
]
end
it 'lexes a path selecting an ID' do
expect(lex_css('#foo')).to eq([
lex_css('#foo').should == [
[:T_HASH, nil],
[:T_IDENT, 'foo']
])
]
end
it 'lexes a path selecting a class' do
expect(lex_css('.foo')).to eq([
lex_css('.foo').should == [
[:T_DOT, nil],
[:T_IDENT, 'foo']
])
]
end
it 'lexes a wildcard path' do
expect(lex_css('*')).to eq([[:T_IDENT, '*']])
lex_css('*').should == [[:T_IDENT, '*']]
end
end
end

View File

@ -3,12 +3,12 @@ require 'spec_helper'
describe Oga::CSS::Lexer do
describe 'predicates' do
it 'lexes a path containing a simple predicate' do
expect(lex_css('foo[bar]')).to eq([
lex_css('foo[bar]').should == [
[:T_IDENT, 'foo'],
[:T_LBRACK, nil],
[:T_IDENT, 'bar'],
[:T_RBRACK, nil]
])
]
end
end
end

View File

@ -3,86 +3,86 @@ require 'spec_helper'
describe Oga::CSS::Lexer do
describe 'pseudo classes' do
it 'lexes the :root pseudo class' do
expect(lex_css(':root')).to eq([
lex_css(':root').should == [
[:T_COLON, nil],
[:T_IDENT, 'root']
])
]
end
it 'lexes the :nth-child pseudo class' do
expect(lex_css(':nth-child(1)')).to eq([
lex_css(':nth-child(1)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
[:T_INT, 1],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :nth-child pseudo class with extra whitespace' do
expect(lex_css(':nth-child( 1)')).to eq([
lex_css(':nth-child( 1)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
[:T_INT, 1],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :nth-child(odd) pseudo class' do
expect(lex_css(':nth-child(odd)')).to eq([
lex_css(':nth-child(odd)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
[:T_ODD, nil],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :nth-child(even) pseudo class' do
expect(lex_css(':nth-child(even)')).to eq([
lex_css(':nth-child(even)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
[:T_EVEN, nil],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :nth-child(n) pseudo class' do
expect(lex_css(':nth-child(n)')).to eq([
lex_css(':nth-child(n)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
[:T_NTH, nil],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :nth-child(-n) pseudo class' do
expect(lex_css(':nth-child(-n)')).to eq([
lex_css(':nth-child(-n)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
[:T_MINUS, nil],
[:T_NTH, nil],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :nth-child(2n) pseudo class' do
expect(lex_css(':nth-child(2n)')).to eq([
lex_css(':nth-child(2n)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
[:T_INT, 2],
[:T_NTH, nil],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :nth-child(2n+1) pseudo class' do
expect(lex_css(':nth-child(2n+1)')).to eq([
lex_css(':nth-child(2n+1)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
@ -90,11 +90,11 @@ describe Oga::CSS::Lexer do
[:T_NTH, nil],
[:T_INT, 1],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :nth-child(2n-1) pseudo class' do
expect(lex_css(':nth-child(2n-1)')).to eq([
lex_css(':nth-child(2n-1)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
@ -102,11 +102,11 @@ describe Oga::CSS::Lexer do
[:T_NTH, nil],
[:T_INT, -1],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :nth-child(-2n-1) pseudo class' do
expect(lex_css(':nth-child(-2n-1)')).to eq([
lex_css(':nth-child(-2n-1)').should == [
[:T_COLON, nil],
[:T_IDENT, 'nth-child'],
[:T_LPAREN, nil],
@ -114,28 +114,28 @@ describe Oga::CSS::Lexer do
[:T_NTH, nil],
[:T_INT, -1],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :lang(fr) pseudo class' do
expect(lex_css(':lang(fr)')).to eq([
lex_css(':lang(fr)').should == [
[:T_COLON, nil],
[:T_IDENT, 'lang'],
[:T_LPAREN, nil],
[:T_IDENT, 'fr'],
[:T_RPAREN, nil]
])
]
end
it 'lexes the :not(#foo) pseudo class' do
expect(lex_css(':not(#foo)')).to eq([
lex_css(':not(#foo)').should == [
[:T_COLON, nil],
[:T_IDENT, 'not'],
[:T_LPAREN, nil],
[:T_HASH, nil],
[:T_IDENT, 'foo'],
[:T_RPAREN, nil]
])
]
end
end
end

View File

@ -3,19 +3,19 @@ require 'spec_helper'
describe Oga::CSS::Lexer do
describe 'strings' do
it 'lexes a single quoted string' do
expect(lex_css("['foo']")).to eq([
lex_css("['foo']").should == [
[:T_LBRACK, nil],
[:T_STRING, 'foo'],
[:T_RBRACK, nil]
])
]
end
it 'lexes a double quoted string' do
expect(lex_css('["foo"]')).to eq([
lex_css('["foo"]').should == [
[:T_LBRACK, nil],
[:T_STRING, 'foo'],
[:T_RBRACK, nil]
])
]
end
end
end

View File

@ -1,7 +0,0 @@
require 'spec_helper'
describe Oga::CSS::Lexer do
it 'ignores leading and trailing whitespace' do
expect(lex_css(' foo ')).to eq([[:T_IDENT, 'foo']])
end
end

View File

@ -3,72 +3,72 @@ require 'spec_helper'
describe Oga::CSS::Parser do
describe 'axes' do
it 'parses the > axis' do
expect(parse_css('x > y')).to eq(parse_xpath('descendant::x/y'))
parse_css('x > y').should == parse_xpath('descendant::x/y')
end
it 'parses the > axis without whitespace' do
expect(parse_css('x>y')).to eq(parse_css('x > y'))
parse_css('x>y').should == parse_css('x > y')
end
it 'parses the > axis called on another > axis' do
expect(parse_css('a > b > c')).to eq(parse_xpath('descendant::a/b/c'))
parse_css('a > b > c').should == parse_xpath('descendant::a/b/c')
end
it 'parses an > axis followed by an element with an ID' do
expect(parse_css('x > foo#bar')).to eq(parse_xpath(
parse_css('x > foo#bar').should == parse_xpath(
'descendant::x/foo[@id="bar"]'
))
)
end
it 'parses an > axis followed by an element with a class' do
expect(parse_css('x > foo.bar')).to eq(parse_xpath(
parse_css('x > foo.bar').should == parse_xpath(
'descendant::x/foo[contains(concat(" ", @class, " "), " bar ")]'
))
)
end
it 'parses the + axis' do
expect(parse_css('x + y')).to eq(parse_xpath(
parse_css('x + y').should == parse_xpath(
'descendant::x/following-sibling::*[1]/self::y'
))
)
end
it 'parses the + axis without whitespace' do
expect(parse_css('x+y')).to eq(parse_css('x + y'))
parse_css('x+y').should == parse_css('x + y')
end
it 'parses the + axis with an identifier only at the right-hand side' do
expect(parse_css('+ y')).to eq(parse_xpath(
parse_css('+ y').should == parse_xpath(
'following-sibling::*[1]/self::y'
))
)
end
it 'parses the + axis called on another + axis' do
expect(parse_css('a + b + c')).to eq(parse_xpath(
parse_css('a + b + c').should == parse_xpath(
'descendant::a/following-sibling::*[1]/self::b/' \
'following-sibling::*[1]/self::c'
))
)
end
it 'parses the ~ axis' do
expect(parse_css('x ~ y')).to eq(parse_xpath(
parse_css('x ~ y').should == parse_xpath(
'descendant::x/following-sibling::y'
))
)
end
it 'parses the ~ axis without whitespace' do
expect(parse_css('x~y')).to eq(parse_css('x ~ y'))
parse_css('x~y').should == parse_css('x ~ y')
end
it 'parses the ~ axis followed by another node test' do
expect(parse_css('x ~ y z')).to eq(parse_xpath(
parse_css('x ~ y z').should == parse_xpath(
'descendant::x/following-sibling::y/descendant::z'
))
)
end
it 'parses the ~ axis called on another ~ axis' do
expect(parse_css('a ~ b ~ c')).to eq(parse_xpath(
parse_css('a ~ b ~ c').should == parse_xpath(
'descendant::a/following-sibling::b/following-sibling::c'
))
)
end
end
end

View File

@ -7,21 +7,21 @@ describe Oga::CSS::Parser do
end
it 'parses an expression' do
expect(described_class.parse_with_cache('foo'))
.to eq(s(:axis, 'descendant', s(:test, nil, 'foo')))
described_class.parse_with_cache('foo')
.should == s(:axis, 'descendant', s(:test, nil, 'foo'))
end
it 'caches an expression after parsing it' do
expect_any_instance_of(described_class)
.to receive(:parse)
described_class.any_instance
.should_receive(:parse)
.once
.and_call_original
expect(described_class.parse_with_cache('foo'))
.to eq(s(:axis, 'descendant', s(:test, nil, 'foo')))
described_class.parse_with_cache('foo')
.should == s(:axis, 'descendant', s(:test, nil, 'foo'))
expect(described_class.parse_with_cache('foo'))
.to eq(s(:axis, 'descendant', s(:test, nil, 'foo')))
described_class.parse_with_cache('foo')
.should == s(:axis, 'descendant', s(:test, nil, 'foo'))
end
end
end

View File

@ -3,29 +3,29 @@ require 'spec_helper'
describe Oga::CSS::Parser do
describe 'classes' do
it 'parses a class selector' do
expect(parse_css('.foo')).to eq(parse_xpath(
parse_css('.foo').should == parse_xpath(
'descendant::*[contains(concat(" ", @class, " "), " foo ")]'
))
)
end
it 'parses a selector for an element with a class' do
expect(parse_css('foo.bar')).to eq(parse_xpath(
parse_css('foo.bar').should == parse_xpath(
'descendant::foo[contains(concat(" ", @class, " "), " bar ")]'
))
)
end
it 'parses a selector using multiple classes' do
expect(parse_css('.foo.bar')).to eq(parse_xpath(
parse_css('.foo.bar').should == parse_xpath(
'descendant::*[contains(concat(" ", @class, " "), " foo ") ' \
'and contains(concat(" ", @class, " "), " bar ")]'
))
)
end
it 'parses a selector using a class and an ID' do
expect(parse_css('#foo.bar')).to eq(parse_xpath(
parse_css('#foo.bar').should == parse_xpath(
'descendant::*[@id="foo" and ' \
'contains(concat(" ", @class, " "), " bar ")]'
))
)
end
end
end

View File

@ -3,24 +3,24 @@ require 'spec_helper'
describe Oga::CSS::Parser do
describe 'IDs' do
it 'parses an ID selector' do
expect(parse_css('#foo')).to eq(parse_xpath('descendant::*[@id="foo"]'))
parse_css('#foo').should == parse_xpath('descendant::*[@id="foo"]')
end
it 'parses a selector for an element with an ID' do
expect(parse_css('foo#bar')).to eq(parse_xpath('descendant::foo[@id="bar"]'))
parse_css('foo#bar').should == parse_xpath('descendant::foo[@id="bar"]')
end
it 'parses a selector using multiple IDs' do
expect(parse_css('#foo#bar')).to eq(parse_xpath(
parse_css('#foo#bar').should == parse_xpath(
'descendant::*[@id="foo" and @id="bar"]'
))
)
end
it 'parses a selector using an ID and a class' do
expect(parse_css('.foo#bar')).to eq(parse_xpath(
parse_css('.foo#bar').should == parse_xpath(
'descendant::*[contains(concat(" ", @class, " "), " foo ") ' \
'and @id="bar"]'
))
)
end
end
end

Some files were not shown because too many files have changed in this diff Show More