solis 0.103.0 → 0.104.0

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.
@@ -207,6 +207,9 @@ class Solis::Query::Runner
207
207
  ISO8601::TimeInterval.parse(literal.value).to_s
208
208
  when "http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON"
209
209
  JSON.parse(literal.value) rescue literal.value
210
+ when /datatypes\/edtf/, /edtf$/i
211
+ # Return EDTF string representation
212
+ literal.value.to_s
210
213
  else
211
214
  # Handle language-tagged strings
212
215
  if literal.respond_to?(:language) && literal.language
@@ -0,0 +1,143 @@
1
+ require 'rdf'
2
+ require 'edtf'
3
+
4
+ module RDF
5
+ class Literal
6
+ ##
7
+ # Extended Date/Time Format (EDTF) literal for RDF 3.x
8
+ #
9
+ # This class provides RDF literal support for EDTF dates, implementing
10
+ # the Library of Congress Extended Date/Time Format specification.
11
+ #
12
+ # @see https://www.loc.gov/standards/datetime/
13
+ # @see https://github.com/inukshuk/edtf-ruby
14
+ class EDTF < Literal
15
+ DATATYPE = RDF::URI('http://id.loc.gov/datatypes/edtf')
16
+
17
+ # Grammar pattern for EDTF validation
18
+ # Supports Level 0, 1, and 2 expressions
19
+ # This is a simplified pattern - the edtf gem handles full validation
20
+ GRAMMAR = /^[\d\-\/\?\~\.\[\]\{\}XuU\^%\,\+\:\s]+$/
21
+
22
+ ##
23
+ # @param [Object] value
24
+ # @param [Hash{Symbol => Object}] options
25
+ # @option options [String] :lexical (nil)
26
+ def initialize(value, datatype: nil, lexical: nil, **options)
27
+ @edtf_value = parse_edtf(value)
28
+
29
+ # Use EDTF string representation as the lexical form
30
+ lexical_value = @edtf_value.respond_to?(:edtf) ? @edtf_value.edtf : value.to_s
31
+
32
+ super(lexical_value, datatype: DATATYPE, lexical: lexical, **options)
33
+ end
34
+
35
+ ##
36
+ # Returns the EDTF object value
37
+ #
38
+ # @return [EDTF::Date, EDTF::Interval, EDTF::Season, EDTF::Set, etc.]
39
+ def object
40
+ @edtf_value
41
+ end
42
+
43
+ alias_method :to_edtf, :object
44
+
45
+ ##
46
+ # Validates the EDTF literal
47
+ #
48
+ # @return [Boolean]
49
+ def valid?
50
+ return false if @edtf_value.nil?
51
+ # EDTF values are valid if they were successfully parsed
52
+ true
53
+ rescue
54
+ false
55
+ end
56
+
57
+ ##
58
+ # Returns the canonical string representation
59
+ #
60
+ # @return [String]
61
+ def canonicalize
62
+ return self if @edtf_value.nil?
63
+ self.class.new(@edtf_value.edtf)
64
+ end
65
+
66
+ ##
67
+ # Converts to a human-readable string
68
+ #
69
+ # @return [String]
70
+ def humanize
71
+ return to_s unless @edtf_value.respond_to?(:humanize)
72
+ @edtf_value.humanize
73
+ rescue
74
+ to_s
75
+ end
76
+
77
+ ##
78
+ # Returns true if this is an uncertain date
79
+ #
80
+ # @return [Boolean]
81
+ def uncertain?
82
+ @edtf_value.respond_to?(:uncertain?) && @edtf_value.uncertain?
83
+ end
84
+
85
+ ##
86
+ # Returns true if this is an approximate date
87
+ #
88
+ # @return [Boolean]
89
+ def approximate?
90
+ @edtf_value.respond_to?(:approximate?) && @edtf_value.approximate?
91
+ end
92
+
93
+ ##
94
+ # Returns true if this is an interval
95
+ #
96
+ # @return [Boolean]
97
+ def interval?
98
+ @edtf_value.is_a?(::EDTF::Interval)
99
+ end
100
+
101
+ ##
102
+ # Returns true if this is a season
103
+ #
104
+ # @return [Boolean]
105
+ def season?
106
+ @edtf_value.is_a?(::EDTF::Season)
107
+ end
108
+
109
+ ##
110
+ # Returns true if this is a set
111
+ #
112
+ # @return [Boolean]
113
+ def set?
114
+ @edtf_value.is_a?(::EDTF::Set)
115
+ end
116
+
117
+ private
118
+
119
+ ##
120
+ # Parses input value to EDTF object
121
+ #
122
+ # @param [Object] value
123
+ # @return [EDTF::Date, EDTF::Interval, EDTF::Season, EDTF::Set, etc.]
124
+ def parse_edtf(value)
125
+ case value
126
+ when ::EDTF::Interval, ::EDTF::Season, ::EDTF::Set, ::EDTF::Epoch
127
+ value
128
+ when ::Date, ::DateTime, ::Time
129
+ # For Ruby Date/DateTime/Time, try to parse as EDTF
130
+ ::Date.edtf(value.to_s) || value
131
+ when String
132
+ # Parse EDTF string
133
+ ::Date.edtf(value) || ::EDTF.parse(value)
134
+ else
135
+ ::EDTF.parse(value.to_s)
136
+ end
137
+ rescue StandardError => e
138
+ Solis::LOGGER.warn("Failed to parse EDTF value '#{value}': #{e.message}") if defined?(Solis::LOGGER)
139
+ nil
140
+ end
141
+ end
142
+ end
143
+ end
@@ -277,4 +277,91 @@ Graphiti::Types[:array_of_temporal_coverages] = {
277
277
  write: Dry::Types["strict.array"].of(Graphiti::Types[:temporal_coverage][:write]),
278
278
  kind: "array",
279
279
  description: "contains a list of temporal coverage"
280
+ }
281
+
282
+ uri_definition = Dry::Types['strict.string']
283
+ read_uri_type = uri_definition.constructor do |i|
284
+ if i.is_a?(RDF::URI)
285
+ i.to_s
286
+ elsif i.is_a?(String)
287
+ i
288
+ else
289
+ i.to_s
290
+ end
291
+ rescue StandardError => e
292
+ Solis::LOGGER.error(e.message)
293
+ raise Solis::Error::InvalidDatatypeError, e.message
294
+ end
295
+
296
+ write_uri_type = uri_definition.constructor do |i|
297
+ i.to_s
298
+ rescue StandardError => e
299
+ Solis::LOGGER.error(e.message)
300
+ raise Solis::Error::InvalidDatatypeError, e.message
301
+ end
302
+
303
+ Graphiti::Types[:anyuri] = {
304
+ canonical_name: :anyuri,
305
+ params: read_uri_type,
306
+ read: read_uri_type,
307
+ write: write_uri_type,
308
+ kind: "scalar",
309
+ description: "contains a URI"
310
+ }
311
+
312
+ Graphiti::Types[:array_of_anyuris] = {
313
+ canonical_name: :anyuri,
314
+ params: Dry::Types["strict.array"].of(Graphiti::Types[:anyuri][:params]),
315
+ read: Dry::Types["strict.array"].of(Graphiti::Types[:anyuri][:read]),
316
+ write: Dry::Types["strict.array"].of(Graphiti::Types[:anyuri][:write]),
317
+ kind: "array",
318
+ description: "contains a list of URIs"
319
+ }
320
+
321
+ edtf_definition = Dry::Types['strict.string']
322
+ read_edtf_type = edtf_definition.constructor do |i|
323
+ if i.respond_to?(:edtf)
324
+ # EDTF object - convert to string representation
325
+ i.edtf
326
+ elsif i.is_a?(RDF::Literal::EDTF)
327
+ # RDF EDTF Literal - get the lexical value
328
+ i.value
329
+ elsif i.is_a?(String)
330
+ # Validate and return as-is
331
+ parsed = Date.edtf(i)
332
+ parsed ? parsed.edtf : i
333
+ else
334
+ i.to_s
335
+ end
336
+ rescue StandardError => e
337
+ Solis::LOGGER.error("EDTF read error: #{e.message}")
338
+ raise Solis::Error::InvalidDatatypeError, e.message
339
+ end
340
+
341
+ write_edtf_type = edtf_definition.constructor do |i|
342
+ # Validate by parsing
343
+ parsed = Date.edtf(i.to_s)
344
+ raise "Invalid EDTF format" unless parsed && parsed.valid?
345
+ parsed.edtf
346
+ rescue StandardError => e
347
+ Solis::LOGGER.error("EDTF write error: #{e.message}")
348
+ raise Solis::Error::InvalidDatatypeError, "Invalid EDTF format: #{e.message}"
349
+ end
350
+
351
+ Graphiti::Types[:edtf] = {
352
+ canonical_name: :edtf,
353
+ params: read_edtf_type,
354
+ read: read_edtf_type,
355
+ write: write_edtf_type,
356
+ kind: "scalar",
357
+ description: "Extended Date/Time Format (EDTF) - supports uncertain, approximate, and interval dates"
358
+ }
359
+
360
+ Graphiti::Types[:array_of_edtfs] = {
361
+ canonical_name: :edtf,
362
+ params: Dry::Types["strict.array"].of(Graphiti::Types[:edtf][:params]),
363
+ read: Dry::Types["strict.array"].of(Graphiti::Types[:edtf][:read]),
364
+ write: Dry::Types["strict.array"].of(Graphiti::Types[:edtf][:write]),
365
+ kind: "array",
366
+ description: "contains a list of EDTF dates"
280
367
  }
