newsmlg2 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/docs.yml +63 -0
- data/.github/workflows/links.yml +99 -0
- data/.github/workflows/performance.yml +25 -0
- data/.github/workflows/rake.yml +19 -0
- data/.github/workflows/release.yml +34 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +51 -0
- data/CHANGELOG.md +31 -0
- data/CLAUDE.md +77 -0
- data/Gemfile +15 -0
- data/LICENSE +24 -0
- data/README.adoc +135 -0
- data/Rakefile +15 -0
- data/docs/Gemfile +6 -0
- data/docs/_config.yml +4 -0
- data/docs/index.md +15 -0
- data/docs/lychee.toml +4 -0
- data/docs/usage.md +36 -0
- data/lib/newsmlg2/base/administrative_metadata_group.rb +40 -0
- data/lib/newsmlg2/base/arbitrary_value_attributes.rb +13 -0
- data/lib/newsmlg2/base/authority_attributes.rb +14 -0
- data/lib/newsmlg2/base/common_power_attributes.rb +17 -0
- data/lib/newsmlg2/base/concept_definition_group.rb +25 -0
- data/lib/newsmlg2/base/concept_relationships_group.rb +22 -0
- data/lib/newsmlg2/base/confirmation_status_attributes.rb +13 -0
- data/lib/newsmlg2/base/deprecated_link_attributes.rb +14 -0
- data/lib/newsmlg2/base/descriptive_metadata_group.rb +54 -0
- data/lib/newsmlg2/base/entity_details_group.rb +28 -0
- data/lib/newsmlg2/base/flex_attributes.rb +13 -0
- data/lib/newsmlg2/base/i18n_attributes.rb +14 -0
- data/lib/newsmlg2/base/item_management_group.rb +59 -0
- data/lib/newsmlg2/base/media_content_characteristics1.rb +22 -0
- data/lib/newsmlg2/base/news_content_attributes.rb +18 -0
- data/lib/newsmlg2/base/news_content_characteristics.rb +30 -0
- data/lib/newsmlg2/base/news_content_type_attributes.rb +17 -0
- data/lib/newsmlg2/base/persistent_edit_attributes.rb +13 -0
- data/lib/newsmlg2/base/qualifying_attributes.rb +13 -0
- data/lib/newsmlg2/base/quantify_attributes.rb +13 -0
- data/lib/newsmlg2/base/ranking_attributes.rb +13 -0
- data/lib/newsmlg2/base/recurrence_rule_attributes.rb +18 -0
- data/lib/newsmlg2/base/target_resource_attributes.rb +20 -0
- data/lib/newsmlg2/base/time_validity_attributes.rb +13 -0
- data/lib/newsmlg2/base.rb +35 -0
- data/lib/newsmlg2/builder/node.rb +125 -0
- data/lib/newsmlg2/builder.rb +69 -0
- data/lib/newsmlg2/catalog.rb +38 -0
- data/lib/newsmlg2/catalog_cache.rb +49 -0
- data/lib/newsmlg2/catalog_ref.rb +13 -0
- data/lib/newsmlg2/catalog_store.rb +66 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_32.xml +620 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_33.xml +625 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_34.xml +626 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_35.xml +635 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_36.xml +644 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_37.xml +661 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_38.xml +674 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_39.xml +682 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_40.xml +724 -0
- data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_41.xml +729 -0
- data/lib/newsmlg2/configuration.rb +61 -0
- data/lib/newsmlg2/document.rb +123 -0
- data/lib/newsmlg2/errors.rb +18 -0
- data/lib/newsmlg2/i18n.rb +20 -0
- data/lib/newsmlg2/items/any_item.rb +180 -0
- data/lib/newsmlg2/items/catalog_item.rb +24 -0
- data/lib/newsmlg2/items/concept_item.rb +23 -0
- data/lib/newsmlg2/items/content_meta.rb +73 -0
- data/lib/newsmlg2/items/item_ref.rb +13 -0
- data/lib/newsmlg2/items/knowledge_item.rb +68 -0
- data/lib/newsmlg2/items/news_item.rb +130 -0
- data/lib/newsmlg2/items/news_message.rb +142 -0
- data/lib/newsmlg2/items/package_item.rb +82 -0
- data/lib/newsmlg2/items/part_meta.rb +68 -0
- data/lib/newsmlg2/items/planning_item.rb +160 -0
- data/lib/newsmlg2/nar_model.rb +76 -0
- data/lib/newsmlg2/nar_namespace.rb +10 -0
- data/lib/newsmlg2/nitf_namespace.rb +9 -0
- data/lib/newsmlg2/same_as_scheme.rb +15 -0
- data/lib/newsmlg2/scheme.rb +30 -0
- data/lib/newsmlg2/types/approximate_date_time_prop_type.rb +14 -0
- data/lib/newsmlg2/types/bag.rb +16 -0
- data/lib/newsmlg2/types/block_types.rb +62 -0
- data/lib/newsmlg2/types/concept.rb +36 -0
- data/lib/newsmlg2/types/concept_id.rb +17 -0
- data/lib/newsmlg2/types/concept_name_type.rb +17 -0
- data/lib/newsmlg2/types/content_meta_types.rb +152 -0
- data/lib/newsmlg2/types/date_opt_time_prop_type.rb +12 -0
- data/lib/newsmlg2/types/date_time_or_null_prop_type.rb +12 -0
- data/lib/newsmlg2/types/date_time_prop_type.rb +12 -0
- data/lib/newsmlg2/types/event_details.rb +213 -0
- data/lib/newsmlg2/types/ext_property.rb +21 -0
- data/lib/newsmlg2/types/facet.rb +17 -0
- data/lib/newsmlg2/types/flex1_prop_type.rb +32 -0
- data/lib/newsmlg2/types/flex_location_prop_type.rb +43 -0
- data/lib/newsmlg2/types/flex_party_prop_type.rb +49 -0
- data/lib/newsmlg2/types/flex_prop2_type.rb +26 -0
- data/lib/newsmlg2/types/flex_prop_type.rb +17 -0
- data/lib/newsmlg2/types/geo.rb +92 -0
- data/lib/newsmlg2/types/hierarchy_info.rb +17 -0
- data/lib/newsmlg2/types/ids.rb +28 -0
- data/lib/newsmlg2/types/inline_types.rb +56 -0
- data/lib/newsmlg2/types/intl_string_type.rb +17 -0
- data/lib/newsmlg2/types/intl_string_type2.rb +14 -0
- data/lib/newsmlg2/types/item_management.rb +74 -0
- data/lib/newsmlg2/types/link.rb +40 -0
- data/lib/newsmlg2/types/object_details.rb +40 -0
- data/lib/newsmlg2/types/party_details.rb +258 -0
- data/lib/newsmlg2/types/poi.rb +79 -0
- data/lib/newsmlg2/types/qcode_prop_type.rb +12 -0
- data/lib/newsmlg2/types/qual_prop_type.rb +23 -0
- data/lib/newsmlg2/types/qual_rel_prop_type.rb +18 -0
- data/lib/newsmlg2/types/related_concept.rb +71 -0
- data/lib/newsmlg2/types/rights.rb +84 -0
- data/lib/newsmlg2/types/ruby_annotation.rb +55 -0
- data/lib/newsmlg2/types/same_as.rb +17 -0
- data/lib/newsmlg2/types/truncated_date_time_prop_type.rb +13 -0
- data/lib/newsmlg2/types/versioned_string_type.rb +10 -0
- data/lib/newsmlg2/types/xml_base.rb +12 -0
- data/lib/newsmlg2/types/xml_lang.rb +12 -0
- data/lib/newsmlg2/types.rb +166 -0
- data/lib/newsmlg2/utils.rb +42 -0
- data/lib/newsmlg2/version.rb +5 -0
- data/lib/newsmlg2.rb +181 -0
- data/lib/tasks/performance.rake +72 -0
- data/newsmlg2.gemspec +34 -0
- metadata +185 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# Registry of the NewsML-G2 root models in a lutaml-model global context,
|
|
5
|
+
# keyed by element id. This is the single source of truth for
|
|
6
|
+
# element-name → class resolution (root dispatch, itemSet items) —
|
|
7
|
+
# following the plurimath/mml pattern, library code never compares
|
|
8
|
+
# element-name strings itself.
|
|
9
|
+
module Configuration
|
|
10
|
+
CONTEXT_ID = :newsmlg2
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def context
|
|
14
|
+
Lutaml::Model::GlobalContext.context(CONTEXT_ID)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Rebuilds the built-in context from the registered models.
|
|
18
|
+
def populate_context!
|
|
19
|
+
Lutaml::Model::GlobalContext.unregister_context(CONTEXT_ID) if context
|
|
20
|
+
type_context = Lutaml::Model::GlobalContext.create_context(
|
|
21
|
+
id: CONTEXT_ID,
|
|
22
|
+
registry: Lutaml::Model::TypeRegistry.new,
|
|
23
|
+
fallback_to: [:default]
|
|
24
|
+
)
|
|
25
|
+
register_models_in(type_context)
|
|
26
|
+
Lutaml::Model::GlobalContext.clear_caches
|
|
27
|
+
type_context
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Registers a model under its element id (see the registration block
|
|
31
|
+
# at the bottom of lib/newsmlg2.rb).
|
|
32
|
+
def register_model(klass, id:)
|
|
33
|
+
normalized_id = id.to_sym
|
|
34
|
+
registered_models[normalized_id] = klass
|
|
35
|
+
(context || populate_context!).registry.register(normalized_id, klass)
|
|
36
|
+
klass
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Resolves an element name to its registered model class, or nil for
|
|
40
|
+
# unregistered names (non-element nodes, foreign roots).
|
|
41
|
+
def resolve(element_name)
|
|
42
|
+
name = element_name.to_sym
|
|
43
|
+
return nil unless Lutaml::Model::GlobalContext.resolvable?(name, CONTEXT_ID)
|
|
44
|
+
|
|
45
|
+
Lutaml::Model::GlobalContext.resolve_type(name, CONTEXT_ID)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def register_models_in(type_context)
|
|
51
|
+
registered_models.each do |model_id, klass|
|
|
52
|
+
type_context.registry.register(model_id, klass)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def registered_models
|
|
57
|
+
@registered_models ||= {}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# The NewsML-G2 document entry point: parses XML into a typed item (or
|
|
5
|
+
# newsMessage), exposes it, and serializes back to XML. Each document owns
|
|
6
|
+
# its CatalogStore, built from inline catalogs and catalogRefs.
|
|
7
|
+
#
|
|
8
|
+
# doc = Newsmlg2.parse(xml) # or Newsmlg2.parse_file(path)
|
|
9
|
+
# doc.item # => Newsmlg2::NewsItem
|
|
10
|
+
# doc.item.content_meta.headlines.first
|
|
11
|
+
# doc.catalog_store.get_scheme_for_alias("ninat")
|
|
12
|
+
# doc.to_xml # => declaration + item XML
|
|
13
|
+
#
|
|
14
|
+
# Root dispatch and itemSet resolution go through the Configuration
|
|
15
|
+
# registry; all model parsing is lutaml-model's from_xml. The gem never
|
|
16
|
+
# references a specific XML parser — the adapter is the consumer's
|
|
17
|
+
# choice via Lutaml::Model::Config.
|
|
18
|
+
class Document
|
|
19
|
+
class << self
|
|
20
|
+
# Parses NewsML-G2 XML into a Document.
|
|
21
|
+
def parse(xml)
|
|
22
|
+
with_resolved_adapter do
|
|
23
|
+
root_name, klass = root_class(xml)
|
|
24
|
+
unless klass
|
|
25
|
+
raise UnknownRootElement,
|
|
26
|
+
"'#{root_name}' is not a NewsML-G2 item or newsMessage root element"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
new(klass.from_xml(xml))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Parses a NewsML-G2 XML file into a Document.
|
|
34
|
+
def parse_file(path)
|
|
35
|
+
parse(File.read(path))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Runs the block with the consumer's configured XML adapter; if none
|
|
41
|
+
# resolves (fresh install, unconfigured default), falls back to the
|
|
42
|
+
# REXML adapter, which ships with Ruby — the gem never depends on a
|
|
43
|
+
# specific parser.
|
|
44
|
+
def with_resolved_adapter(&)
|
|
45
|
+
Lutaml::Model::Config.adapter_for(:xml)
|
|
46
|
+
yield
|
|
47
|
+
rescue Lutaml::Model::UnknownAdapterTypeError, LoadError
|
|
48
|
+
Lutaml::Model::Config.with_adapter(xml: :rexml, &)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# NewsML-G2 has eight possible root elements and lutaml-model has no
|
|
52
|
+
# multi-root dispatch, so the document's root element name is read
|
|
53
|
+
# once through the configured adapter and resolved to its registered
|
|
54
|
+
# model class. No element-name strings live in this file — the
|
|
55
|
+
# registry (lib/newsmlg2.rb registration block) is the single source
|
|
56
|
+
# of truth.
|
|
57
|
+
def root_class(xml)
|
|
58
|
+
root = Lutaml::Model::Config.adapter_for(:xml).parse(xml).root
|
|
59
|
+
name = root.name.sub(/\A[\w.-]+:/, '')
|
|
60
|
+
[name, Configuration.resolve(name)]
|
|
61
|
+
rescue StandardError
|
|
62
|
+
['', nil]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
attr_reader :item
|
|
67
|
+
|
|
68
|
+
# @param item [Newsmlg2::AnyItem, Newsmlg2::NewsMessage]
|
|
69
|
+
def initialize(item = nil)
|
|
70
|
+
@item = item
|
|
71
|
+
@catalog_store = CatalogStore.new
|
|
72
|
+
load_catalogs
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Replaces the document's item and rebuilds its catalog store.
|
|
76
|
+
def item=(new_item)
|
|
77
|
+
unless new_item.is_a?(AnyItem) || new_item.is_a?(NewsMessage)
|
|
78
|
+
raise ArgumentError,
|
|
79
|
+
"expected a Newsmlg2 item or NewsMessage, got #{new_item.class}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@item = new_item
|
|
83
|
+
@catalog_store = CatalogStore.new
|
|
84
|
+
load_catalogs
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The catalogs known to this document (inline catalogs plus catalogRefs
|
|
88
|
+
# resolvable through the bundled IPTC catalog cache).
|
|
89
|
+
def catalog_store
|
|
90
|
+
@catalog_store ||= CatalogStore.new.tap { |store| load_catalogs_into(store) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Serializes the document: XML declaration plus the item, validating that
|
|
94
|
+
# a guid is present (items only).
|
|
95
|
+
def to_xml(**options)
|
|
96
|
+
item.validate! if item.is_a?(AnyItem)
|
|
97
|
+
item.to_xml({ declaration: true }.merge(options))
|
|
98
|
+
end
|
|
99
|
+
alias to_xml_string to_xml
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def load_catalogs
|
|
104
|
+
load_catalogs_into(catalog_store)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def load_catalogs_into(store)
|
|
108
|
+
return unless item
|
|
109
|
+
|
|
110
|
+
case item
|
|
111
|
+
when AnyItem then load_catalog_holders(store, item)
|
|
112
|
+
when NewsMessage then load_catalog_holders(store, item.header)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def load_catalog_holders(store, holder)
|
|
117
|
+
return unless holder
|
|
118
|
+
|
|
119
|
+
holder.catalogs.to_a.each { |catalog| store.add_catalog(catalog) }
|
|
120
|
+
holder.catalog_refs.to_a.each { |ref| store.add_catalog_ref(ref.href) }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
|
|
6
|
+
# No catalog known to the document declares this scheme alias.
|
|
7
|
+
class AliasNotFoundInCatalogs < Error; end
|
|
8
|
+
|
|
9
|
+
# No catalog known to the document declares this scheme URI.
|
|
10
|
+
class UriNotFoundInCatalogs < Error; end
|
|
11
|
+
URINotFoundInCatalogs = UriNotFoundInCatalogs
|
|
12
|
+
|
|
13
|
+
# The parsed root element is not a NewsML-G2 item or newsMessage.
|
|
14
|
+
class UnknownRootElement < Error; end
|
|
15
|
+
|
|
16
|
+
# An item without a guid cannot be serialized.
|
|
17
|
+
class MissingGuidError < Error; end
|
|
18
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# Helpers for multi-language content collections (elements carrying
|
|
5
|
+
# xml:lang, such as ConceptNameType values).
|
|
6
|
+
module I18n
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# The languages used by a collection of localized elements.
|
|
10
|
+
def languages(collection)
|
|
11
|
+
collection.filter_map(&:xml_lang).uniq
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# The first element of the collection matching the given language tag
|
|
15
|
+
# (exact match), or nil.
|
|
16
|
+
def for_language(collection, lang)
|
|
17
|
+
collection.find { |item| item.xml_lang == lang }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# An action which is executed at this hop in the hop history.
|
|
5
|
+
class Action < Newsmlg2::Types::QualRelPropType
|
|
6
|
+
xml_attributes :target, :targeturi, :timestamp
|
|
7
|
+
|
|
8
|
+
xml do
|
|
9
|
+
element 'action'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# A single hop of the Hop History.
|
|
14
|
+
class Hop < Newsmlg2::NarModel
|
|
15
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
16
|
+
|
|
17
|
+
xml_attributes :seq, :timestamp
|
|
18
|
+
|
|
19
|
+
xml_element :parties, xml: 'party',
|
|
20
|
+
type: Newsmlg2::Types::Party, collection: true
|
|
21
|
+
xml_element :actions, xml: 'action',
|
|
22
|
+
type: Newsmlg2::Action, collection: true
|
|
23
|
+
|
|
24
|
+
xml do
|
|
25
|
+
element 'hop'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# A history of the creation and modifications of the content object of
|
|
30
|
+
# this item, expressed as a sequence of hops.
|
|
31
|
+
class HopHistory < Newsmlg2::NarModel
|
|
32
|
+
xml_element :hops, xml: 'hop', type: Newsmlg2::Hop, collection: true
|
|
33
|
+
|
|
34
|
+
xml do
|
|
35
|
+
element 'hopHistory'
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Time stamp representing an optionally truncated date and time.
|
|
40
|
+
class Timestamp < Newsmlg2::Types::TruncatedDateTimePropType
|
|
41
|
+
xml do
|
|
42
|
+
element 'timestamp'
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# A step in the pubHistory.
|
|
47
|
+
class Published < Newsmlg2::NarModel
|
|
48
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
49
|
+
|
|
50
|
+
xml_attributes :qcode, :uri, :literal
|
|
51
|
+
|
|
52
|
+
xml_element :timestamp, xml: 'timestamp',
|
|
53
|
+
type: Newsmlg2::Timestamp
|
|
54
|
+
xml_element :names, xml: 'name',
|
|
55
|
+
type: Newsmlg2::Types::ConceptNameType, collection: true
|
|
56
|
+
xml_element :related, xml: 'related',
|
|
57
|
+
type: Newsmlg2::Types::Related, collection: true
|
|
58
|
+
xml_element :published_ext_properties, xml: 'publishedExtProperty',
|
|
59
|
+
type: Newsmlg2::Types::Flex2ExtPropType, collection: true
|
|
60
|
+
|
|
61
|
+
xml do
|
|
62
|
+
element 'published'
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# One to many datasets about publishing this item.
|
|
67
|
+
class PubHistory < Newsmlg2::NarModel
|
|
68
|
+
xml_element :published, xml: 'published',
|
|
69
|
+
type: Newsmlg2::Published, collection: true
|
|
70
|
+
|
|
71
|
+
xml do
|
|
72
|
+
element 'pubHistory'
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# An assertion about a concept. XSD AssertType carries xs:any ##any
|
|
77
|
+
# content — NAR-namespace properties (name, geoAreaDetails, …) or
|
|
78
|
+
# provider extensions — preserved verbatim in #content.
|
|
79
|
+
class Assert < Newsmlg2::NarModel
|
|
80
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
81
|
+
include Newsmlg2::Base::I18NAttributes
|
|
82
|
+
|
|
83
|
+
xml_attributes :qcode, :uri, :literal
|
|
84
|
+
|
|
85
|
+
attribute :content, :string
|
|
86
|
+
|
|
87
|
+
xml do
|
|
88
|
+
element 'assert'
|
|
89
|
+
map_all to: :content
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Inline reference: the concept represented by the content identified by
|
|
94
|
+
# the local identifier(s).
|
|
95
|
+
class InlineRef < Newsmlg2::Types::Flex1PropType
|
|
96
|
+
include Newsmlg2::Base::QuantifyAttributes
|
|
97
|
+
|
|
98
|
+
xml_attributes :idrefs
|
|
99
|
+
|
|
100
|
+
xml do
|
|
101
|
+
element 'inlineRef'
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Refers to the ids of elements whose values have been derived from the
|
|
106
|
+
# concept represented by this property.
|
|
107
|
+
class DerivedFrom < Newsmlg2::Types::Flex1PropType
|
|
108
|
+
xml_attributes :idrefs
|
|
109
|
+
|
|
110
|
+
xml do
|
|
111
|
+
element 'derivedFrom'
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Represents the non-Concept value that was used for deriving the value of
|
|
116
|
+
# one or more properties in this NewsML-G2 item.
|
|
117
|
+
class DerivedFromValue < Newsmlg2::NarModel
|
|
118
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
119
|
+
|
|
120
|
+
xml_attributes :sourceidref, :idrefs
|
|
121
|
+
|
|
122
|
+
xml do
|
|
123
|
+
element 'derivedFromValue'
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# A set of properties directly associated with the Item.
|
|
128
|
+
class ItemMeta < Newsmlg2::NarModel
|
|
129
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
130
|
+
include Newsmlg2::Base::I18NAttributes
|
|
131
|
+
include Newsmlg2::Base::ItemManagementGroup
|
|
132
|
+
|
|
133
|
+
xml_element :links, xml: 'link',
|
|
134
|
+
type: Newsmlg2::Types::Link, collection: true
|
|
135
|
+
xml_element :item_meta_ext_properties, xml: 'itemMetaExtProperty',
|
|
136
|
+
type: Newsmlg2::Types::Flex2ExtPropType, collection: true
|
|
137
|
+
|
|
138
|
+
xml do
|
|
139
|
+
element 'itemMeta'
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Abstract base of all G2 items. Item subclasses declare their own element
|
|
144
|
+
# name ("newsItem", "packageItem", …) and content model.
|
|
145
|
+
class AnyItem < Newsmlg2::NarModel
|
|
146
|
+
include Newsmlg2::Base::I18NAttributes
|
|
147
|
+
|
|
148
|
+
attribute :standard, :string, default: -> { 'NewsML-G2' }
|
|
149
|
+
attribute :standardversion, :string, default: -> { Newsmlg2::SPEC_VERSION }
|
|
150
|
+
attribute :conformance, :string, default: -> { 'power' }
|
|
151
|
+
attribute :guid, :string
|
|
152
|
+
attribute :version, :string, default: -> { '1' }
|
|
153
|
+
|
|
154
|
+
xml do
|
|
155
|
+
map_attribute 'standard', to: :standard, render_default: true
|
|
156
|
+
map_attribute 'standardversion', to: :standardversion, render_default: true
|
|
157
|
+
map_attribute 'conformance', to: :conformance, render_default: true
|
|
158
|
+
map_attribute 'guid', to: :guid
|
|
159
|
+
map_attribute 'version', to: :version, render_default: true
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
xml_element :catalog_refs, xml: 'catalogRef',
|
|
163
|
+
type: Newsmlg2::CatalogRef, collection: true
|
|
164
|
+
xml_element :catalogs, xml: 'catalog',
|
|
165
|
+
type: Newsmlg2::Catalog, collection: true
|
|
166
|
+
xml_element :hop_history, xml: 'hopHistory', type: Newsmlg2::HopHistory
|
|
167
|
+
xml_element :pub_history, xml: 'pubHistory', type: Newsmlg2::PubHistory
|
|
168
|
+
xml_element :rights_infos, xml: 'rightsInfo',
|
|
169
|
+
type: Newsmlg2::Types::RightsInfo, collection: true
|
|
170
|
+
xml_element :item_meta, xml: 'itemMeta', type: Newsmlg2::ItemMeta
|
|
171
|
+
|
|
172
|
+
# Raises unless the item carries the attributes any NewsML-G2 item
|
|
173
|
+
# requires before serialization.
|
|
174
|
+
def validate!
|
|
175
|
+
raise MissingGuidError, "#{self.class.name.split('::').last} needs a guid" if guid.to_s.empty?
|
|
176
|
+
|
|
177
|
+
self
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# The container of a single catalog.
|
|
5
|
+
class CatalogContainer < Newsmlg2::NarModel
|
|
6
|
+
xml_element :catalog, xml: 'catalog', type: Newsmlg2::Catalog
|
|
7
|
+
|
|
8
|
+
xml do
|
|
9
|
+
element 'catalogContainer'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# An Item containing a single managed NewsML-G2 catalog.
|
|
14
|
+
class CatalogItem < AnyItem
|
|
15
|
+
xml_element :content_meta, xml: 'contentMeta',
|
|
16
|
+
type: Newsmlg2::ContentMetaCat
|
|
17
|
+
xml_element :catalog_container, xml: 'catalogContainer',
|
|
18
|
+
type: Newsmlg2::CatalogContainer
|
|
19
|
+
|
|
20
|
+
xml do
|
|
21
|
+
element 'catalogItem'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# An Item containing information about a concept.
|
|
5
|
+
class ConceptItem < AnyItem
|
|
6
|
+
xml_element :content_meta, xml: 'contentMeta',
|
|
7
|
+
type: Newsmlg2::ContentMetaAcD
|
|
8
|
+
xml_element :asserts, xml: 'assert',
|
|
9
|
+
type: Newsmlg2::Assert, collection: true
|
|
10
|
+
xml_element :inline_refs, xml: 'inlineRef',
|
|
11
|
+
type: Newsmlg2::InlineRef, collection: true
|
|
12
|
+
xml_element :derived_from, xml: 'derivedFrom',
|
|
13
|
+
type: Newsmlg2::DerivedFrom, collection: true
|
|
14
|
+
xml_element :derived_from_values, xml: 'derivedFromValue',
|
|
15
|
+
type: Newsmlg2::DerivedFromValue, collection: true
|
|
16
|
+
xml_element :concept, xml: 'concept',
|
|
17
|
+
type: Newsmlg2::Types::Concept
|
|
18
|
+
|
|
19
|
+
xml do
|
|
20
|
+
element 'conceptItem'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# The full content metadata type (administrative + full descriptive
|
|
5
|
+
# properties) used by news and package items; element name "contentMeta"
|
|
6
|
+
# is declared by the owning item.
|
|
7
|
+
class ContentMeta < Newsmlg2::NarModel
|
|
8
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
9
|
+
include Newsmlg2::Base::I18NAttributes
|
|
10
|
+
|
|
11
|
+
# python-newsmlg2 fixtures carry an in-namespace <name> inside
|
|
12
|
+
# contentMeta (an xs:any-style provider extension in those files);
|
|
13
|
+
# modelled so the value round-trips instead of being hoisted.
|
|
14
|
+
xml_element :names, xml: 'name',
|
|
15
|
+
type: Newsmlg2::Types::ConceptNameType, collection: true
|
|
16
|
+
xml_element :icons, xml: 'icon',
|
|
17
|
+
type: Newsmlg2::Types::Icon, collection: true
|
|
18
|
+
include Newsmlg2::Base::AdministrativeMetadataGroup
|
|
19
|
+
include Newsmlg2::Base::DescriptiveMetadataGroup
|
|
20
|
+
|
|
21
|
+
xml_element :content_meta_ext_properties, xml: 'contentMetaExtProperty',
|
|
22
|
+
type: Newsmlg2::Types::Flex2ExtPropType, collection: true
|
|
23
|
+
|
|
24
|
+
xml do
|
|
25
|
+
element 'contentMeta'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Content metadata with the core descriptive set, used by concept,
|
|
30
|
+
# knowledge and planning items.
|
|
31
|
+
class ContentMetaAcD < Newsmlg2::NarModel
|
|
32
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
33
|
+
include Newsmlg2::Base::I18NAttributes
|
|
34
|
+
|
|
35
|
+
# python-newsmlg2 fixtures carry an in-namespace <name> inside
|
|
36
|
+
# contentMeta (an xs:any-style provider extension in those files);
|
|
37
|
+
# modelled so the value round-trips instead of being hoisted.
|
|
38
|
+
xml_element :names, xml: 'name',
|
|
39
|
+
type: Newsmlg2::Types::ConceptNameType, collection: true
|
|
40
|
+
xml_element :icons, xml: 'icon',
|
|
41
|
+
type: Newsmlg2::Types::Icon, collection: true
|
|
42
|
+
include Newsmlg2::Base::AdministrativeMetadataGroup
|
|
43
|
+
include Newsmlg2::Base::DescriptiveMetadataCoreGroup
|
|
44
|
+
|
|
45
|
+
xml_element :content_meta_ext_properties, xml: 'contentMetaExtProperty',
|
|
46
|
+
type: Newsmlg2::Types::Flex2ExtPropType, collection: true
|
|
47
|
+
|
|
48
|
+
xml do
|
|
49
|
+
element 'contentMeta'
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The restricted content metadata of a catalog item.
|
|
54
|
+
class ContentMetaCat < Newsmlg2::NarModel
|
|
55
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
56
|
+
include Newsmlg2::Base::I18NAttributes
|
|
57
|
+
|
|
58
|
+
xml_element :content_created, xml: 'contentCreated',
|
|
59
|
+
type: Newsmlg2::Types::TruncatedDateTimePropType
|
|
60
|
+
xml_element :content_modified, xml: 'contentModified',
|
|
61
|
+
type: Newsmlg2::Types::TruncatedDateTimePropType
|
|
62
|
+
xml_element :creators, xml: 'creator',
|
|
63
|
+
type: Newsmlg2::Types::FlexAuthorPropType, collection: true
|
|
64
|
+
xml_element :contributors, xml: 'contributor',
|
|
65
|
+
type: Newsmlg2::Types::FlexAuthorPropType, collection: true
|
|
66
|
+
xml_element :alt_ids, xml: 'altId',
|
|
67
|
+
type: Newsmlg2::Types::AltId, collection: true
|
|
68
|
+
|
|
69
|
+
xml do
|
|
70
|
+
element 'contentMeta'
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# A reference to a target item or Web resource. Official IPTC examples
|
|
5
|
+
# carry media characteristics (width, height, …) on itemRef.
|
|
6
|
+
class ItemRef < Newsmlg2::Types::Link1Type
|
|
7
|
+
include Newsmlg2::Base::NewsContentCharacteristics
|
|
8
|
+
|
|
9
|
+
xml do
|
|
10
|
+
element 'itemRef'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# An unordered set of concepts.
|
|
5
|
+
class ConceptSet < Newsmlg2::NarModel
|
|
6
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
7
|
+
|
|
8
|
+
xml_element :concepts, xml: 'concept',
|
|
9
|
+
type: Newsmlg2::Types::Concept, collection: true
|
|
10
|
+
|
|
11
|
+
xml do
|
|
12
|
+
element 'conceptSet'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Metadata about a scheme conveyed by a Knowledge Item.
|
|
17
|
+
class SchemeMeta < Newsmlg2::NarModel
|
|
18
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
19
|
+
include Newsmlg2::Base::AuthorityAttributes
|
|
20
|
+
|
|
21
|
+
xml_element :same_as_schemes, xml: 'sameAsScheme',
|
|
22
|
+
type: Newsmlg2::SameAsScheme, collection: true
|
|
23
|
+
xml_element :names, xml: 'name',
|
|
24
|
+
type: Newsmlg2::Types::ConceptNameType, collection: true
|
|
25
|
+
xml_element :definitions, xml: 'definition',
|
|
26
|
+
type: Newsmlg2::Types::Definition, collection: true
|
|
27
|
+
xml_element :notes, xml: 'note',
|
|
28
|
+
type: Newsmlg2::Types::Note, collection: true
|
|
29
|
+
xml_element :related, xml: 'related',
|
|
30
|
+
type: Newsmlg2::Types::Related, collection: true
|
|
31
|
+
xml_element :scheme_meta_ext_properties, xml: 'schemeMetaExtProperty',
|
|
32
|
+
type: Newsmlg2::Types::Flex2ExtPropType, collection: true
|
|
33
|
+
|
|
34
|
+
xml_attributes(
|
|
35
|
+
:uri, :preferredalias, :concepttype,
|
|
36
|
+
:schemecreated, :schememodified, :schemeretired
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
xml do
|
|
40
|
+
element 'schemeMeta'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# An Item used for collating a set of concept definitions to form the
|
|
45
|
+
# physical representation of a controlled vocabulary.
|
|
46
|
+
class KnowledgeItem < AnyItem
|
|
47
|
+
xml_element :content_meta, xml: 'contentMeta',
|
|
48
|
+
type: Newsmlg2::ContentMetaAcD
|
|
49
|
+
xml_element :part_metas, xml: 'partMeta',
|
|
50
|
+
type: Newsmlg2::PartMeta, collection: true
|
|
51
|
+
xml_element :asserts, xml: 'assert',
|
|
52
|
+
type: Newsmlg2::Assert, collection: true
|
|
53
|
+
xml_element :inline_refs, xml: 'inlineRef',
|
|
54
|
+
type: Newsmlg2::InlineRef, collection: true
|
|
55
|
+
xml_element :derived_from, xml: 'derivedFrom',
|
|
56
|
+
type: Newsmlg2::DerivedFrom, collection: true
|
|
57
|
+
xml_element :derived_from_values, xml: 'derivedFromValue',
|
|
58
|
+
type: Newsmlg2::DerivedFromValue, collection: true
|
|
59
|
+
xml_element :concept_set, xml: 'conceptSet',
|
|
60
|
+
type: Newsmlg2::ConceptSet
|
|
61
|
+
xml_element :scheme_meta, xml: 'schemeMeta',
|
|
62
|
+
type: Newsmlg2::SchemeMeta
|
|
63
|
+
|
|
64
|
+
xml do
|
|
65
|
+
element 'knowledgeItem'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|