lutaml-model 0.8.4 → 0.8.6

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +5 -0
  3. data/.rubocop.yml +18 -0
  4. data/.rubocop_todo.yml +91 -22
  5. data/Gemfile +2 -0
  6. data/README.adoc +114 -2
  7. data/docs/_guides/index.adoc +18 -0
  8. data/docs/_guides/jsonld-serialization.adoc +217 -0
  9. data/docs/_guides/rdf-serialization.adoc +344 -0
  10. data/docs/_guides/turtle-serialization.adoc +224 -0
  11. data/docs/_migrations/0-8-0-namespace-restructuring.adoc +90 -0
  12. data/docs/_pages/serialization_adapters.adoc +31 -0
  13. data/docs/_references/index.adoc +1 -0
  14. data/docs/_references/rdf-namespaces.adoc +243 -0
  15. data/docs/index.adoc +3 -2
  16. data/lib/lutaml/jsonld/adapter.rb +23 -0
  17. data/lib/lutaml/jsonld/context.rb +69 -0
  18. data/lib/lutaml/jsonld/term_definition.rb +39 -0
  19. data/lib/lutaml/jsonld/transform.rb +174 -0
  20. data/lib/lutaml/jsonld.rb +23 -0
  21. data/lib/lutaml/model/format_registry.rb +10 -1
  22. data/lib/lutaml/model/serialize/format_conversion.rb +17 -1
  23. data/lib/lutaml/model/version.rb +1 -1
  24. data/lib/lutaml/model.rb +6 -0
  25. data/lib/lutaml/rdf/error.rb +7 -0
  26. data/lib/lutaml/rdf/iri.rb +44 -0
  27. data/lib/lutaml/rdf/language_tagged.rb +11 -0
  28. data/lib/lutaml/rdf/literal.rb +62 -0
  29. data/lib/lutaml/rdf/mapping.rb +71 -0
  30. data/lib/lutaml/rdf/mapping_rule.rb +35 -0
  31. data/lib/lutaml/rdf/member_rule.rb +13 -0
  32. data/lib/lutaml/rdf/namespace.rb +58 -0
  33. data/lib/lutaml/rdf/namespace_set.rb +69 -0
  34. data/lib/lutaml/rdf/namespaces/dcterms_namespace.rb +12 -0
  35. data/lib/lutaml/rdf/namespaces/owl_namespace.rb +12 -0
  36. data/lib/lutaml/rdf/namespaces/rdf_namespace.rb +14 -0
  37. data/lib/lutaml/rdf/namespaces/rdfs_namespace.rb +12 -0
  38. data/lib/lutaml/rdf/namespaces/skos_namespace.rb +12 -0
  39. data/lib/lutaml/rdf/namespaces/xsd_namespace.rb +12 -0
  40. data/lib/lutaml/rdf/namespaces.rb +14 -0
  41. data/lib/lutaml/rdf/transform.rb +36 -0
  42. data/lib/lutaml/rdf.rb +19 -0
  43. data/lib/lutaml/turtle/adapter.rb +35 -0
  44. data/lib/lutaml/turtle/mapping.rb +7 -0
  45. data/lib/lutaml/turtle/transform.rb +158 -0
  46. data/lib/lutaml/turtle.rb +22 -0
  47. data/lib/lutaml/xml/adapter/adapter_helpers.rb +1 -42
  48. data/lib/lutaml/xml/adapter/base_adapter.rb +48 -458
  49. data/lib/lutaml/xml/adapter/namespace_data.rb +0 -17
  50. data/lib/lutaml/xml/adapter/namespace_uri_collector.rb +71 -0
  51. data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +5 -1110
  52. data/lib/lutaml/xml/adapter/oga_adapter.rb +6 -846
  53. data/lib/lutaml/xml/adapter/ox_adapter.rb +7 -884
  54. data/lib/lutaml/xml/adapter/plan_based_builder.rb +929 -0
  55. data/lib/lutaml/xml/adapter/rexml_adapter.rb +10 -864
  56. data/lib/lutaml/xml/adapter/xml_parser.rb +86 -0
  57. data/lib/lutaml/xml/adapter/xml_serializer.rb +291 -0
  58. data/lib/lutaml/xml/adapter.rb +0 -1
  59. data/lib/lutaml/xml/adapter_element.rb +7 -1
  60. data/lib/lutaml/xml/builder/base.rb +0 -1
  61. data/lib/lutaml/xml/data_model.rb +9 -1
  62. data/lib/lutaml/xml/document.rb +3 -1
  63. data/lib/lutaml/xml/element.rb +13 -10
  64. data/lib/lutaml/xml/serialization/format_conversion.rb +19 -42
  65. data/lib/lutaml/xml/serialization/instance_methods.rb +26 -35
  66. data/lib/lutaml/xml/transformation/custom_method_wrapper.rb +34 -55
  67. data/lib/lutaml/xml/transformation/rule_applier.rb +1 -1
  68. data/lib/lutaml/xml/xml_element.rb +24 -20
  69. data/spec/lutaml/integration/edge_cases_spec.rb +109 -0
  70. data/spec/lutaml/integration/multi_format_spec.rb +106 -0
  71. data/spec/lutaml/integration/round_trip_spec.rb +170 -0
  72. data/spec/lutaml/jsonld/adapter_spec.rb +46 -0
  73. data/spec/lutaml/jsonld/context_spec.rb +114 -0
  74. data/spec/lutaml/jsonld/term_definition_spec.rb +55 -0
  75. data/spec/lutaml/jsonld/transform_spec.rb +211 -0
  76. data/spec/lutaml/rdf/graph_serialization_spec.rb +137 -0
  77. data/spec/lutaml/rdf/iri_spec.rb +73 -0
  78. data/spec/lutaml/rdf/literal_spec.rb +98 -0
  79. data/spec/lutaml/rdf/mapping_spec.rb +164 -0
  80. data/spec/lutaml/rdf/member_rule_spec.rb +17 -0
  81. data/spec/lutaml/rdf/namespace_set_spec.rb +115 -0
  82. data/spec/lutaml/rdf/namespace_spec.rb +241 -0
  83. data/spec/lutaml/rdf/rdf_transform_spec.rb +82 -0
  84. data/spec/lutaml/turtle/adapter_spec.rb +47 -0
  85. data/spec/lutaml/turtle/mapping_spec.rb +123 -0
  86. data/spec/lutaml/turtle/transform_spec.rb +273 -0
  87. data/spec/lutaml/xml/adapter/base_adapter_regression_spec.rb +151 -0
  88. data/spec/lutaml/xml/adapter/order_spec.rb +150 -0
  89. data/spec/lutaml/xml/clear_parse_state_spec.rb +139 -0
  90. data/spec/lutaml/xml/doubly_defined_namespace_spec.rb +0 -2
  91. data/spec/lutaml/xml/schema/compiler_spec.rb +75 -69
  92. data/spec/lutaml/xml/transformation/custom_method_wrapper_spec.rb +213 -14
  93. metadata +58 -3
  94. data/lib/lutaml/xml/adapter/xml_serialization.rb +0 -145
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module JsonLd
5
+ class Context
6
+ attr_reader :prefixes, :terms
7
+
8
+ def initialize
9
+ @prefixes = {}
10
+ @terms = {}
11
+ @vocab = nil
12
+ @language = nil
13
+ @base = nil
14
+ end
15
+
16
+ def prefix(namespace_class)
17
+ @prefixes[namespace_class.prefix] = namespace_class.uri
18
+ end
19
+
20
+ def vocab(uri = nil)
21
+ @vocab = uri if uri
22
+ @vocab
23
+ end
24
+
25
+ def language(lang = nil)
26
+ @language = lang if lang
27
+ @language
28
+ end
29
+
30
+ def base(uri = nil)
31
+ @base = uri if uri
32
+ @base
33
+ end
34
+
35
+ def term(name, id: nil, type: nil, container: nil, language: nil,
36
+ reverse: false)
37
+ @terms[name] = TermDefinition.new(
38
+ name: name,
39
+ id: id,
40
+ type: type,
41
+ container: container,
42
+ language: language,
43
+ reverse: reverse,
44
+ )
45
+ end
46
+
47
+ def to_hash
48
+ ctx = {}
49
+ ctx["@vocab"] = @vocab if @vocab
50
+ ctx["@language"] = @language if @language
51
+ ctx["@base"] = @base if @base
52
+ @prefixes.each { |pfx, uri| ctx[pfx] = uri }
53
+ @terms.each_value { |td| ctx.merge!(td.to_context_hash) }
54
+ ctx
55
+ end
56
+
57
+ def resolve(term_name)
58
+ if term_name.include?(":")
59
+ pfx, local = term_name.split(":", 2)
60
+ "#{@prefixes[pfx]}#{local}" if @prefixes.key?(pfx)
61
+ elsif @terms.key?(term_name)
62
+ @terms[term_name].id
63
+ elsif @vocab
64
+ "#{@vocab}#{term_name}"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module JsonLd
5
+ class TermDefinition
6
+ attr_reader :name, :id, :type, :container, :language, :reverse
7
+
8
+ def initialize(name:, id: nil, type: nil, container: nil, language: nil,
9
+ reverse: false)
10
+ @name = name
11
+ @id = id
12
+ @type = type
13
+ @container = container
14
+ @language = language
15
+ @reverse = reverse
16
+ end
17
+
18
+ def to_context_hash
19
+ if simple_mapping?
20
+ { @name => @id }
21
+ else
22
+ defn = {}
23
+ defn["@id"] = @id if @id
24
+ defn["@type"] = @type if @type
25
+ defn["@container"] = "@#{@container}" if @container
26
+ defn["@language"] = @language if @language
27
+ defn["@reverse"] = @reverse if @reverse
28
+ { @name => defn }
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def simple_mapping?
35
+ @id && @type.nil? && @container.nil? && @language.nil? && !@reverse
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Lutaml
6
+ module JsonLd
7
+ class Transform < Lutaml::Rdf::Transform
8
+ def model_to_data(instance, _format, options = {})
9
+ mapping = extract_mapping(options)
10
+ return {} unless mapping
11
+
12
+ if mapping.rdf_members.any?
13
+ build_graph_document(mapping, instance)
14
+ else
15
+ build_resource_object(mapping, instance)
16
+ end
17
+ end
18
+
19
+ def data_to_model(data, _format, options = {})
20
+ mapping = extract_mapping(options)
21
+ return model_class.new unless mapping
22
+
23
+ hash = data.is_a?(String) ? JSON.parse(data) : data
24
+
25
+ if hash.key?("@graph") && hash["@graph"].is_a?(Array) && !hash["@graph"].empty?
26
+ graph_data = hash["@graph"]
27
+ first = graph_data.first
28
+ hash = first.is_a?(Hash) ? first : {}
29
+ end
30
+
31
+ hash = strip_jsonld_keywords(hash)
32
+
33
+ attrs = {}
34
+ mapping.rdf_predicates.each do |rule|
35
+ value = hash[rule.predicate_name]
36
+ next if value.nil?
37
+
38
+ attrs[rule.to] = if rule.lang_tagged && value.is_a?(Hash)
39
+ flatten_language_map(value)
40
+ else
41
+ value
42
+ end
43
+ end
44
+
45
+ build_instance(attrs, options)
46
+ end
47
+
48
+ private
49
+
50
+ def extract_mapping(options)
51
+ options[:mappings] || mappings_for(:jsonld, lutaml_register)
52
+ end
53
+
54
+ def build_graph_document(mapping, instance)
55
+ context = build_merged_context(mapping, instance)
56
+ graph = []
57
+
58
+ if mapping.rdf_subject
59
+ resource = build_resource_data(mapping, instance)
60
+ graph << resource unless resource.empty?
61
+ end
62
+
63
+ mapping.rdf_members.each do |member_rule|
64
+ collection = Array(instance.public_send(member_rule.attr_name))
65
+ collection.each do |member|
66
+ member_mapping = member.class.mappings[:jsonld]
67
+ next unless member_mapping
68
+
69
+ resource = build_resource_data(member_mapping, member)
70
+ graph << resource unless resource.empty?
71
+ end
72
+ end
73
+
74
+ { "@context" => context, "@graph" => graph }
75
+ end
76
+
77
+ def build_merged_context(mapping, instance)
78
+ context_hash = build_context_from_mapping(mapping).to_hash
79
+
80
+ mapping.rdf_members.each do |member_rule|
81
+ collection = Array(instance.public_send(member_rule.attr_name))
82
+ next if collection.empty?
83
+
84
+ member_mapping = collection.first.class.mappings[:jsonld]
85
+ next unless member_mapping
86
+
87
+ context_hash.merge!(build_context_from_mapping(member_mapping).to_hash)
88
+ end
89
+
90
+ context_hash
91
+ end
92
+
93
+ def build_context_from_mapping(mapping)
94
+ context = Context.new
95
+ mapping.namespace_set.each { |ns| context.prefix(ns) }
96
+ mapping.rdf_predicates.each do |pred|
97
+ if pred.lang_tagged
98
+ context.term(pred.predicate_name,
99
+ id: pred.uri,
100
+ container: :language)
101
+ else
102
+ context.term(pred.predicate_name, id: pred.uri)
103
+ end
104
+ end
105
+ context
106
+ end
107
+
108
+ def build_resource_object(mapping, instance)
109
+ context = build_context_from_mapping(mapping).to_hash
110
+ data = build_resource_data(mapping, instance)
111
+ { "@context" => context }.merge(data)
112
+ end
113
+
114
+ def build_resource_data(mapping, instance)
115
+ result = {}
116
+
117
+ if mapping.rdf_type
118
+ result["@type"] = resolve_type_compact(mapping)
119
+ end
120
+
121
+ if mapping.rdf_subject
122
+ result["@id"] = resolve_subject_uri(mapping, instance)
123
+ end
124
+
125
+ mapping.rdf_predicates.each do |rule|
126
+ value = instance.public_send(rule.to)
127
+ next if value.nil?
128
+ next if value.is_a?(String) && value.empty?
129
+
130
+ result[rule.predicate_name] = if rule.lang_tagged
131
+ build_language_map(value)
132
+ else
133
+ serialize_rdf_value(value)
134
+ end
135
+ end
136
+
137
+ result
138
+ end
139
+
140
+ def build_language_map(values)
141
+ case values
142
+ when Array
143
+ map = {}
144
+ values.each do |v|
145
+ lang = extract_language(v)
146
+ map[lang] = v.to_s if lang
147
+ end
148
+ map.empty? ? nil : map
149
+ else
150
+ lang = extract_language(values)
151
+ lang ? { lang => values.to_s } : values.to_s
152
+ end
153
+ end
154
+
155
+ def flatten_language_map(lang_map)
156
+ lang_map.values
157
+ end
158
+
159
+ def serialize_rdf_value(value)
160
+ case value
161
+ when Array then value.map { |v| serialize_rdf_value(v) }
162
+ when Integer, Float, TrueClass, FalseClass then value
163
+ else value.to_s
164
+ end
165
+ end
166
+
167
+ def strip_jsonld_keywords(data)
168
+ return data unless data.is_a?(Hash)
169
+
170
+ data.reject { |key, _| key.start_with?("@") }
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "model"
4
+ require_relative "rdf"
5
+
6
+ module Lutaml
7
+ module JsonLd
8
+ autoload :Context, "#{__dir__}/jsonld/context"
9
+ autoload :TermDefinition, "#{__dir__}/jsonld/term_definition"
10
+ autoload :Transform, "#{__dir__}/jsonld/transform"
11
+ autoload :Adapter, "#{__dir__}/jsonld/adapter"
12
+ end
13
+ end
14
+
15
+ Lutaml::Model::FormatRegistry.register(
16
+ :jsonld,
17
+ mapping_class: Lutaml::Rdf::Mapping,
18
+ adapter_class: Lutaml::JsonLd::Adapter,
19
+ transformer: Lutaml::JsonLd::Transform,
20
+ key_value: false,
21
+ rdf: true,
22
+ error_types: ["JSON::ParserError"],
23
+ )
@@ -23,7 +23,7 @@ module Lutaml
23
23
  # @param adapter_options [Hash, nil] { available: [...], default: :name }
