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,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
class Builder
|
|
5
|
+
# Wraps one model instance and exposes a DSL method per lutaml-model
|
|
6
|
+
# attribute. Methods are generated at wrap time from the attribute
|
|
7
|
+
# metadata, so unknown methods fail with a normal NoMethodError.
|
|
8
|
+
class Node
|
|
9
|
+
def initialize(model)
|
|
10
|
+
@model = model
|
|
11
|
+
define_attribute_methods
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :model
|
|
15
|
+
|
|
16
|
+
# Runs a block against this node and returns the underlying model.
|
|
17
|
+
def apply(&block)
|
|
18
|
+
instance_eval(&block) if block
|
|
19
|
+
@model
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def define_attribute_methods
|
|
25
|
+
@model.class.attributes.each_key do |name|
|
|
26
|
+
next if respond_to?(name, false) || respond_to?(name)
|
|
27
|
+
|
|
28
|
+
define_singleton_method(name) do |value = UNSET, **attrs, &block|
|
|
29
|
+
set_attribute(name, value, attrs, block)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
singular = singular_of(name)
|
|
33
|
+
next unless singular && collection?(name)
|
|
34
|
+
|
|
35
|
+
define_singleton_method(singular) do |value = UNSET, **attrs, &block|
|
|
36
|
+
set_attribute(name, value, attrs, block)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# python-newsmlg2 parity: singular accessor appending to a plural
|
|
42
|
+
# collection attribute (names -> name, keywords -> keyword,
|
|
43
|
+
# libraries -> library).
|
|
44
|
+
def singular_of(name)
|
|
45
|
+
s = name.to_s
|
|
46
|
+
if s.end_with?('ies')
|
|
47
|
+
"#{s[0..-4]}y"
|
|
48
|
+
elsif s.end_with?('s') && !s.end_with?('ss')
|
|
49
|
+
s[0..-2]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def private_method_defined?(name)
|
|
54
|
+
respond_to?(name, true) && !respond_to?(name)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def set_attribute(name, value, attrs, block)
|
|
58
|
+
child =
|
|
59
|
+
if attrs.any? && !value.equal?(UNSET)
|
|
60
|
+
build_child(name, **attrs, text: value.to_s)
|
|
61
|
+
elsif attrs.any?
|
|
62
|
+
build_child(name, **attrs)
|
|
63
|
+
elsif !value.equal?(UNSET)
|
|
64
|
+
coerce(name, value)
|
|
65
|
+
else
|
|
66
|
+
build_child(name)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
child = self.class.new(child).apply(&block) if block && child.is_a?(NarModel)
|
|
70
|
+
assign(name, child)
|
|
71
|
+
child
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def build_child(name, **attrs)
|
|
75
|
+
type = attribute_type(name)
|
|
76
|
+
raise ArgumentError, "attribute #{name} is a plain value" if type == String
|
|
77
|
+
|
|
78
|
+
type.new(**attrs)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def coerce(name, value)
|
|
82
|
+
type = attribute_type(name)
|
|
83
|
+
return value if type == String || (!value.is_a?(String) && !value.is_a?(Numeric))
|
|
84
|
+
|
|
85
|
+
# A String (or number) assigned to a content-bearing model type
|
|
86
|
+
# wraps into the type's text content (python-newsmlg2's
|
|
87
|
+
# "located.name = 'Berlin'" convenience).
|
|
88
|
+
type.new(text: value.to_s)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def text_typed?(name)
|
|
92
|
+
type = attribute_type(name)
|
|
93
|
+
type != String
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def assign(name, child)
|
|
97
|
+
if collection?(name)
|
|
98
|
+
current = @model.send(name)
|
|
99
|
+
if current.nil?
|
|
100
|
+
@model.send(:"#{name}=", [child])
|
|
101
|
+
else
|
|
102
|
+
current << child
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
@model.send(:"#{name}=", child)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def collection?(name)
|
|
110
|
+
attribute = @model.class.attributes[name]
|
|
111
|
+
attribute.options[:collection] == true
|
|
112
|
+
rescue StandardError
|
|
113
|
+
false
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def attribute_type(name)
|
|
117
|
+
attribute = @model.class.attributes[name]
|
|
118
|
+
type = attribute.type
|
|
119
|
+
type.is_a?(Class) ? type : String
|
|
120
|
+
rescue StandardError
|
|
121
|
+
String
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# Generic, reflection-driven builder DSL: node methods are generated from
|
|
5
|
+
# each model's lutaml-model attribute metadata, so every current and
|
|
6
|
+
# future Newsmlg2 model is buildable without per-class builder code.
|
|
7
|
+
#
|
|
8
|
+
# doc = Newsmlg2.build_news_item(guid: "...", lang: "en-GB") do |item|
|
|
9
|
+
# item.item_meta do |meta|
|
|
10
|
+
# meta.item_class qcode: "ninat:text"
|
|
11
|
+
# meta.provider qcode: "nprov:acme" do |p|
|
|
12
|
+
# p.name "Acme News Agency"
|
|
13
|
+
# end
|
|
14
|
+
# meta.version_created "2026-08-30T12:00:00+00:00"
|
|
15
|
+
# end
|
|
16
|
+
# item.content_meta do |cm|
|
|
17
|
+
# cm.urgency 2
|
|
18
|
+
# cm.headline "Volcano erupts"
|
|
19
|
+
# cm.subject qcode: "medtop:20000962" do |s|
|
|
20
|
+
# s.name "Volcano"
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# doc.to_xml # => Newsmlg2::Document
|
|
26
|
+
class Builder
|
|
27
|
+
UNSET = Object.new.freeze
|
|
28
|
+
|
|
29
|
+
autoload :Node, 'newsmlg2/builder/node'
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# Builds any item model and returns a Document.
|
|
33
|
+
def build(klass, attributes = {}, &block)
|
|
34
|
+
attributes = attributes.transform_keys(lang: :xml_lang)
|
|
35
|
+
model = klass.new(attributes)
|
|
36
|
+
Builder::Node.new(model).apply(&block) if block
|
|
37
|
+
Document.new(model)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def build_news_item(**attributes, &)
|
|
41
|
+
build(Newsmlg2::NewsItem, attributes, &)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build_package_item(**attributes, &)
|
|
45
|
+
build(Newsmlg2::PackageItem, attributes, &)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def build_concept_item(**attributes, &)
|
|
49
|
+
build(Newsmlg2::ConceptItem, attributes, &)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def build_knowledge_item(**attributes, &)
|
|
53
|
+
build(Newsmlg2::KnowledgeItem, attributes, &)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def build_catalog_item(**attributes, &)
|
|
57
|
+
build(Newsmlg2::CatalogItem, attributes, &)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def build_planning_item(**attributes, &)
|
|
61
|
+
build(Newsmlg2::PlanningItem, attributes, &)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def build_news_message(**attributes, &)
|
|
65
|
+
build(Newsmlg2::NewsMessage, attributes, &)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# A local or remote catalog: a set of scheme alias-to-URI declarations.
|
|
5
|
+
class Catalog < Newsmlg2::NarModel
|
|
6
|
+
include Newsmlg2::Base::CommonPowerAttributes
|
|
7
|
+
include Newsmlg2::Base::AuthorityAttributes
|
|
8
|
+
|
|
9
|
+
xml_attributes additionalinfo: 'additionalInfo', url: 'url',
|
|
10
|
+
guid: 'guid', version: 'version'
|
|
11
|
+
xml_element :titles, xml: 'title',
|
|
12
|
+
type: Newsmlg2::Types::Label1Type, collection: true
|
|
13
|
+
xml_element :schemes, xml: 'scheme',
|
|
14
|
+
type: Newsmlg2::Scheme, collection: true
|
|
15
|
+
|
|
16
|
+
xml do
|
|
17
|
+
element 'catalog'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def get_scheme_for_alias(alias_name)
|
|
21
|
+
schemes.find { |scheme| scheme.alias_attr == alias_name }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def get_scheme_for_uri(uri)
|
|
25
|
+
schemes.find { |scheme| scheme.uri == uri }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def length
|
|
29
|
+
schemes.length
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_s
|
|
33
|
+
return '<Catalog>' if titles.empty?
|
|
34
|
+
|
|
35
|
+
"<Catalog \"#{titles.first}\">"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# Maps remote IPTC catalog URLs to the catalog XML files bundled with this
|
|
5
|
+
# gem (under lib/newsmlg2/catalogs), so catalogRef resolution never touches
|
|
6
|
+
# the network.
|
|
7
|
+
module CatalogCache
|
|
8
|
+
CATALOGS_PATH = File.join(__dir__, 'catalogs')
|
|
9
|
+
|
|
10
|
+
URL_TO_FILE = {
|
|
11
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_32.xml' => 'catalog.IPTC-G2-Standards_32.xml',
|
|
12
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_33.xml' => 'catalog.IPTC-G2-Standards_33.xml',
|
|
13
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_34.xml' => 'catalog.IPTC-G2-Standards_34.xml',
|
|
14
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_35.xml' => 'catalog.IPTC-G2-Standards_35.xml',
|
|
15
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_36.xml' => 'catalog.IPTC-G2-Standards_36.xml',
|
|
16
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_37.xml' => 'catalog.IPTC-G2-Standards_37.xml',
|
|
17
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_38.xml' => 'catalog.IPTC-G2-Standards_38.xml',
|
|
18
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_39.xml' => 'catalog.IPTC-G2-Standards_39.xml',
|
|
19
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_40.xml' => 'catalog.IPTC-G2-Standards_40.xml',
|
|
20
|
+
'http://www.iptc.org/std/catalog/catalog.IPTC-G2-Standards_41.xml' => 'catalog.IPTC-G2-Standards_41.xml'
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
# The bundled file path for a remote catalog URL, or nil when unknown.
|
|
25
|
+
def bundled_path_for(url)
|
|
26
|
+
file = URL_TO_FILE[url]
|
|
27
|
+
return nil unless file
|
|
28
|
+
|
|
29
|
+
File.join(CATALOGS_PATH, file)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Reads the bundled catalog XML for a remote catalog URL, or nil.
|
|
33
|
+
def read(url)
|
|
34
|
+
path = bundled_path_for(url)
|
|
35
|
+
return nil unless path
|
|
36
|
+
|
|
37
|
+
File.read(path)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Parses the bundled catalog for a remote URL into a Catalog.
|
|
41
|
+
def load(url)
|
|
42
|
+
xml = read(url)
|
|
43
|
+
return nil unless xml
|
|
44
|
+
|
|
45
|
+
Newsmlg2::Catalog.from_xml(xml)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# A reference to a remote catalog: a hyperlink to a set of scheme alias
|
|
5
|
+
# declarations.
|
|
6
|
+
class CatalogRef < Newsmlg2::NarModel
|
|
7
|
+
xml_attributes :title, :href
|
|
8
|
+
|
|
9
|
+
xml do
|
|
10
|
+
element 'catalogRef'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Newsmlg2
|
|
4
|
+
# The set of catalogs known to one NewsML-G2 document: inline <catalog>
|
|
5
|
+
# elements plus catalogs resolved from <catalogRef> hrefs (IPTC standard
|
|
6
|
+
# catalogs resolve to the files bundled with this gem). Owns scheme alias
|
|
7
|
+
# and URI lookups used for qcode expansion.
|
|
8
|
+
class CatalogStore
|
|
9
|
+
include Enumerable
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@catalogs = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :catalogs
|
|
16
|
+
|
|
17
|
+
def add_catalog(catalog)
|
|
18
|
+
catalogs << catalog
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
alias << add_catalog
|
|
22
|
+
|
|
23
|
+
def add_catalog_ref(href)
|
|
24
|
+
catalog = Newsmlg2::CatalogCache.load(href)
|
|
25
|
+
add_catalog(catalog) if catalog
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def each(&)
|
|
29
|
+
catalogs.each(&)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def length
|
|
33
|
+
catalogs.length
|
|
34
|
+
end
|
|
35
|
+
alias size length
|
|
36
|
+
|
|
37
|
+
def [](index)
|
|
38
|
+
catalogs[index]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def get_scheme_for_alias(alias_name)
|
|
42
|
+
schemes.each do |scheme|
|
|
43
|
+
return scheme if scheme.alias_attr == alias_name
|
|
44
|
+
end
|
|
45
|
+
raise AliasNotFoundInCatalogs,
|
|
46
|
+
'no catalog of this document declares the scheme alias ' \
|
|
47
|
+
"'#{alias_name}'"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def get_scheme_for_uri(uri)
|
|
51
|
+
schemes.each do |scheme|
|
|
52
|
+
return scheme if scheme.uri == uri
|
|
53
|
+
end
|
|
54
|
+
raise UriNotFoundInCatalogs,
|
|
55
|
+
"no catalog of this document declares the scheme URI '#{uri}'"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def schemes
|
|
59
|
+
catalogs.flat_map(&:schemes)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def to_s
|
|
63
|
+
"#<Newsmlg2::CatalogStore #{length} catalog(s)>"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|