@@ -511,6 +511,8 @@ hide empty members
511
511
  { "type" => "string", "format" => "date-time" }
512
512
  when /anyURI$/
513
513
  { "type" => "string", "format" => "uri" }
514
+ when /datatypes\/edtf/, /edtf$/i
515
+ { "type" => "string", "format" => "edtf" }
514
516
  else
515
517
  { "type" => "string" }
516
518
  end
data/lib/solis/shape.rb CHANGED
@@ -24,7 +24,7 @@ module Solis
24
24
  if datatype =~ /^http:\/\/www.w3.org\/2001\/XMLSchema#/
25
25
  case datatype
26
26
  when /^http:\/\/www.w3.org\/2001\/XMLSchema#anyURI/
27
- :string
27
+ :anyuri
28
28
  when /http:\/\/www.w3.org\/2001\/XMLSchema#duration/
29
29
  :duration
30
30
  when /http:\/\/www.w3.org\/2001\/XMLSchema#integer/
@@ -72,6 +72,9 @@ module Solis
72
72
  # normalize ex."https://data.q.odis.be/person#Name" to Name
73
73
  #node.value.split('/').last.gsub(/Shape$/, '').split('#').last.to_sym
74
74
  #node.value.split('/').last.gsub(/Shape$/, '').gsub('#','').camelize.to_sym