24
24
  def register(format, mapping_class:, adapter_class:, transformer:,
25
25
  adapter_loader: nil, castable_type: nil, key_value: nil,
26
- error_types: nil, adapter_options: nil)
26
+ rdf: nil, error_types: nil, adapter_options: nil)
27
27
  validate_registration!(format, mapping_class, transformer)
28
28
 
29
29
  registered_formats[format] = {
@@ -33,6 +33,7 @@ module Lutaml
33
33
  adapter_loader: adapter_loader,
34
34
  castable_type: castable_type,
35
35
  key_value: key_value,
36
+ rdf: rdf,
36
37
  error_types: error_types,
37
38
  adapter_options: adapter_options,
38
39
  registered_at: Time.now,
@@ -109,6 +110,14 @@ module Lutaml
109
110
  registered_formats.dig(format, :key_value) == true
110
111
  end
111
112
 
113
+ def rdf_formats
114
+ registered_formats.select { |_, info| info[:rdf] }.keys
115
+ end
116
+
117
+ def rdf?(format)
118
+ registered_formats.dig(format, :rdf) == true
119
+ end
120
+
112
121
  def error_types_for(format)
113
122
  registered_formats.dig(format, :error_types)
114
123
  end
@@ -15,7 +15,12 @@ module Lutaml
15
15
  # @param block [Proc] The DSL block to evaluate
16
16
  def process_mapping(format, *_args, &)
17
17
  klass = ::Lutaml::Model::Config.mappings_class_for(format)
18
- mappings[format] ||= klass.new
18
+ existing = mappings[format]
19
+ mappings[format] = if existing.nil? || !existing.is_a?(klass)
20
+ klass.new
21
+ else
22
+ existing
23
+ end
19
24
  mappings[format].instance_eval(&)
20
25
 
21
26
  if mappings[format].respond_to?(:finalize)
@@ -273,6 +278,17 @@ module Lutaml
273
278
  end
274
279
  end
275
280
 
281
+ # Define RDF mappings for multiple formats (Turtle, JSON-LD, etc.).
282
+ # Discovers RDF formats dynamically from FormatRegistry.
283
+ #
284
+ # @param block [Proc] The DSL block
285
+ def rdf(&block)
286
+ Lutaml::Model::FormatRegistry.rdf_formats.each do |format|
287
+ mappings[format] = Lutaml::Rdf::Mapping.new
288
+ mappings[format].instance_eval(&block)
289
+ end
290
+ end
291
+
276
292
  # Get resolved mapping for a format
277
293
  #
278
294
  # Delegates to TransformationRegistry for centralized caching
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.8.4"
5
+ VERSION = "0.8.6"
6
6
  end
7
7
  end
data/lib/lutaml/model.rb CHANGED
@@ -10,6 +10,8 @@ module Lutaml
10
10
  autoload :Jsonl, "#{__dir__}/jsonl"
11
11
  autoload :Yamls, "#{__dir__}/yamls"
12
12
  autoload :Xml, "#{__dir__}/xml"
13
+ autoload :JsonLd, "#{__dir__}/jsonld"
14
+ autoload :Turtle, "#{__dir__}/turtle"
13
15
 
14
16
  module Model
15
17
  # Autoloads for lazy loading - set up BEFORE any requires
@@ -241,6 +243,10 @@ require "#{__dir__}/jsonl"
241
243
  require "#{__dir__}/yamls"
242
244
  require "#{__dir__}/xml"
243
245
 
246
+ # Optional formats: require "lutaml/jsonld" or "lutaml/turtle" to enable.
247
+ # These are not eagerly loaded because they depend on optional gems
248
+ # (e.g., rdf-turtle) that most users don't need.
249
+
244
250
  # Prepend builder interface into Serialize
245
251
  # Builder must be prepended AFTER XML so its initialize runs first
246
252
  # (Builder -> XML InstanceMethods -> Serialize)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ class Error < Lutaml::Model::Error; end
6
+ end
7
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ class Iri
6
+ include Comparable
7
+
8
+ attr_reader :value
9
+
10
+ def initialize(uri_string)
11
+ @value = uri_string.to_s.freeze
12
+ end
13
+
14
+ def expand(namespace_set)
15
+ namespace_set.resolve_compact_iri(value)
16
+ end
17
+
18
+ def compact(namespace_set)
19
+ namespace_set.compact(value)
20
+ end
21
+
22
+ def <=>(other)
23
+ other.is_a?(self.class) ? value <=> other.value : nil
24
+ end
25
+
26
+ def ==(other)
27
+ other.is_a?(self.class) && value == other.value
28
+ end
29
+ alias_method :eql?, :==
30
+
31
+ def hash
32
+ value.hash
33
+ end
34
+
35
+ def to_s
36
+ value
37
+ end
38
+
39
+ def inspect
40
+ "#<#{self.class.name} #{value}>"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ module LanguageTagged
6
+ def language_tag
7
+ language
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ class Literal
6
+ include LanguageTagged
7
+
8
+ attr_reader :value, :datatype, :language
9
+
10
+ def initialize(value, datatype: nil, language: nil)
11
+ @value = value
12
+ @datatype = datatype
13
+ @language = language
14
+ end
15
+
16
+ def to_turtle
17
+ escaped = escape_turtle(value.to_s)
18
+ if language
19
+ "#{escaped}@#{language}"
20
+ elsif datatype
21
+ "#{escaped}^^<#{datatype}>"
22
+ else
23
+ escaped
24
+ end
25
+ end
26
+
27
+ def to_jsonld_term
28
+ if language
29
+ { "@value" => value, "@language" => language }
30
+ elsif datatype
31
+ { "@value" => value, "@type" => datatype.to_s }
32
+ else
33
+ value
34
+ end
35
+ end
36
+
37
+ def ==(other)
38
+ other.is_a?(self.class) &&
39
+ value == other.value &&
40
+ datatype == other.datatype &&
41
+ language == other.language
42
+ end
43
+ alias_method :eql?, :==
44
+
45
+ def hash
46
+ [value, datatype, language].hash
47
+ end
48
+
49
+ private
50
+
51
+ def escape_turtle(str)
52
+ escaped = str.gsub(/[\n\r\t"\\]/,
53
+ "\\" => "\\\\",
54
+ '"' => "\\\"",
55
+ "\n" => "\\n",
56
+ "\r" => "\\r",
57
+ "\t" => "\\t")
58
+ "\"#{escaped}\""
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ class Mapping < Lutaml::Model::Mapping
6
+ attr_reader :namespace_set, :rdf_subject, :rdf_type, :rdf_predicates,
7
+ :rdf_members
8
+
9
+ def initialize
10
+ super
11
+ @namespace_set = Lutaml::Rdf::NamespaceSet.new
12
+ @rdf_subject = nil
13
+ @rdf_type = nil
14
+ @rdf_predicates = []
15
+ @rdf_members = []
16
+ end
17
+
18
+ def namespace(*namespace_classes)
19
+ @namespace_set = Lutaml::Rdf::NamespaceSet.new(*namespace_classes)
20
+ end
21
+
22
+ def subject(&)
23
+ @rdf_subject = Proc.new(&) if block_given?
24
+ end
25
+
26
+ def type(value)
27
+ @rdf_type = value
28
+ end
29
+
30
+ def predicate(name, namespace:, to:, lang_tagged: false)
31
+ @rdf_predicates << Lutaml::Rdf::MappingRule.new(
32
+ name,
33
+ namespace: namespace,
34
+ to: to,
35
+ lang_tagged: lang_tagged,
36
+ )
37
+ end
38
+
39
+ def members(attr_name)
40
+ @rdf_members << Lutaml::Rdf::MemberRule.new(attr_name)
41
+ end
42
+
43
+ def mappings(_register_id = nil)
44
+ @rdf_predicates
45
+ end
46
+
47
+ def finalize(_mapper_class); end
48
+
49
+ def finalized?
50
+ true
51
+ end
52
+
53
+ def map_element(name, to:)
54
+ raise Lutaml::Model::IncorrectMappingArgumentsError,
55
+ "RDF mappings use `predicate` instead of `map_element`. " \
56
+ "Use `predicate :#{name}, namespace: MyNs, to: :#{to}` inside `rdf do`."
57
+ end
58
+
59
+ def deep_dup
60
+ self.class.new.tap do |new_mapping|
61
+ new_mapping.instance_variable_set(:@namespace_set, @namespace_set)
62
+ new_mapping.instance_variable_set(:@rdf_subject, @rdf_subject)
63
+ new_mapping.instance_variable_set(:@rdf_type, @rdf_type)
64
+ new_mapping.instance_variable_set(:@rdf_predicates,
65
+ @rdf_predicates.dup)
66
+ new_mapping.instance_variable_set(:@rdf_members, @rdf_members.dup)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ class MappingRule
6
+ attr_reader :predicate_name, :namespace, :to, :lang_tagged
7
+
8
+ def initialize(predicate_name, namespace:, to:, lang_tagged: false)
9
+ validate!(predicate_name, namespace, to)
10
+ @predicate_name = predicate_name.to_s.freeze
11
+ @namespace = namespace
12
+ @to = to
13
+ @lang_tagged = lang_tagged
14
+ end
15
+
16
+ def uri
17
+ @namespace[@predicate_name]
18
+ end
19
+
20
+ def prefixed_name
21
+ @namespace.prefixed(@predicate_name)
22
+ end
23
+
24
+ private
25
+
26
+ def validate!(name, namespace_class, target)
27
+ raise ArgumentError, "predicate_name is required" unless name
28
+ unless namespace_class.is_a?(Class) && namespace_class < Lutaml::Rdf::Namespace
29
+ raise ArgumentError, "namespace must be a Rdf::Namespace subclass"
30
+ end
31
+ raise ArgumentError, ":to is required" unless target
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Rdf
5
+ class MemberRule
6
+ attr_reader :attr_name
7
+
8
+ def initialize(attr_name)
9
+ @attr_name = attr_name.to_sym
10
+ end
11
+ end
12
+ end
13
+ end