NewsML-G2 for Ruby, built on lutaml-model.
Parse, manipulate, build and serialize IPTC NewsML-G2 (News Architecture) XML with round-trip fidelity. Targets specification version 2.35, power conformance. The model layer is a port of python-newsmlg2, its full test suite is ported, and the official IPTC specification examples and unit-test suite are adopted as compliance fixtures.
require "newsmlg2"
doc = Newsmlg2.parse_file("newsitem.xml") # or Newsmlg2.parse(xml_string)
doc.item # => Newsmlg2::NewsItem (typed)
doc.item.item_meta.item_class.qcode # => "ninat:text"
doc.item.content_meta.headlines.first.to_s # => "Fed to halt QE ..."
Every root element dispatches to its typed model: NewsItem, PackageItem, ConceptItem, KnowledgeItem, CatalogItem, PlanningItem or NewsMessage.
A document carries its own catalog store: inline <catalog> elements plus <catalogRef> hrefs (the IPTC standard catalogs are bundled, so no network access is needed).
doc.catalog_store.get_scheme_for_alias("ninat").uri
# => "http://cv.iptc.org/newscodes/ninature/"
Newsmlg2.qcode_to_uri("ninat:text", doc)
# => "http://cv.iptc.org/newscodes/ninature/text"
Newsmlg2.uri_to_qcode("http://cv.iptc.org/newscodes/ninature/text", doc)
# => "ninat:text"
doc = Newsmlg2.build_news_item(guid: "urn:newsml:acme.com:20260830:00001",
lang: "en-GB") do |item|
item.item_meta do |meta|
meta.item_class qcode: "ninat:text"
meta.provider qcode: "nprov:acme" do |p|
p.name "Acme News Agency"
end
meta.version_created "2026-08-30T12:00:00+00:00"
end
item.content_meta do |cm|
cm.urgency 2
cm.headline "Eruption of Icelandic volcano"
cm.subject qcode: "medtop:20000962" do |s|
s.name "Volcano"
s.name "Vulkan", xml_lang: "de"
end
end
end
doc.to_xml # => "<?xml version=\"1.0\" ... ?>"
Builder methods are generated from the model metadata: one per attribute (collections accept repeated calls and singular aliases — subject appends to subjects), keyword arguments become child attributes, plain strings wrap into content-bearing types, and blocks nest.
Scalars Time, Date, DateTime and Symbol are accepted wherever a string is, formatted as ISO 8601 with the original offset preserved.
Builder blocks are evaluated against the node itself (instance_eval), so an unqualified method call inside a block resolves against the node first. If its name happens to match an attribute — for example calling your own version_created(item) helper while the node also defines a version_created attribute method — the call is swallowed by the DSL and the argument becomes the attribute value.
Assigning a non-coercible value (a foreign application object) fails fast with an ArgumentError explaining this, instead of corrupting the model graph and failing later at serialization time. Note that a helper whose result is a valid model or scalar will still be assigned silently — the guard cannot tell a legitimate value from an accidental one.
Compute values before entering the block, or call helpers on an explicit receiver:
created = projection.version_created(item) # computed outside
doc = Newsmlg2.build_news_item(guid: guid) do |item|
item.item_meta do |meta|
meta.version_created created
# or: meta.version_created projection.version_created(item)
end
end
doc.to_xml # declaration + canonical 2-space-pretty XML doc.item.item_meta.to_xml # any subtree
Serializing an item without a guid raises Newsmlg2::MissingGuidError. The spec’s default attributes (standard, standardversion, conformance, version) are filled in like python-newsmlg2 does.
-
lib/newsmlg2/nar_model.rb — the base model: binds the NAR namespace once and provides the declaration DSL (xml_attributes, xml_element, xml_content).
-
lib/newsmlg2/base/ — XSD attribute groups and element groups as mixins (one file per group, ported from python-newsmlg2’s attributegroups.py and the *Group element lists).
-
lib/newsmlg2/types/ — one class per XSD complex type; element names are declared once, at their usage site in the owning model.
-
lib/newsmlg2/items/ — the seven item types, newsMessage, and their structural children (ItemMeta, ContentMeta, PartMeta, GroupSet …).
-
lib/newsmlg2/catalog_store.rb — per-document catalog store and qcode resolution; IPTC catalogs v32–v41 are bundled under lib/newsmlg2/catalogs/.
-
lib/newsmlg2/document.rb — the parse/serialize entry point.
-
lib/newsmlg2/builder.rb — the reflection-driven build DSL.
Three test tiers, all green in CI:
-
python-newsmlg2 test suite port — spec/newsmlg2/*_spec.rb.
-
IPTC official examples — all 35 example documents (spec/compliance/examples_spec.rb).
-
IPTC official validation suite — 161 should-pass/should-fail files across all schema versions, ~2100 assertions (spec/compliance/xsd_validation_spec.rb).
IPTC material (examples, schemas, unit-test files) is vendored under spec/fixtures/iptc/ under the IPTC’s CC-BY 4.0 / MIT terms; see spec/fixtures/iptc/README.md.