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.
Files changed (127) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/docs.yml +63 -0
  3. data/.github/workflows/links.yml +99 -0
  4. data/.github/workflows/performance.yml +25 -0
  5. data/.github/workflows/rake.yml +19 -0
  6. data/.github/workflows/release.yml +34 -0
  7. data/.gitignore +17 -0
  8. data/.rubocop.yml +51 -0
  9. data/CHANGELOG.md +31 -0
  10. data/CLAUDE.md +77 -0
  11. data/Gemfile +15 -0
  12. data/LICENSE +24 -0
  13. data/README.adoc +135 -0
  14. data/Rakefile +15 -0
  15. data/docs/Gemfile +6 -0
  16. data/docs/_config.yml +4 -0
  17. data/docs/index.md +15 -0
  18. data/docs/lychee.toml +4 -0
  19. data/docs/usage.md +36 -0
  20. data/lib/newsmlg2/base/administrative_metadata_group.rb +40 -0
  21. data/lib/newsmlg2/base/arbitrary_value_attributes.rb +13 -0
  22. data/lib/newsmlg2/base/authority_attributes.rb +14 -0
  23. data/lib/newsmlg2/base/common_power_attributes.rb +17 -0
  24. data/lib/newsmlg2/base/concept_definition_group.rb +25 -0
  25. data/lib/newsmlg2/base/concept_relationships_group.rb +22 -0
  26. data/lib/newsmlg2/base/confirmation_status_attributes.rb +13 -0
  27. data/lib/newsmlg2/base/deprecated_link_attributes.rb +14 -0
  28. data/lib/newsmlg2/base/descriptive_metadata_group.rb +54 -0
  29. data/lib/newsmlg2/base/entity_details_group.rb +28 -0
  30. data/lib/newsmlg2/base/flex_attributes.rb +13 -0
  31. data/lib/newsmlg2/base/i18n_attributes.rb +14 -0
  32. data/lib/newsmlg2/base/item_management_group.rb +59 -0
  33. data/lib/newsmlg2/base/media_content_characteristics1.rb +22 -0
  34. data/lib/newsmlg2/base/news_content_attributes.rb +18 -0
  35. data/lib/newsmlg2/base/news_content_characteristics.rb +30 -0
  36. data/lib/newsmlg2/base/news_content_type_attributes.rb +17 -0
  37. data/lib/newsmlg2/base/persistent_edit_attributes.rb +13 -0
  38. data/lib/newsmlg2/base/qualifying_attributes.rb +13 -0
  39. data/lib/newsmlg2/base/quantify_attributes.rb +13 -0
  40. data/lib/newsmlg2/base/ranking_attributes.rb +13 -0
  41. data/lib/newsmlg2/base/recurrence_rule_attributes.rb +18 -0
  42. data/lib/newsmlg2/base/target_resource_attributes.rb +20 -0
  43. data/lib/newsmlg2/base/time_validity_attributes.rb +13 -0
  44. data/lib/newsmlg2/base.rb +35 -0
  45. data/lib/newsmlg2/builder/node.rb +125 -0
  46. data/lib/newsmlg2/builder.rb +69 -0
  47. data/lib/newsmlg2/catalog.rb +38 -0
  48. data/lib/newsmlg2/catalog_cache.rb +49 -0
  49. data/lib/newsmlg2/catalog_ref.rb +13 -0
  50. data/lib/newsmlg2/catalog_store.rb +66 -0
  51. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_32.xml +620 -0
  52. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_33.xml +625 -0
  53. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_34.xml +626 -0
  54. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_35.xml +635 -0
  55. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_36.xml +644 -0
  56. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_37.xml +661 -0
  57. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_38.xml +674 -0
  58. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_39.xml +682 -0
  59. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_40.xml +724 -0
  60. data/lib/newsmlg2/catalogs/catalog.IPTC-G2-Standards_41.xml +729 -0
  61. data/lib/newsmlg2/configuration.rb +61 -0
  62. data/lib/newsmlg2/document.rb +123 -0
  63. data/lib/newsmlg2/errors.rb +18 -0
  64. data/lib/newsmlg2/i18n.rb +20 -0
  65. data/lib/newsmlg2/items/any_item.rb +180 -0
  66. data/lib/newsmlg2/items/catalog_item.rb +24 -0
  67. data/lib/newsmlg2/items/concept_item.rb +23 -0
  68. data/lib/newsmlg2/items/content_meta.rb +73 -0
  69. data/lib/newsmlg2/items/item_ref.rb +13 -0
  70. data/lib/newsmlg2/items/knowledge_item.rb +68 -0
  71. data/lib/newsmlg2/items/news_item.rb +130 -0
  72. data/lib/newsmlg2/items/news_message.rb +142 -0
  73. data/lib/newsmlg2/items/package_item.rb +82 -0
  74. data/lib/newsmlg2/items/part_meta.rb +68 -0
  75. data/lib/newsmlg2/items/planning_item.rb +160 -0
  76. data/lib/newsmlg2/nar_model.rb +76 -0
  77. data/lib/newsmlg2/nar_namespace.rb +10 -0
  78. data/lib/newsmlg2/nitf_namespace.rb +9 -0
  79. data/lib/newsmlg2/same_as_scheme.rb +15 -0
  80. data/lib/newsmlg2/scheme.rb +30 -0
  81. data/lib/newsmlg2/types/approximate_date_time_prop_type.rb +14 -0
  82. data/lib/newsmlg2/types/bag.rb +16 -0
  83. data/lib/newsmlg2/types/block_types.rb +62 -0
  84. data/lib/newsmlg2/types/concept.rb +36 -0
  85. data/lib/newsmlg2/types/concept_id.rb +17 -0
  86. data/lib/newsmlg2/types/concept_name_type.rb +17 -0
  87. data/lib/newsmlg2/types/content_meta_types.rb +152 -0
  88. data/lib/newsmlg2/types/date_opt_time_prop_type.rb +12 -0
  89. data/lib/newsmlg2/types/date_time_or_null_prop_type.rb +12 -0
  90. data/lib/newsmlg2/types/date_time_prop_type.rb +12 -0
  91. data/lib/newsmlg2/types/event_details.rb +213 -0
  92. data/lib/newsmlg2/types/ext_property.rb +21 -0
  93. data/lib/newsmlg2/types/facet.rb +17 -0
  94. data/lib/newsmlg2/types/flex1_prop_type.rb +32 -0
  95. data/lib/newsmlg2/types/flex_location_prop_type.rb +43 -0
  96. data/lib/newsmlg2/types/flex_party_prop_type.rb +49 -0
  97. data/lib/newsmlg2/types/flex_prop2_type.rb +26 -0
  98. data/lib/newsmlg2/types/flex_prop_type.rb +17 -0
  99. data/lib/newsmlg2/types/geo.rb +92 -0
  100. data/lib/newsmlg2/types/hierarchy_info.rb +17 -0
  101. data/lib/newsmlg2/types/ids.rb +28 -0
  102. data/lib/newsmlg2/types/inline_types.rb +56 -0
  103. data/lib/newsmlg2/types/intl_string_type.rb +17 -0
  104. data/lib/newsmlg2/types/intl_string_type2.rb +14 -0
  105. data/lib/newsmlg2/types/item_management.rb +74 -0
  106. data/lib/newsmlg2/types/link.rb +40 -0
  107. data/lib/newsmlg2/types/object_details.rb +40 -0
  108. data/lib/newsmlg2/types/party_details.rb +258 -0
  109. data/lib/newsmlg2/types/poi.rb +79 -0
  110. data/lib/newsmlg2/types/qcode_prop_type.rb +12 -0
  111. data/lib/newsmlg2/types/qual_prop_type.rb +23 -0
  112. data/lib/newsmlg2/types/qual_rel_prop_type.rb +18 -0
  113. data/lib/newsmlg2/types/related_concept.rb +71 -0
  114. data/lib/newsmlg2/types/rights.rb +84 -0
  115. data/lib/newsmlg2/types/ruby_annotation.rb +55 -0
  116. data/lib/newsmlg2/types/same_as.rb +17 -0
  117. data/lib/newsmlg2/types/truncated_date_time_prop_type.rb +13 -0
  118. data/lib/newsmlg2/types/versioned_string_type.rb +10 -0
  119. data/lib/newsmlg2/types/xml_base.rb +12 -0
  120. data/lib/newsmlg2/types/xml_lang.rb +12 -0
  121. data/lib/newsmlg2/types.rb +166 -0
  122. data/lib/newsmlg2/utils.rb +42 -0
  123. data/lib/newsmlg2/version.rb +5 -0
  124. data/lib/newsmlg2.rb +181 -0
  125. data/lib/tasks/performance.rake +72 -0
  126. data/newsmlg2.gemspec +34 -0
  127. metadata +185 -0
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newsmlg2
4
+ # Shared value types (XSD complex types usable as element payloads) and
5
+ # W3C xml-namespace attribute types.
6
+ module Types
7
+ autoload :A, 'newsmlg2/types/inline_types'
8
+ autoload :Affiliation, 'newsmlg2/types/party_details'
9
+ autoload :Access, 'newsmlg2/types/poi'
10
+ autoload :AccessStatus, 'newsmlg2/types/event_details'
11
+ autoload :Address, 'newsmlg2/types/party_details'
12
+ autoload :AddressLine, 'newsmlg2/types/party_details'
13
+ autoload :AltId, 'newsmlg2/types/ids'
14
+ autoload :AltRep, 'newsmlg2/types/item_management'
15
+ autoload :ApproximateDateTimePropType, 'newsmlg2/types/approximate_date_time_prop_type'
16
+ autoload :DateOptTimePropType, 'newsmlg2/types/date_opt_time_prop_type'
17
+ autoload :DateTimeOrNullPropType, 'newsmlg2/types/date_time_or_null_prop_type'
18
+ autoload :DateTimePropType, 'newsmlg2/types/date_time_prop_type'
19
+ autoload :Area, 'newsmlg2/types/party_details'
20
+ autoload :Audience, 'newsmlg2/types/content_meta_types'
21
+ autoload :AudienceType, 'newsmlg2/types/content_meta_types'
22
+ autoload :Bag, 'newsmlg2/types/bag'
23
+ autoload :BlockType, 'newsmlg2/types/block_types'
24
+ autoload :Born, 'newsmlg2/types/party_details'
25
+ autoload :Br, 'newsmlg2/types/ruby_annotation'
26
+ autoload :Broader, 'newsmlg2/types/related_concept'
27
+ autoload :By, 'newsmlg2/types/content_meta_types'
28
+ autoload :Capacity, 'newsmlg2/types/poi'
29
+ autoload :Concept, 'newsmlg2/types/concept'
30
+ autoload :ConceptId, 'newsmlg2/types/concept_id'
31
+ autoload :ConceptIdType, 'newsmlg2/types/concept_id'
32
+ autoload :ConceptNameType, 'newsmlg2/types/concept_name_type'
33
+ autoload :ConceptType, 'newsmlg2/types/qual_prop_type'
34
+ autoload :Confirmation, 'newsmlg2/types/event_details'
35
+ autoload :ContactInfoType, 'newsmlg2/types/party_details'
36
+ autoload :ContentDescription, 'newsmlg2/types/content_meta_types'
37
+ autoload :CopyrightNotice, 'newsmlg2/types/rights'
38
+ autoload :Country, 'newsmlg2/types/party_details'
39
+ autoload :Creditline, 'newsmlg2/types/content_meta_types'
40
+ autoload :Dateline, 'newsmlg2/types/content_meta_types'
41
+ autoload :Definition, 'newsmlg2/types/block_types'
42
+ autoload :Details, 'newsmlg2/types/poi'
43
+ autoload :DigitalSourceType, 'newsmlg2/types/content_meta_types'
44
+ autoload :Died, 'newsmlg2/types/party_details'
45
+ autoload :Email, 'newsmlg2/types/party_details'
46
+ autoload :End, 'newsmlg2/types/event_details'
47
+ autoload :Event, 'newsmlg2/types/event_details'
48
+ autoload :EventDetails, 'newsmlg2/types/event_details'
49
+ autoload :EventDetailsLocation, 'newsmlg2/types/event_details'
50
+ autoload :EventDetailsSubject, 'newsmlg2/types/event_details'
51
+ autoload :EventDuration, 'newsmlg2/types/event_details'
52
+ autoload :EventNewsCoverage, 'newsmlg2/types/event_details'
53
+ autoload :Events, 'newsmlg2/types/event_details'
54
+ autoload :ExclAudience, 'newsmlg2/types/content_meta_types'
55
+ autoload :ExDate, 'newsmlg2/types/event_details'
56
+ autoload :ExRule, 'newsmlg2/types/event_details'
57
+ autoload :Facet, 'newsmlg2/types/facet'
58
+ autoload :FacetConcept, 'newsmlg2/types/related_concept'
59
+ autoload :Fax, 'newsmlg2/types/party_details'
60
+ autoload :FileName, 'newsmlg2/types/item_management'
61
+ autoload :Flex1ConceptPropType, 'newsmlg2/types/flex1_prop_type'
62
+ autoload :Flex1ExtPropType, 'newsmlg2/types/ext_property'
63
+ autoload :Flex1PartyPropType, 'newsmlg2/types/flex_party_prop_type'
64
+ autoload :Flex1PropType, 'newsmlg2/types/flex1_prop_type'
65
+ autoload :Flex1RolePropType, 'newsmlg2/types/flex1_prop_type'
66
+ autoload :Flex2ExtPropType, 'newsmlg2/types/ext_property'
67
+ autoload :FlexAuthorPropType, 'newsmlg2/types/flex_party_prop_type'
68
+ autoload :FlexGeoAreaPropType, 'newsmlg2/types/flex_location_prop_type'
69
+ autoload :FlexLocationPropType, 'newsmlg2/types/flex_location_prop_type'
70
+ autoload :FlexPartyPropType, 'newsmlg2/types/flex_party_prop_type'
71
+ autoload :FlexPersonPropType, 'newsmlg2/types/flex_party_prop_type'
72
+ autoload :FlexPOIPropType, 'newsmlg2/types/flex_location_prop_type'
73
+ autoload :FlexProp2Type, 'newsmlg2/types/flex_prop2_type'
74
+ autoload :FlexPropType, 'newsmlg2/types/flex_prop_type'
75
+ autoload :FlexRelatedConceptType, 'newsmlg2/types/related_concept'
76
+ autoload :FlexRelatedPropType, 'newsmlg2/types/flex_prop2_type'
77
+ autoload :Genre, 'newsmlg2/types/content_meta_types'
78
+ autoload :GeoAreaCircle, 'newsmlg2/types/geo'
79
+ autoload :GeoAreaDetails, 'newsmlg2/types/geo'
80
+ autoload :GeoAreaDissolved, 'newsmlg2/types/geo'
81
+ autoload :GeoAreaFounded, 'newsmlg2/types/geo'
82
+ autoload :GeoAreaLine, 'newsmlg2/types/geo'
83
+ autoload :GeoAreaPolygon, 'newsmlg2/types/geo'
84
+ autoload :GeoCoordinatesType, 'newsmlg2/types/geo'
85
+ autoload :Generator, 'newsmlg2/types/item_management'
86
+ autoload :HasInstrument, 'newsmlg2/types/party_details'
87
+ autoload :Hash, 'newsmlg2/types/ids'
88
+ autoload :Headline, 'newsmlg2/types/content_meta_types'
89
+ autoload :HierarchyInfo, 'newsmlg2/types/hierarchy_info'
90
+ autoload :Icon, 'newsmlg2/types/content_meta_types'
91
+ autoload :Im, 'newsmlg2/types/party_details'
92
+ autoload :IncomingFeedId, 'newsmlg2/types/item_management'
93
+ autoload :Inline, 'newsmlg2/types/inline_types'
94
+ autoload :IntlStringType, 'newsmlg2/types/intl_string_type'
95
+ autoload :IntlStringType2, 'newsmlg2/types/intl_string_type2'
96
+ autoload :Keyword, 'newsmlg2/types/content_meta_types'
97
+ autoload :Label1Type, 'newsmlg2/types/block_types'
98
+ autoload :Language, 'newsmlg2/types/content_meta_types'
99
+ autoload :LanguageName, 'newsmlg2/types/content_meta_types'
100
+ autoload :Link, 'newsmlg2/types/link'
101
+ autoload :Link1Type, 'newsmlg2/types/link'
102
+ autoload :Locality, 'newsmlg2/types/party_details'
103
+ autoload :MainConcept, 'newsmlg2/types/related_concept'
104
+ autoload :Narrower, 'newsmlg2/types/related_concept'
105
+ autoload :NewsCoverageStatus, 'newsmlg2/types/event_details'
106
+ autoload :Note, 'newsmlg2/types/block_types'
107
+ autoload :ObjectCreated, 'newsmlg2/types/object_details'
108
+ autoload :ObjectCeasedToExist, 'newsmlg2/types/object_details'
109
+ autoload :ObjectDetails, 'newsmlg2/types/object_details'
110
+ autoload :OccurStatus, 'newsmlg2/types/event_details'
111
+ autoload :OpenHours, 'newsmlg2/types/poi'
112
+ autoload :OrganisationDetails, 'newsmlg2/types/party_details'
113
+ autoload :OrganisationDissolved, 'newsmlg2/types/party_details'
114
+ autoload :OrganisationFounded, 'newsmlg2/types/party_details'
115
+ autoload :OrganisationLocation, 'newsmlg2/types/party_details'
116
+ autoload :Organiser, 'newsmlg2/types/event_details'
117
+ autoload :OrigRep, 'newsmlg2/types/item_management'
118
+ autoload :Participant, 'newsmlg2/types/event_details'
119
+ autoload :ParticipationRequirement, 'newsmlg2/types/event_details'
120
+ autoload :Party, 'newsmlg2/types/flex_party_prop_type'
121
+ autoload :PersonDetails, 'newsmlg2/types/party_details'
122
+ autoload :Phone, 'newsmlg2/types/party_details'
123
+ autoload :POICeasedToExist, 'newsmlg2/types/poi'
124
+ autoload :POICreated, 'newsmlg2/types/poi'
125
+ autoload :POIDetails, 'newsmlg2/types/poi'
126
+ autoload :Position, 'newsmlg2/types/geo'
127
+ autoload :PostalCode, 'newsmlg2/types/party_details'
128
+ autoload :QCodePropType, 'newsmlg2/types/qcode_prop_type'
129
+ autoload :QualPropType, 'newsmlg2/types/qual_prop_type'
130
+ autoload :QualRelPropType, 'newsmlg2/types/qual_rel_prop_type'
131
+ autoload :Rb, 'newsmlg2/types/ruby_annotation'
132
+ autoload :RDate, 'newsmlg2/types/event_details'
133
+ autoload :Rating, 'newsmlg2/types/content_meta_types'
134
+ autoload :Registration, 'newsmlg2/types/event_details'
135
+ autoload :Related, 'newsmlg2/types/related_concept'
136
+ autoload :RelatedConceptType, 'newsmlg2/types/related_concept'
137
+ autoload :RemoteInfo, 'newsmlg2/types/link'
138
+ autoload :RightsBlockType, 'newsmlg2/types/rights'
139
+ autoload :RightsExpressionData, 'newsmlg2/types/rights'
140
+ autoload :RightsExpressionXML, 'newsmlg2/types/rights'
141
+ autoload :RightsInfo, 'newsmlg2/types/rights'
142
+ autoload :Rp, 'newsmlg2/types/ruby_annotation'
143
+ autoload :RRule, 'newsmlg2/types/event_details'
144
+ autoload :Rt, 'newsmlg2/types/ruby_annotation'
145
+ autoload :Ruby, 'newsmlg2/types/ruby_annotation'
146
+ autoload :SameAs, 'newsmlg2/types/same_as'
147
+ autoload :SameAsType, 'newsmlg2/types/same_as'
148
+ autoload :Signal, 'newsmlg2/types/item_management'
149
+ autoload :Slugline, 'newsmlg2/types/content_meta_types'
150
+ autoload :Span, 'newsmlg2/types/inline_types'
151
+ autoload :Start, 'newsmlg2/types/event_details'
152
+ autoload :Subject, 'newsmlg2/types/content_meta_types'
153
+ autoload :TruncatedDateTimePropType, 'newsmlg2/types/truncated_date_time_prop_type'
154
+ autoload :Urgency, 'newsmlg2/types/content_meta_types'
155
+ autoload :UsageTerms, 'newsmlg2/types/rights'
156
+ autoload :UserInteraction, 'newsmlg2/types/content_meta_types'
157
+ autoload :VersionedStringType, 'newsmlg2/types/versioned_string_type'
158
+ autoload :Web, 'newsmlg2/types/party_details'
159
+ autoload :WorldRegion, 'newsmlg2/types/party_details'
160
+ autoload :XmlBase, 'newsmlg2/types/xml_base'
161
+ autoload :XmlLang, 'newsmlg2/types/xml_lang'
162
+
163
+ # python-newsmlg2 API parity aliases.
164
+ Name = ConceptNameType
165
+ end
166
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newsmlg2
4
+ # QCode <-> URI conversion using a document's catalogs.
5
+ module Utils
6
+ module_function
7
+
8
+ # Expands a qcode (e.g. "ninat:text") to its full concept URI using the
9
+ # schemes declared in the document's catalogs.
10
+ #
11
+ # @raise [Newsmlg2::AliasNotFoundInCatalogs]
12
+ def qcode_to_uri(qcode, store_or_document)
13
+ store = catalog_store_for(store_or_document)
14
+ alias_name, code = qcode.split(':', 2)
15
+ raise ArgumentError, "not a qcode: #{qcode.inspect}" if code.nil?
16
+
17
+ store.get_scheme_for_alias(alias_name).uri + code
18
+ end
19
+
20
+ # Compresses a concept URI back to a qcode using the schemes declared in
21
+ # the document's catalogs.
22
+ #
23
+ # @raise [Newsmlg2::UriNotFoundInCatalogs]
24
+ def uri_to_qcode(uri, store_or_document)
25
+ store = catalog_store_for(store_or_document)
26
+ idx = uri.rindex('/')
27
+ raise ArgumentError, "not a concept URI: #{uri.inspect}" unless idx
28
+
29
+ scheme_uri = uri[0..idx]
30
+ code = uri[(idx + 1)..]
31
+ "#{store.get_scheme_for_uri(scheme_uri).alias_attr}:#{code}"
32
+ end
33
+
34
+ def catalog_store_for(store_or_document)
35
+ if store_or_document.is_a?(CatalogStore)
36
+ store_or_document
37
+ else
38
+ store_or_document.catalog_store
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newsmlg2
4
+ VERSION = '0.1.1'
5
+ end
data/lib/newsmlg2.rb ADDED
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lutaml/model'
4
+
5
+ # Newsmlg2 is a lutaml-model-based object model for IPTC NewsML-G2 (NAR):
6
+ # parse, manipulate, build and serialize NewsML-G2 XML with round-trip
7
+ # fidelity. Targets specification version 2.35, power conformance.
8
+ module Newsmlg2
9
+ # The NewsML-G2 specification version this library models; used as the
10
+ # default standardversion of serialized items.
11
+ SPEC_VERSION = '2.35'
12
+
13
+ autoload :VERSION, 'newsmlg2/version'
14
+
15
+ # Shared mixin groups (XSD attribute groups and element groups).
16
+ autoload :Base, 'newsmlg2/base'
17
+
18
+ # Shared value types (XSD complex types usable as element payloads).
19
+ autoload :Types, 'newsmlg2/types'
20
+
21
+ # Item model classes (XSD item types and their structural children).
22
+ autoload :Action, 'newsmlg2/items/any_item'
23
+ autoload :AltLoc, 'newsmlg2/items/news_item'
24
+ autoload :AnyItem, 'newsmlg2/items/any_item'
25
+ autoload :Assert, 'newsmlg2/items/any_item'
26
+ autoload :AssignedTo, 'newsmlg2/items/planning_item'
27
+ autoload :CatalogContainer, 'newsmlg2/items/catalog_item'
28
+ autoload :CatalogItem, 'newsmlg2/items/catalog_item'
29
+ autoload :Channel, 'newsmlg2/items/news_item'
30
+ autoload :ConceptItem, 'newsmlg2/items/concept_item'
31
+ autoload :ConceptRef, 'newsmlg2/items/package_item'
32
+ autoload :ConceptSet, 'newsmlg2/items/knowledge_item'
33
+ autoload :ContentMeta, 'newsmlg2/items/content_meta'
34
+ autoload :ContentMetaAcD, 'newsmlg2/items/content_meta'
35
+ autoload :ContentMetaCat, 'newsmlg2/items/content_meta'
36
+ autoload :ContentSet, 'newsmlg2/items/news_item'
37
+ autoload :DeliveredItemRef, 'newsmlg2/items/planning_item'
38
+ autoload :Delivery, 'newsmlg2/items/planning_item'
39
+ autoload :DerivedFrom, 'newsmlg2/items/any_item'
40
+ autoload :DerivedFromValue, 'newsmlg2/items/any_item'
41
+ autoload :Destination, 'newsmlg2/items/news_message'
42
+ autoload :G2ContentType, 'newsmlg2/items/planning_item'
43
+ autoload :Group, 'newsmlg2/items/package_item'
44
+ autoload :GroupRef, 'newsmlg2/items/package_item'
45
+ autoload :GroupSet, 'newsmlg2/items/package_item'
46
+ autoload :Header, 'newsmlg2/items/news_message'
47
+ autoload :Hop, 'newsmlg2/items/any_item'
48
+ autoload :HopHistory, 'newsmlg2/items/any_item'
49
+ autoload :InlineData, 'newsmlg2/items/news_item'
50
+ autoload :InlineRef, 'newsmlg2/items/any_item'
51
+ autoload :InlineXML, 'newsmlg2/items/news_item'
52
+ autoload :ItemCount, 'newsmlg2/items/planning_item'
53
+ autoload :ItemMeta, 'newsmlg2/items/any_item'
54
+ autoload :ItemRef, 'newsmlg2/items/item_ref'
55
+ autoload :ItemSet, 'newsmlg2/items/news_message'
56
+ autoload :KnowledgeItem, 'newsmlg2/items/knowledge_item'
57
+ autoload :MessageChannel, 'newsmlg2/items/news_message'
58
+ autoload :MessageTimestamp, 'newsmlg2/items/news_message'
59
+ autoload :NewsContentCharacteristicsElement, 'newsmlg2/items/planning_item'
60
+ autoload :NewsCoverage, 'newsmlg2/items/planning_item'
61
+ autoload :NewsCoverageSet, 'newsmlg2/items/planning_item'
62
+ autoload :NewsItem, 'newsmlg2/items/news_item'
63
+ autoload :NewsMessage, 'newsmlg2/items/news_message'
64
+ autoload :Origin, 'newsmlg2/items/news_message'
65
+ autoload :PackageItem, 'newsmlg2/items/package_item'
66
+ autoload :PartMeta, 'newsmlg2/items/part_meta'
67
+ autoload :PartMetaRole, 'newsmlg2/items/part_meta'
68
+ autoload :Planning, 'newsmlg2/items/planning_item'
69
+ autoload :PlanningItem, 'newsmlg2/items/planning_item'
70
+ autoload :Priority, 'newsmlg2/items/news_message'
71
+ autoload :PubHistory, 'newsmlg2/items/any_item'
72
+ autoload :Published, 'newsmlg2/items/any_item'
73
+ autoload :RegionDelim, 'newsmlg2/items/part_meta'
74
+ autoload :RemoteContent, 'newsmlg2/items/news_item'
75
+ autoload :Scheduled, 'newsmlg2/items/planning_item'
76
+ autoload :SchemeMeta, 'newsmlg2/items/knowledge_item'
77
+ autoload :Sender, 'newsmlg2/items/news_message'
78
+ autoload :Sent, 'newsmlg2/items/news_message'
79
+ autoload :StringType, 'newsmlg2/items/news_message'
80
+ autoload :TimeDelim, 'newsmlg2/items/part_meta'
81
+ autoload :Timestamp, 'newsmlg2/items/any_item'
82
+
83
+ # Catalog subsystem (qcode resolution).
84
+ autoload :Catalog, 'newsmlg2/catalog'
85
+ autoload :CatalogCache, 'newsmlg2/catalog_cache'
86
+ autoload :CatalogRef, 'newsmlg2/catalog_ref'
87
+ autoload :CatalogStore, 'newsmlg2/catalog_store'
88
+ autoload :Scheme, 'newsmlg2/scheme'
89
+ autoload :SameAsScheme, 'newsmlg2/same_as_scheme'
90
+
91
+ # Document entry point and support.
92
+ autoload :AliasNotFoundInCatalogs, 'newsmlg2/errors'
93
+ autoload :Builder, 'newsmlg2/builder'
94
+ autoload :Configuration, 'newsmlg2/configuration'
95
+ autoload :Document, 'newsmlg2/document'
96
+ autoload :Error, 'newsmlg2/errors'
97
+ autoload :I18n, 'newsmlg2/i18n'
98
+ autoload :MissingGuidError, 'newsmlg2/errors'
99
+ autoload :NarModel, 'newsmlg2/nar_model'
100
+ autoload :NarNamespace, 'newsmlg2/nar_namespace'
101
+ autoload :NitfNamespace, 'newsmlg2/nitf_namespace'
102
+ autoload :UnknownRootElement, 'newsmlg2/errors'
103
+ autoload :UriNotFoundInCatalogs, 'newsmlg2/errors'
104
+ autoload :Utils, 'newsmlg2/utils'
105
+
106
+ class << self
107
+ # Parses NewsML-G2 XML (any item type or a newsMessage envelope).
108
+ #
109
+ # @return [Newsmlg2::Document]
110
+ def parse(xml)
111
+ Document.parse(xml)
112
+ end
113
+
114
+ # Parses a NewsML-G2 XML file.
115
+ #
116
+ # @return [Newsmlg2::Document]
117
+ def parse_file(path)
118
+ Document.parse_file(path)
119
+ end
120
+
121
+ # Expands a qcode (e.g. "ninat:text") to its full concept URI using the
122
+ # catalogs registered with the given document.
123
+ #
124
+ # @raise [Newsmlg2::AliasNotFoundInCatalogs]
125
+ def qcode_to_uri(qcode, document)
126
+ Utils.qcode_to_uri(qcode, document)
127
+ end
128
+
129
+ # Compresses a concept URI back to a qcode using the given document's
130
+ # catalogs.
131
+ #
132
+ # @raise [Newsmlg2::UriNotFoundInCatalogs]
133
+ def uri_to_qcode(uri, document)
134
+ Utils.uri_to_qcode(uri, document)
135
+ end
136
+
137
+ # Builder DSL entry points (see Newsmlg2::Builder).
138
+ def build_item(klass, **attributes, &)
139
+ Builder.build(klass, attributes, &)
140
+ end
141
+
142
+ def build_news_item(**attributes, &)
143
+ Builder.build_news_item(**attributes, &)
144
+ end
145
+
146
+ def build_package_item(**attributes, &)
147
+ Builder.build_package_item(**attributes, &)
148
+ end
149
+
150
+ def build_concept_item(**attributes, &)
151
+ Builder.build_concept_item(**attributes, &)
152
+ end
153
+
154
+ def build_knowledge_item(**attributes, &)
155
+ Builder.build_knowledge_item(**attributes, &)
156
+ end
157
+
158
+ def build_catalog_item(**attributes, &)
159
+ Builder.build_catalog_item(**attributes, &)
160
+ end
161
+
162
+ def build_planning_item(**attributes, &)
163
+ Builder.build_planning_item(**attributes, &)
164
+ end
165
+
166
+ def build_news_message(**attributes, &)
167
+ Builder.build_news_message(**attributes, &)
168
+ end
169
+ end
170
+
171
+ # Registry: element id -> model class. The single source of truth for
172
+ # root-element dispatch and itemSet item resolution (plurimath/mml
173
+ # pattern); library code never compares element-name strings itself.
174
+ Configuration.register_model(NewsItem, id: :newsItem)
175
+ Configuration.register_model(PackageItem, id: :packageItem)
176
+ Configuration.register_model(ConceptItem, id: :conceptItem)
177
+ Configuration.register_model(KnowledgeItem, id: :knowledgeItem)
178
+ Configuration.register_model(CatalogItem, id: :catalogItem)
179
+ Configuration.register_model(PlanningItem, id: :planningItem)
180
+ Configuration.register_model(NewsMessage, id: :newsMessage)
181
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Baseline: parse + serialize timings over the official IPTC examples.
4
+ # `rake performance:baseline` writes tmp/performance-baseline.json;
5
+ # `rake performance:compare` compares the current run against it (skipped
6
+ # with a message when no baseline exists).
7
+ namespace :performance do
8
+ ROOT = File.expand_path('../..', __dir__)
9
+ EXAMPLES = File.join(ROOT, 'spec/fixtures/iptc/examples')
10
+
11
+ def run_benchmarks
12
+ require 'newsmlg2'
13
+ results = {}
14
+ Dir[File.join(EXAMPLES, '*.xml')].each do |path|
15
+ results[File.basename(path)] = benchmark_document(path)
16
+ end
17
+ results
18
+ end
19
+
20
+ def benchmark_document(path)
21
+ source = File.read(path)
22
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
23
+ begin
24
+ Newsmlg2.parse(source).to_xml
25
+ rescue StandardError
26
+ nil
27
+ end
28
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start).round(6)
29
+ end
30
+
31
+ def baseline_path
32
+ File.join(ROOT, 'tmp/performance-baseline.json')
33
+ end
34
+
35
+ desc 'Write a performance baseline'
36
+ task :baseline do
37
+ require 'json'
38
+ results = run_benchmarks
39
+ FileUtils.mkdir_p(File.dirname(baseline_path))
40
+ File.write(baseline_path, JSON.pretty_generate(results))
41
+ puts "Baseline written: #{baseline_path} (#{results.size} documents)"
42
+ end
43
+
44
+ desc 'Compare current performance against the baseline'
45
+ task :compare do
46
+ require 'json'
47
+ unless File.exist?(baseline_path)
48
+ puts "No baseline at #{baseline_path} — run `rake performance:baseline` first."
49
+ next
50
+ end
51
+
52
+ baseline = JSON.parse(File.read(baseline_path))
53
+ current = run_benchmarks
54
+ slower = []
55
+ current.each do |name, seconds|
56
+ base = baseline[name]
57
+ next unless base
58
+
59
+ ratio = seconds / base
60
+ slower << [name, base, seconds, ratio] if ratio > 1.5
61
+ end
62
+ if slower.empty?
63
+ puts "No document regressed by more than 50% (#{current.size} documents)."
64
+ else
65
+ slower.sort_by { |(_, _, _, ratio)| -ratio }.first(10).each do |name, base, now, ratio|
66
+ warn format('%<name>s: %<base>.4fs -> %<now>.4fs (%<ratio>.2fx)', name: name, base: base, now: now,
67
+ ratio: ratio)
68
+ end
69
+ raise 'performance regression detected'
70
+ end
71
+ end
72
+ end
data/newsmlg2.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/newsmlg2/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'newsmlg2'
7
+ spec.version = Newsmlg2::VERSION
8
+ spec.authors = ['Ribose Inc.']
9
+ spec.email = ['open.source@ribose.com']
10
+
11
+ spec.summary = 'NewsML-G2 — IPTC News Architecture (NewsML-G2) for Ruby.'
12
+ spec.description = 'Newsmlg2 provides a lutaml-model-based Ruby object model for IPTC ' \
13
+ 'NewsML-G2 (NAR). Parse, manipulate, build and serialize NewsML-G2 ' \
14
+ 'XML with full round-trip fidelity, including a builder DSL.'
15
+ spec.homepage = 'https://github.com/lutaml/newsmlg2-ruby'
16
+ spec.license = 'BSD-2-Clause'
17
+ spec.required_ruby_version = Gem::Requirement.new('>= 3.3.0')
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/lutaml/newsmlg2-ruby'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/lutaml/newsmlg2-ruby/blob/main/CHANGELOG.md'
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ f.match(%r{^(test|spec|features)/})
27
+ end
28
+ end
29
+ spec.bindir = 'exe'
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ['lib']
32
+
33
+ spec.add_dependency 'lutaml-model', '~> 0.8.0'
34
+ end