75
+ elsif datatype =~ /datatypes\/edtf/ || datatype =~ /edtf$/i
76
+ # Library of Congress EDTF datatype
77
+ :edtf
75
78
  elsif datatype =~ /^http:\/\/www.w3.org\/1999\/02\/22-rdf-syntax-ns/
76
79
  case datatype
77
80
  when /http:\/\/www.w3.org\/1999\/02\/22-rdf-syntax-ns#langString/
data/lib/solis/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Solis
2
- VERSION = "0.103.0"
2
+ VERSION = "0.104.0"
3
3
  end
data/lib/solis.rb CHANGED
@@ -6,6 +6,7 @@ require 'logger'
6
6
  require "solis/version"
7
7
  require 'solis/config_file'
8
8
  require "solis/error"
9
+ require 'solis/rdf_edtf_literal'
9
10
  require 'solis/graph'
10
11
  require 'solis/shape'
11
12
 
data/solis.gemspec CHANGED
@@ -43,7 +43,7 @@ Gem::Specification.new do |spec|
43
43
  spec.add_runtime_dependency 'csv', '~> 3.2'
44
44
  spec.add_runtime_dependency 'mutex_m', '~> 0.1'
45
45
  spec.add_runtime_dependency 'ostruct', '~> 0.5'
46
- # spec.add_runtime_dependency 'rdf-edtf', '~> 1.1.2'
46
+ spec.add_runtime_dependency 'edtf', '~> 3.0'
47
47
 
48
48
  spec.add_development_dependency 'rake', '~> 13.0'
49
49
  spec.add_development_dependency 'minitest', '~> 5.19'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.103.0
4
+ version: 0.104.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mehmet Celik
@@ -233,6 +233,20 @@ dependencies:
233
233
  - - "~>"
234
234
  - !ruby/object:Gem::Version
235
235
  version: '0.5'
236
+ - !ruby/object:Gem::Dependency
237
+ name: edtf
238
+ requirement: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - "~>"
241
+ - !ruby/object:Gem::Version
242
+ version: '3.0'
243
+ type: :runtime
244
+ prerelease: false
245
+ version_requirements: !ruby/object:Gem::Requirement
246
+ requirements:
247
+ - - "~>"
248
+ - !ruby/object:Gem::Version
249
+ version: '3.0'
236
250
  - !ruby/object:Gem::Dependency
237
251
  name: rake
238
252
  requirement: !ruby/object:Gem::Requirement
@@ -296,10 +310,12 @@ files:
296
310
  - lib/solis/model.rb
297
311
  - lib/solis/model.rb.ok
298
312
  - lib/solis/options.rb
313
+ - lib/solis/overlay_fs.rb
299
314
  - lib/solis/query.rb
300
315
  - lib/solis/query/construct.rb
301
316
  - lib/solis/query/filter.rb
302
317
  - lib/solis/query/run.rb
318
+ - lib/solis/rdf_edtf_literal.rb
303
319
  - lib/solis/resource.rb
304
320
  - lib/solis/shape.rb
305
321
  - lib/solis/shape/data_types.rb