Module: MultiXML Private
- Extended by:
- Helpers, Options, ParseSupport, ParserResolution
- Defined in:
- lib/multi_xml.rb,
lib/multi_xml/errors.rb,
lib/multi_xml/parser.rb,
lib/multi_xml/helpers.rb,
lib/multi_xml/options.rb,
lib/multi_xml/version.rb,
lib/multi_xml/constants.rb,
lib/multi_xml/file_like.rb,
lib/multi_xml/deprecated.rb,
lib/multi_xml/parsers/ox.rb,
lib/multi_xml/concurrency.rb,
lib/multi_xml/parsers/oga.rb,
lib/multi_xml/parse_support.rb,
lib/multi_xml/parsers/rexml.rb,
lib/multi_xml/parsers/libxml.rb,
lib/multi_xml/parsers/nokogiri.rb,
lib/multi_xml/parser_resolution.rb,
lib/multi_xml/parsers/dom_parser.rb,
lib/multi_xml/parsers/libxml_sax.rb,
lib/multi_xml/parsers/sax_handler.rb,
lib/multi_xml/parsers/nokogiri_sax.rb,
lib/multi_xml/options_normalization.rb,
sig/multi_xml.rbs
Overview
This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.
Deprecated public API kept around for one major release
Each method here emits a one-time deprecation warning on first call and delegates to its current-API counterpart. The whole file is loaded by MultiXML so the deprecation surface stays out of the main module definition.
Defined Under Namespace
Modules: Concurrency, FileLike, Helpers, Options, OptionsNormalization, ParseSupport, Parser, ParserResolution, Parsers, _Parser Classes: DisallowedTypeError, FileIO, NoParserError, ParseError, ParserLoadError
Constant Summary collapse
- DEPRECATION_WARNINGS_SHOWN =
Tracks which deprecation warnings have already been emitted so each one fires at most once per process. Stored as a Set rather than a Hash so presence checks have unambiguous semantics for mutation tests.
Set.new
- VERSION =
The current version of MultiXML
Gem::Version.create("0.9.1")
- TEXT_CONTENT_KEY =
Hash key for storing text content within element hashes
"__content__".freeze
- RUBY_TYPE_TO_XML =
Maps Ruby class names to XML type attribute values
{ "Symbol" => "symbol", "Integer" => "integer", "BigDecimal" => "decimal", "Float" => "float", "TrueClass" => "boolean", "FalseClass" => "boolean", "Date" => "date", "DateTime" => "datetime", "Time" => "datetime", "Array" => "array", "Hash" => "hash" }.freeze
- DISALLOWED_TYPES =
XML type attributes disallowed by default for security
These types are blocked to prevent code execution vulnerabilities.
%w[symbol yaml].freeze
- FALSE_BOOLEAN_VALUES =
Values that represent false in XML boolean attributes
Set.new(%w[0 false]).freeze
- NAMESPACE_MODES =
Supported values for the :namespaces parse option
%i[strip preserve].freeze
- DEFAULT_OPTIONS =
Default parsing options
{ typecast_xml_value: true, disallowed_types: DISALLOWED_TYPES, symbolize_names: false, namespaces: :strip }.freeze
- PARSER_PREFERENCE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Array of [library_name, parser_symbol] pairs
if RUBY_ENGINE == "truffleruby" [ ["ox", :ox], ["rexml/document", :rexml], ["libxml-ruby", :libxml], ["oga", :oga], ["nokogiri", :nokogiri] ].freeze else [ ["ox", :ox], ["libxml-ruby", :libxml], ["nokogiri", :nokogiri], ["oga", :oga], ["rexml/document", :rexml] ].freeze end
- PARSE_DATETIME =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Parses datetime strings, trying Time first then DateTime
lambda do |string| Time.parse(string).utc rescue ArgumentError begin DateTime.parse(string).to_time.utc rescue ArgumentError, NoMethodError MultiXML.send(:parse_iso_week_datetime, string) end end
- ISO_WEEK_DATE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Regex matching ISO week dates like YYYY-Www or YYYY-Www-d.
/\A(?<year>\d{4})-W(?<week>\d{2})(?:-(?<day>\d))?\z/- FILE_CONVERTER =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Lambda for creating file-like StringIO from base64 content Uses untyped for content because unpack1 returns various types Uses untyped for entity because hash values are xmlValue but we access specific String keys
lambda do |content, entity| StringIO.new(content.unpack1("m")).tap do |io| io.extend(FileLike) file_io = io # : FileIO file_io.original_filename = entity["name"] file_io.content_type = entity["content_type"] end end
- TYPE_CONVERTERS =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Type converters keyed by XML type attribute string Uses untyped key because hash returns xmlValue, and Hash#[] with non-String returns nil
{ "symbol" => ->(s) { s.to_sym }, "string" => :to_s.to_proc, "integer" => :to_i.to_proc, "float" => :to_f.to_proc, "double" => :to_f.to_proc, "decimal" => ->(s) { BigDecimal(s) }, "boolean" => ->(s) { !FALSE_BOOLEAN_VALUES.include?(s.strip) }, "date" => Date.method(:parse), "datetime" => PARSE_DATETIME, "dateTime" => PARSE_DATETIME, "base64Binary" => ->(s) { s.unpack1("m") }, "binary" => ->(s, entity) { (entity["encoding"] == "base64") ? s.unpack1("m") : s }, "file" => FILE_CONVERTER, "yaml" => lambda do |string| YAML.safe_load(string, permitted_classes: [Symbol, Date, Time]) rescue ArgumentError, Psych::SyntaxError string end }.freeze
Constants included from ParserResolution
ParserResolution::LOADED_PARSER_CHECKS
Constants included from Options
Class Method Summary collapse
-
.apply_postprocessing ⇒ xmlHash
private
Apply typecasting and key-symbolization as configured.
-
.camelize ⇒ String
private
Convert snake_case to CamelCase.
-
.detect_parser ⇒ Symbol, String
private
Detect the best available parser.
-
.find_available_parser ⇒ String, ...
private
Try to find an available parser by requiring libraries.
-
.find_loaded_parser ⇒ Symbol?
private
Find an already-loaded parser library.
-
.load ⇒ xmlHash
private
Deprecated alias for
parse. -
.load_parser ⇒ Module
private
Load a parser module by name.
-
.normalize_input ⇒ Object
private
Convert String to StringIO, pass through IO-like objects Uses respond_to?(:read) duck typing - returns input unchanged if IO-like.
-
.parse(xml, options = {}) ⇒ xmlHash
private
Public API: Parse XML into a Ruby Hash Uses untyped for options because values vary by key (:parser, :symbolize_keys, :disallowed_types, :typecast_xml_value).
-
.parse_with_error_handling ⇒ xmlHash
private
Parse with error handling and key normalization xml_parser implements _Parser interface; original_input uses respond_to? duck typing.
-
.parse_with_namespaces_compatibility ⇒ xmlHash?
private
Dispatch to parsers that may or may not accept
namespaces:. -
.parser ⇒ Module
private
Public API: Get the current XML parser module.
-
.parser=(new_parser) ⇒ Module
private
Public API: Set the XML parser to use.
-
.parser_supports_namespaces_keyword? ⇒ Boolean
private
Check whether a parser accepts the
namespaces:keyword. -
.raise_no_parser_error ⇒ bot
private
Raise NoParserError - never returns.
-
.resolve_parse_parser ⇒ Module
private
Resolve which parser this parse call should use, honoring the :parser option.
-
.resolve_parser ⇒ Module
private
Resolve a parser specification (Symbol, String, or Module) to a parser.
-
.try_require ⇒ Boolean
private
Attempt to require a library, returning success/failure Kernel#require accepts String; library may be Symbol from PARSER_PREFERENCE (coerced at runtime).
-
.validate_namespaces_mode ⇒ Symbol
private
Validate the :namespaces option value.
-
.validate_parser! ⇒ Module
private
Validate that a parser satisfies the contract.
-
.warn_deprecation_once(key, message) ⇒ void
private
Emit a deprecation warning at most once per process.
-
.with_parser(new_parser) ⇒ void
private
Public API: Execute a block with a temporarily-swapped parser.
Methods included from Helpers
apply_converter, convert_hash, convert_text_content, disallowed_type?, empty_value?, extract_array_entries, find_array_entries, self?.apply_converter, self?.convert_hash, self?.convert_text_content, self?.disallowed_type?, self?.empty_value?, self?.extract_array_entries, self?.find_array_entries, self?.symbolize_keys, self?.transform_keys, self?.typecast_array, self?.typecast_children, self?.typecast_hash, self?.typecast_xml_value, self?.undasherize_keys, self?.unwrap_file_if_present, self?.unwrap_if_simple, self?.wrap_and_typecast, symbolize_keys, transform_keys, typecast_array, typecast_children, typecast_hash, typecast_xml_value, undasherize_keys, unwrap_file_if_present, unwrap_if_simple, wrap_and_typecast
Methods included from Options
Class Method Details
.apply_postprocessing ⇒ xmlHash
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Apply typecasting and key-symbolization as configured
164 |
# File 'sig/multi_xml.rbs', line 164
def self.apply_postprocessing: (xmlHash result, Hash[Symbol, untyped] options) -> xmlHash
|
.camelize ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Convert snake_case to CamelCase
125 |
# File 'sig/multi_xml.rbs', line 125
def self.camelize: (String name) -> String
|
.detect_parser ⇒ Symbol, String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Detect the best available parser
128 |
# File 'sig/multi_xml.rbs', line 128
def self.detect_parser: () -> (Symbol | String)
|
.find_available_parser ⇒ String, ...
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Try to find an available parser by requiring libraries
134 |
# File 'sig/multi_xml.rbs', line 134
def self.find_available_parser: () -> (String | Symbol | nil)
|
.find_loaded_parser ⇒ Symbol?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Find an already-loaded parser library
131 |
# File 'sig/multi_xml.rbs', line 131
def self.find_loaded_parser: () -> Symbol?
|
.load ⇒ xmlHash
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Deprecated alias for parse
112 |
# File 'sig/multi_xml.rbs', line 112
def self.load: (String | StringIO xml, ?Hash[Symbol, untyped] options) -> xmlHash
|
.load_parser ⇒ Module
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Load a parser module by name
122 |
# File 'sig/multi_xml.rbs', line 122
def self.load_parser: (Symbol | String name) -> Module
|
.normalize_input ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Convert String to StringIO, pass through IO-like objects Uses respond_to?(:read) duck typing - returns input unchanged if IO-like
145 |
# File 'sig/multi_xml.rbs', line 145
def self.normalize_input: (String | StringIO xml) -> untyped
|
.parse(xml, options = {}) ⇒ xmlHash
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Public API: Parse XML into a Ruby Hash Uses untyped for options because values vary by key (:parser, :symbolize_keys, :disallowed_types, :typecast_xml_value)
119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/multi_xml.rb', line 119 def parse(xml, = {}) call_site = OptionsNormalization.normalize_symbolize_option() global = OptionsNormalization.normalize_symbolize_option((call_site)) = DEFAULT_OPTIONS.merge(global, call_site) namespaces = validate_namespaces_mode(.fetch(:namespaces)) io = normalize_input(xml) return {} if io.eof? result = parse_with_error_handling(io, xml, resolve_parse_parser(), namespaces) apply_postprocessing(result, ) end |
.parse_with_error_handling ⇒ xmlHash
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Parse with error handling and key normalization xml_parser implements _Parser interface; original_input uses respond_to? duck typing
149 |
# File 'sig/multi_xml.rbs', line 149
def self.parse_with_error_handling: ((IO | StringIO) io, untyped original_input, untyped xml_parser, Symbol namespaces) -> xmlHash
|
.parse_with_namespaces_compatibility ⇒ xmlHash?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Dispatch to parsers that may or may not accept namespaces:
152 |
# File 'sig/multi_xml.rbs', line 152
def self.parse_with_namespaces_compatibility: ((IO | StringIO) io, untyped xml_parser, Symbol namespaces) -> xmlHash?
|
.parser ⇒ Module
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Public API: Get the current XML parser module
77 78 79 80 81 82 |
# File 'lib/multi_xml.rb', line 77 def parser override = Fiber[:multi_xml_parser] return override if override @parser ||= resolve_parser(detect_parser) end |
.parser=(new_parser) ⇒ Module
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Public API: Set the XML parser to use
96 97 98 |
# File 'lib/multi_xml.rb', line 96 def parser=(new_parser) @parser = resolve_parser(new_parser) end |
.parser_supports_namespaces_keyword? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Check whether a parser accepts the namespaces: keyword
161 |
# File 'sig/multi_xml.rbs', line 161
def self.parser_supports_namespaces_keyword?: (untyped xml_parser) -> bool
|
.raise_no_parser_error ⇒ bot
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Raise NoParserError - never returns
141 |
# File 'sig/multi_xml.rbs', line 141
def self.raise_no_parser_error: () -> bot
|
.resolve_parse_parser ⇒ Module
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Resolve which parser this parse call should use, honoring the :parser option
158 |
# File 'sig/multi_xml.rbs', line 158
def self.resolve_parse_parser: (Hash[Symbol, untyped] options) -> Module
|
.resolve_parser ⇒ Module
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Resolve a parser specification (Symbol, String, or Module) to a parser
116 |
# File 'sig/multi_xml.rbs', line 116
def self.resolve_parser: (Symbol | String | Module spec) -> Module
|
.try_require ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Attempt to require a library, returning success/failure Kernel#require accepts String; library may be Symbol from PARSER_PREFERENCE (coerced at runtime)
138 |
# File 'sig/multi_xml.rbs', line 138
def self.try_require: (untyped library) -> bool
|
.validate_namespaces_mode ⇒ Symbol
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Validate the :namespaces option value
155 |
# File 'sig/multi_xml.rbs', line 155
def self.validate_namespaces_mode: (untyped mode) -> Symbol
|
.validate_parser! ⇒ Module
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Validate that a parser satisfies the contract
119 |
# File 'sig/multi_xml.rbs', line 119
def self.validate_parser!: (Module parser) -> Module
|
.warn_deprecation_once(key, message) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Emit a deprecation warning at most once per process
50 51 52 53 54 55 56 57 |
# File 'lib/multi_xml.rb', line 50 def self.warn_deprecation_once(key, ) Concurrency.synchronize(:deprecation_warnings) do return if DEPRECATION_WARNINGS_SHOWN.include?(key) Kernel.warn(, category: :deprecated) DEPRECATION_WARNINGS_SHOWN.add(key) end end |
.with_parser(new_parser) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Public API: Execute a block with a temporarily-swapped parser
145 146 147 148 149 150 151 |
# File 'lib/multi_xml.rb', line 145 def self.with_parser(new_parser) previous_override = Fiber[:multi_xml_parser] Fiber[:multi_xml_parser] = resolve_parser(new_parser) yield ensure Fiber[:multi_xml_parser] = previous_override end |