Skip to content

Commit

Permalink
Nokogiri::XML::Schema.read_memory() support keyword arguments (#3333)
Browse files Browse the repository at this point in the history
**What problem is this PR intended to solve?**

Related to #3323,
introducing keyword argument support in
Nokogiri::XML::Schema.read_memory() and argument forwarding in
Nokogiri::XML::Schema{,.new}().

**Have you included adequate test coverage?**

Some minor test coverage mimicking the existing permutations of options.

**Does this change affect the behavior of either the C or the Java
implementations?**

No
  • Loading branch information
flavorjones authored Dec 6, 2024
2 parents e548e80 + a6aa11c commit 0fffe34
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 86 deletions.
9 changes: 6 additions & 3 deletions ext/nokogiri/xml_relax_ng.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,12 @@ _noko_xml_relax_ng_parse_schema(
* from_document(document) → Nokogiri::XML::RelaxNG
* from_document(document, parse_options) → Nokogiri::XML::RelaxNG
*
* Parse a RELAX NG schema definition from a Document to create a new Schema.
* Parse a RELAX NG schema definition from a Document to create a new Nokogiri::XML::RelaxNG.
*
* [Parameters]
* - +document+ (XML::Document) RELAX NG schema definition
* - +parse_options+ (Nokogiri::XML::ParseOptions) ⚠ Unused
* - +document+ (XML::Document) A document containing the RELAX NG schema definition
* - +parse_options+ (Nokogiri::XML::ParseOptions)
* Defaults to ParseOptions::DEFAULT_SCHEMA ⚠ Unused
*
* [Returns] Nokogiri::XML::RelaxNG
*
Expand All @@ -119,6 +120,8 @@ _noko_xml_relax_ng_parse_schema(
static VALUE
noko_xml_relax_ng_s_from_document(int argc, VALUE *argv, VALUE rb_class)
{
/* TODO: deprecate this method and put file-or-string logic into .new so that becomes the
* preferred entry point, and this can become a private method */
VALUE rb_document;
VALUE rb_parse_options;
xmlDocPtr c_document;
Expand Down
10 changes: 6 additions & 4 deletions ext/nokogiri/xml_schema.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,13 @@ xml_schema_parse_schema(

/*
* :call-seq:
* from_document(document) → Nokogiri::XML::Schema
* from_document(document, parse_options) → Nokogiri::XML::Schema
* from_document(input) → Nokogiri::XML::Schema
* from_document(input, parse_options) → Nokogiri::XML::Schema
*
* Create a Schema from an already-parsed XSD schema definition document.
* Parse an \XSD schema definition from a Document to create a new Nokogiri::XML::Schema
*
* [Parameters]
* - +document+ (XML::Document) A document object representing the parsed XSD
* - +input+ (XML::Document) A document containing the \XSD schema definition
* - +parse_options+ (Nokogiri::XML::ParseOptions)
* Defaults to Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA
*
Expand All @@ -169,6 +169,8 @@ xml_schema_parse_schema(
static VALUE
noko_xml_schema_s_from_document(int argc, VALUE *argv, VALUE rb_class)
{
/* TODO: deprecate this method and put file-or-string logic into .new so that becomes the
* preferred entry point, and this can become a private method */
VALUE rb_document;
VALUE rb_parse_options;
VALUE rb_schema;
Expand Down
40 changes: 24 additions & 16 deletions lib/nokogiri/xml/relax_ng.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class << self
# RelaxNG(input) → Nokogiri::XML::RelaxNG
# RelaxNG(input, options:) → Nokogiri::XML::RelaxNG
#
# Convenience method for calling Nokogiri::XML::RelaxNG.read_memory
# Convenience method for Nokogiri::XML::RelaxNG.new
def RelaxNG(...)
RelaxNG.new(...)
end
Expand All @@ -22,46 +22,54 @@ def RelaxNG(...)
#
# *Example:* Determine whether an \XML document is valid.
#
# schema = Nokogiri::XML::RelaxNG.read_memory(File.read(RELAX_NG_FILE))
# schema = Nokogiri::XML::RelaxNG.new(File.read(RELAX_NG_FILE))
# doc = Nokogiri::XML::Document.parse(File.read(XML_FILE))
# schema.valid?(doc) # Boolean
#
# *Example:* Validate an \XML document against a \RelaxNG schema, and capture any errors that are found.
#
# schema = Nokogiri::XML::RelaxNG.read_memory(File.open(RELAX_NG_FILE))
# schema = Nokogiri::XML::RelaxNG.new(File.open(RELAX_NG_FILE))
# doc = Nokogiri::XML::Document.parse(File.open(XML_FILE))
# errors = schema.validate(doc) # Array<SyntaxError>
#
# *Example:* Validate an \XML document using a Document containing a RELAX NG schema definition.
#
# schema_doc = Nokogiri::XML::Document.parse(File.read(RELAX_NG_FILE))
# schema = Nokogiri::XML::RelaxNG.from_document(schema_doc)
# doc = Nokogiri::XML::Document.parse(File.open(XML_FILE))
# schema.valid?(doc) # Boolean
#
class RelaxNG < Nokogiri::XML::Schema
# :call-seq:
# new(input) → Nokogiri::XML::RelaxNG
# new(input, options:) → Nokogiri::XML::RelaxNG
#
# Convenience method for calling Nokogiri::XML::RelaxNG.read_memory
def self.new(...)
read_memory(...)
end

# :call-seq:
# read_memory(input) → Nokogiri::XML::RelaxNG
# read_memory(input, options:) → Nokogiri::XML::RelaxNG
#
# Parse a RELAX NG schema definition from a String to create a new Schema.
# Parse a RELAX NG schema definition from a String or IO to create a new Nokogiri::XML::RelaxNG.
#
# [Parameters]
# - +input+ (String) RELAX NG schema definition
# - +input+ (String | IO) RELAX NG schema definition
# - +options:+ (Nokogiri::XML::ParseOptions)
# Defaults to ParseOptions::DEFAULT_SCHEMA ⚠ Unused
# Defaults to Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA ⚠ Unused
#
# [Returns] Nokogiri::XML::RelaxNG
#
# ⚠ +parse_options+ is currently unused by this method and is present only as a placeholder for
# future functionality.
#
# Also see convenience method Nokogiri::XML::RelaxNG()
def self.read_memory(input, parse_options_ = ParseOptions::DEFAULT_SCHEMA, options: parse_options_)
def self.new(input, parse_options_ = ParseOptions::DEFAULT_SCHEMA, options: parse_options_)
from_document(Nokogiri::XML::Document.parse(input), options)
end

# :call-seq:
# read_memory(input) → Nokogiri::XML::RelaxNG
# read_memory(input, options:) → Nokogiri::XML::RelaxNG
#
# Convenience method for Nokogiri::XML::RelaxNG.new.
def self.read_memory(...)
# TODO deprecate this method
new(...)
end
end
end
end
98 changes: 43 additions & 55 deletions lib/nokogiri/xml/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,43 @@ class << self
# Schema(input) → Nokogiri::XML::Schema
# Schema(input, parse_options) → Nokogiri::XML::Schema
#
# Parse an XSD schema definition and create a new {Schema} object. This is a convenience
# method for Nokogiri::XML::Schema.new
#
# See related: Nokogiri::XML::Schema.new
#
# [Parameters]
# - +input+ (String, IO) XSD schema definition
# - +parse_options+ (Nokogiri::XML::ParseOptions)
# [Returns] Nokogiri::XML::Schema
#
def Schema(input, parse_options = ParseOptions::DEFAULT_SCHEMA)
Schema.new(input, parse_options)
# Convenience method for Nokogiri::XML::Schema.new
def Schema(...)
Schema.new(...)
end
end

# Nokogiri::XML::Schema is used for validating XML against an XSD schema definition.
# Nokogiri::XML::Schema is used for validating \XML against an \XSD schema definition.
#
# *Example:* Determine whether an XML document is valid.
# ⚠ Since v1.11.0, Schema treats inputs as *untrusted* by default, and so external entities are
# not resolved from the network (+http://+ or +ftp://+). When parsing a trusted document, the
# caller may turn off the +NONET+ option via the ParseOptions to (re-)enable external entity
# resolution over a network connection.
#
# 🛡 Before v1.11.0, documents were "trusted" by default during schema parsing which was counter
# to Nokogiri's "untrusted by default" security policy.
#
# schema = Nokogiri::XML::Schema(File.read(XSD_FILE))
# doc = Nokogiri::XML(File.read(XML_FILE))
# *Example:* Determine whether an \XML document is valid.
#
# schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
# doc = Nokogiri::XML::Document.parse(File.read(XML_FILE))
# schema.valid?(doc) # Boolean
#
# *Example:* Validate an XML document against a Schema, and capture any errors that are found.
# *Example:* Validate an \XML document against an \XSD schema, and capture any errors that are found.
#
# schema = Nokogiri::XML::Schema(File.read(XSD_FILE))
# doc = Nokogiri::XML(File.read(XML_FILE))
# schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
# doc = Nokogiri::XML::Document.parse(File.read(XML_FILE))
# errors = schema.validate(doc) # Array<SyntaxError>
#
# ⚠ As of v1.11.0, Schema treats inputs as *untrusted* by default, and so external entities are
# not resolved from the network (+http://+ or +ftp://+). When parsing a trusted document, the
# caller may turn off the +NONET+ option via the ParseOptions to (re-)enable external entity
# resolution over a network connection.
# *Example:* Validate an \XML document using a Document containing an \XSD schema definition.
#
# schema_doc = Nokogiri::XML::Document.parse(File.read(RELAX_NG_FILE))
# schema = Nokogiri::XML::Schema.from_document(schema_doc)
# doc = Nokogiri::XML::Document.parse(File.read(XML_FILE))
# schema.valid?(doc) # Boolean
#
# Previously, documents were "trusted" by default during schema parsing which was counter to
# Nokogiri's "untrusted by default" security policy.
class Schema
# The errors found while parsing the XSD
# The errors found while parsing the \XSD
#
# [Returns] Array<Nokogiri::XML::SyntaxError>
attr_accessor :errors
Expand All @@ -58,36 +57,27 @@ class Schema
# new(input) → Nokogiri::XML::Schema
# new(input, parse_options) → Nokogiri::XML::Schema
#
# Parse an XSD schema definition and create a new Nokogiri::XML:Schema object.
# Parse an \XSD schema definition from a String or IO to create a new Nokogiri::XML::Schema
#
# [Parameters]
# - +input+ (String, IO) XSD schema definition
# - +input+ (String | IO) \XSD schema definition
# - +parse_options+ (Nokogiri::XML::ParseOptions)
# Defaults to Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA
#
# [Returns] Nokogiri::XML::Schema
#
def self.new(input, parse_options = ParseOptions::DEFAULT_SCHEMA)
read_memory(input, parse_options)
def self.new(input, parse_options_ = ParseOptions::DEFAULT_SCHEMA, parse_options: parse_options_)
from_document(Nokogiri::XML::Document.parse(input), parse_options)
end

# :call-seq:
# read_memory(input) → Nokogiri::XML::Schema
# read_memory(input, parse_options) → Nokogiri::XML::Schema
#
# Parse an XSD schema definition and create a new Schema object.
#
# 💡 Note that the limitation of this method relative to Schema.new is that +input+ must be type
# String, whereas Schema.new also supports IO types.
#
# [parameters]
# - +input+ (String) XSD schema definition
# - +parse_options+ (Nokogiri::XML::ParseOptions)
# Defaults to Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA
#
# [Returns] Nokogiri::XML::Schema
def self.read_memory(input, parse_options = ParseOptions::DEFAULT_SCHEMA)
from_document(Nokogiri::XML::Document.parse(input), parse_options)
# Convenience method for Nokogiri::XML::Schema.new
def self.read_memory(...)
# TODO deprecate this method
new(...)
end

#
Expand All @@ -96,20 +86,19 @@ def self.read_memory(input, parse_options = ParseOptions::DEFAULT_SCHEMA)
# Validate +input+ and return any errors that are found.
#
# [Parameters]
# - +input+ (Nokogiri::XML::Document, String)
#
# - +input+ (Nokogiri::XML::Document | String)
# A parsed document, or a string containing a local filename.
#
# [Returns] Array<SyntaxError>
#
# *Example:* Validate an existing Document +document+, and capture any errors that are found.
# *Example:* Validate an existing XML::Document, and capture any errors that are found.
#
# schema = Nokogiri::XML::Schema(File.read(XSD_FILE))
# schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
# errors = schema.validate(document)
#
# *Example:* Validate an XML document on disk, and capture any errors that are found.
# *Example:* Validate an \XML document on disk, and capture any errors that are found.
#
# schema = Nokogiri::XML::Schema(File.read(XSD_FILE))
# schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
# errors = schema.validate("/path/to/file.xml")
#
def validate(input)
Expand All @@ -128,20 +117,19 @@ def validate(input)
# Validate +input+ and return a Boolean indicating whether the document is valid
#
# [Parameters]
# - +input+ (Nokogiri::XML::Document, String)
#
# - +input+ (Nokogiri::XML::Document | String)
# A parsed document, or a string containing a local filename.
#
# [Returns] Boolean
#
# *Example:* Validate an existing XML::Document +document+
# *Example:* Validate an existing XML::Document
#
# schema = Nokogiri::XML::Schema(File.read(XSD_FILE))
# schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
# return unless schema.valid?(document)
#
# *Example:* Validate an XML document on disk
# *Example:* Validate an \XML document on disk
#
# schema = Nokogiri::XML::Schema(File.read(XSD_FILE))
# schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
# return unless schema.valid?("/path/to/file.xml")
#
def valid?(input)
Expand Down
22 changes: 14 additions & 8 deletions test/xml/test_relax_ng.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,25 @@ def test_parse_with_memory
assert_equal(0, schema.errors.length)
end

def test_new
assert(schema = Nokogiri::XML::RelaxNG.new(
File.read(ADDRESS_SCHEMA_FILE),
))
def test_new_with_string
schema = Nokogiri::XML::RelaxNG.new(File.read(ADDRESS_SCHEMA_FILE))
assert_instance_of(Nokogiri::XML::RelaxNG, schema)
assert_equal(0, schema.errors.length)

doc = Nokogiri::XML(File.read(ADDRESS_XML_FILE))
assert(schema.valid?(doc))
end

def test_parse_with_io
xsd = nil
def test_new_with_io
schema = nil
File.open(ADDRESS_SCHEMA_FILE, "rb") do |f|
assert(xsd = Nokogiri::XML::RelaxNG(f))
schema = Nokogiri::XML::RelaxNG.new(f)
end
assert_equal(0, xsd.errors.length)
assert_instance_of(Nokogiri::XML::RelaxNG, schema)
assert_equal(0, schema.errors.length)

doc = Nokogiri::XML(File.read(ADDRESS_XML_FILE))
assert(schema.valid?(doc))
end

def test_constructor_method_with_parse_options
Expand Down
Loading

0 comments on commit 0fffe34

Please sign in to comment.