solis 0.64.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +6 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +287 -0
  8. data/Rakefile +10 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/examples/after_hooks.rb +24 -0
  12. data/examples/config.yml.template +15 -0
  13. data/examples/read_from_shacl.rb +22 -0
  14. data/examples/read_from_shacl_abv.rb +84 -0
  15. data/examples/read_from_sheet.rb +22 -0
  16. data/lib/solis/config_file.rb +91 -0
  17. data/lib/solis/error/cursor_error.rb +6 -0
  18. data/lib/solis/error/general_error.rb +6 -0
  19. data/lib/solis/error/invalid_attribute_error.rb +6 -0
  20. data/lib/solis/error/invalid_datatype_error.rb +6 -0
  21. data/lib/solis/error/not_found_error.rb +6 -0
  22. data/lib/solis/error/query_error.rb +6 -0
  23. data/lib/solis/error.rb +3 -0
  24. data/lib/solis/graph.rb +360 -0
  25. data/lib/solis/model.rb +565 -0
  26. data/lib/solis/options.rb +19 -0
  27. data/lib/solis/query/construct.rb +93 -0
  28. data/lib/solis/query/filter.rb +133 -0
  29. data/lib/solis/query/run.rb +97 -0
  30. data/lib/solis/query.rb +347 -0
  31. data/lib/solis/resource.rb +37 -0
  32. data/lib/solis/shape/data_types.rb +280 -0
  33. data/lib/solis/shape/reader/csv.rb +12 -0
  34. data/lib/solis/shape/reader/file.rb +16 -0
  35. data/lib/solis/shape/reader/sheet.rb +777 -0
  36. data/lib/solis/shape/reader/simple_sheets/sheet.rb +59 -0
  37. data/lib/solis/shape/reader/simple_sheets/worksheet.rb +173 -0
  38. data/lib/solis/shape/reader/simple_sheets.rb +40 -0
  39. data/lib/solis/shape.rb +189 -0
  40. data/lib/solis/sparql_adaptor.rb +318 -0
  41. data/lib/solis/store/sparql/client/query.rb +35 -0
  42. data/lib/solis/store/sparql/client.rb +41 -0
  43. data/lib/solis/version.rb +3 -0
  44. data/lib/solis.rb +13 -0
  45. data/solis.gemspec +50 -0
  46. metadata +304 -0
@@ -0,0 +1,565 @@
1
+ require 'securerandom'
2
+ require 'iso8601'
3
+ require_relative 'query'
4
+
5
+ module Solis
6
+ class Model
7
+
8
+ class_attribute :before_read_proc, :after_read_proc, :before_create_proc, :after_create_proc, :before_update_proc, :after_update_proc, :before_delete_proc, :after_delete_proc
9
+
10
+ def initialize(attributes = {})
11
+ @model_name = self.class.name
12
+ @model_plural_name = @model_name.pluralize
13
+ @language = Graphiti.context[:object]&.language || Solis::Options.instance.get[:language] || 'en'
14
+
15
+ raise "Please look at /#{@model_name.tableize}/model for structure to supply" if attributes.nil?
16
+
17
+ attributes.each do |attribute, value|
18
+ if self.class.metadata[:attributes].keys.include?(attribute.to_s)
19
+ if !self.class.metadata[:attributes][attribute.to_s][:node_kind].nil? && !(value.is_a?(Hash) || value.is_a?(Array) || value.class.ancestors.include?(Solis::Model))
20
+ raise Solis::Error::InvalidAttributeError, "'#{@model_name}.#{attribute}' must be an object"
21
+ end
22
+
23
+ if self.class.metadata[:attributes][attribute.to_s][:node_kind].is_a?(RDF::URI) && value.is_a?(Hash)
24
+ inner_model = self.class.graph.shape_as_model(self.class.metadata[:attributes][attribute.to_s][:datatype].to_s)
25
+ value = inner_model.new(value)
26
+ end
27
+
28
+ # switched off. currently language query parameters returns the value
29
+ # value = {
30
+ # "@language" => @language,
31
+ # "@value" => value
32
+ # } if self.class.metadata[:attributes][attribute.to_s][:datatype_rdf].eql?('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString')
33
+
34
+ value = value.first if value.is_a?(Array) && (attribute.eql?('id') || attribute.eql?(:id))
35
+
36
+ instance_variable_set("@#{attribute}", value)
37
+ else
38
+ raise Solis::Error::InvalidAttributeError, "'#{attribute}' is not part of the definition of #{@model_name}"
39
+ end
40
+ end
41
+
42
+ self.class.make_id_for(self)
43
+ # id = instance_variable_get("@id")
44
+ # if id.nil? || (id.is_a?(String) && id&.empty?)
45
+ # instance_variable_set("@id", SecureRandom.uuid)
46
+ # end
47
+ rescue StandardError => e
48
+ Solis::LOGGER.error(e.message)
49
+ raise Solis::Error::GeneralError, "Unable to create entity #{@model_name}"
50
+ end
51
+
52
+ def name(plural = false)
53
+ if plural
54
+ @model_plural_name
55
+ else
56
+ @model_name
57
+ end
58
+ end
59
+
60
+ def query
61
+ raise "I need a SPARQL endpoint" if self.class.sparql_endpoint.nil?
62
+
63
+ #before_read_proc&.call(self)
64
+ result = Solis::Query.new(self)
65
+ #after_read_proc&.call(result)
66
+ result
67
+ end
68
+
69
+ def to_ttl(resolve_all = true)
70
+ graph = as_graph(self, resolve_all)
71
+ graph.dump(:ttl)
72
+ end
73
+
74
+ def dump(format = :ttl, resolve_all = true)
75
+ graph = as_graph(self, resolve_all)
76
+ graph.dump(format)
77
+ end
78
+
79
+ def to_graph(resolve_all = true)
80
+ as_graph(self, resolve_all)
81
+ end
82
+
83
+ def valid?
84
+ begin
85
+ graph = as_graph(self, false)
86
+ rescue Solis::Error::InvalidAttributeError => e
87
+ Solis::LOGGER.error(e.message)
88
+ end
89
+
90
+ shacl = SHACL.get_shapes(self.class.graph.instance_variable_get(:"@graph"))
91
+ report = shacl.execute(graph)
92
+
93
+ report.conform?
94
+ rescue StandardError => e
95
+ false
96
+ end
97
+
98
+ def destroy
99
+ raise "I need a SPARQL endpoint" if self.class.sparql_endpoint.nil?
100
+ before_delete_proc&.call(self)
101
+
102
+ sparql = SPARQL::Client.new(self.class.sparql_endpoint)
103
+ graph = as_graph(klass = self, resolve_all = false)
104
+ Solis::LOGGER.info graph.dump(:ttl) if ConfigFile[:debug]
105
+
106
+ result = sparql.delete_data(graph, graph: graph.name)
107
+ after_delete_proc&.call(result)
108
+ result
109
+ end
110
+
111
+ def save(validate_dependencies = true)
112
+ raise "I need a SPARQL endpoint" if self.class.sparql_endpoint.nil?
113
+
114
+ before_create_proc&.call(self)
115
+ sparql = SPARQL::Client.new(self.class.sparql_endpoint)
116
+ graph = as_graph(self, validate_dependencies)
117
+
118
+ # File.open('/Users/mehmetc/Dropbox/AllSources/LP/graphiti-api/save.ttl', 'wb') do |file|
119
+ # file.puts graph.dump(:ttl)
120
+ # end
121
+ Solis::LOGGER.info SPARQL::Client::Update::InsertData.new(graph, graph: graph.name).to_s if ConfigFile[:debug]
122
+
123
+ result = sparql.insert_data(graph, graph: graph.name)
124
+ after_create_proc&.call(result)
125
+ result
126
+ rescue StandardError => e
127
+ Solis::LOGGER.error e.message
128
+ Solis::LOGGER.error e.message
129
+ raise e
130
+ end
131
+
132
+ def update(data, validate_dependencies = true)
133
+ raise Solis::Error::GeneralError, "I need a SPARQL endpoint" if self.class.sparql_endpoint.nil?
134
+
135
+ attributes = data.include?('attributes') ? data['attributes'] : data
136
+ raise "id is mandatory in attributes" unless attributes.keys.include?('id')
137
+
138
+ id = attributes.delete('id')
139
+
140
+ sparql = SPARQL::Client.new(self.class.sparql_endpoint)
141
+
142
+ original_klass = self.query.filter({language: nil, filters: { id: [id] } }).find_all.map { |m| m }&.first
143
+ raise Solis::Error::NotFoundError if original_klass.nil?
144
+ updated_klass = original_klass.deep_dup
145
+
146
+ attributes.each_pair do |key, value|
147
+ updated_klass.send(:"#{key}=", value)
148
+ end
149
+
150
+ before_update_proc&.call(original_klass, updated_klass)
151
+
152
+ delete_graph = as_graph(original_klass, false)
153
+ # where_graph = RDF::Graph.new
154
+ # where_graph.name = RDF::URI(self.class.graph_name)
155
+
156
+ where_graph = RDF::Graph.new(graph_name: RDF::URI("#{self.class.graph_name}#{self.name.tableize}/#{id}"), data: RDF::Repository.new)
157
+
158
+ if id.is_a?(Array)
159
+ id.each do |i|
160
+ where_graph << [RDF::URI("#{self.class.graph_name}#{self.name.tableize}/#{i}"), :p, :o]
161
+ end
162
+ else
163
+ where_graph << [RDF::URI("#{self.class.graph_name}#{self.name.tableize}/#{id}"), :p, :o]
164
+ end
165
+
166
+ insert_graph = as_graph(updated_klass, true)
167
+
168
+ #Solis::LOGGER.info delete_graph.dump(:ttl) if ConfigFile[:debug]
169
+ #Solis::LOGGER.info insert_graph.dump(:ttl) if ConfigFile[:debug]
170
+ #Solis::LOGGER.info where_graph.dump(:ttl) if ConfigFile[:debug]
171
+
172
+ #if ConfigFile[:debug]
173
+ delete_insert_query = SPARQL::Client::Update::DeleteInsert.new(delete_graph, insert_graph, where_graph, graph: insert_graph.name).to_s
174
+ delete_insert_query.gsub!('_:p', '?p')
175
+ Solis::LOGGER.info delete_insert_query
176
+ data = sparql.query(delete_insert_query)
177
+ pp data
178
+ #end
179
+
180
+ # sparql.delete_insert(delete_graph, insert_graph, where_graph, graph: insert_graph.name)
181
+
182
+ data = self.query.filter({ filters: { id: [id] } }).find_all.map { |m| m }&.first
183
+ if data.nil?
184
+ sparql.insert_data(insert_graph, graph: insert_graph.name)
185
+ data = self.query.filter({ filters: { id: [id] } }).find_all.map { |m| m }&.first
186
+ end
187
+
188
+ after_update_proc&.call(updated_klass, data)
189
+ data
190
+ rescue StandardError => e
191
+ original_graph = as_graph(original_klass, false)
192
+ Solis::LOGGER.error(e.message)
193
+ Solis::LOGGER.error original_graph.dump(:ttl)
194
+ sparql.insert_data(original_graph, graph: original_graph.name)
195
+
196
+ raise e
197
+ end
198
+
199
+ def self.make_id_for(model)
200
+ id = model.instance_variable_get("@id")
201
+ if id.nil? || (id.is_a?(String) && id&.empty?)
202
+ model.instance_variable_set("@id", SecureRandom.uuid)
203
+ end
204
+ model
205
+ end
206
+
207
+ def self.metadata
208
+ @metadata
209
+ end
210
+
211
+ def self.metadata=(m)
212
+ @metadata = m
213
+ end
214
+
215
+ def self.shapes=(s)
216
+ @shapes = s
217
+ end
218
+
219
+ def self.shapes
220
+ @shapes
221
+ end
222
+
223
+ def self.graph_name
224
+ @graph_name
225
+ end
226
+
227
+ def self.graph_name=(graph_name)
228
+ @graph_name = graph_name
229
+ end
230
+
231
+ def self.graph_prefix=(graph_prefix)
232
+ @graph_prefix = graph_prefix
233
+ end
234
+
235
+ def self.graph_prefix
236
+ @graph_prefix
237
+ end
238
+
239
+ def self.sparql_endpoint
240
+ @sparql_endpoint
241
+ end
242
+
243
+ def self.sparql_endpoint=(sparql_endpoint)
244
+ @sparql_endpoint = sparql_endpoint
245
+ end
246
+
247
+ def self.graph
248
+ @graph
249
+ end
250
+
251
+ def self.graph=(graph)
252
+ @graph = graph
253
+ end
254
+
255
+ def self.language
256
+ Graphiti.context[:object]&.language || Solis::Options.instance.get[:language] || @language || 'en'
257
+ end
258
+
259
+ def self.language=(language)
260
+ @language = language
261
+ end
262
+
263
+ def self.model(level = 0)
264
+ m = { type: self.name.tableize, attributes: {} }
265
+ self.metadata[:attributes].each do |attribute, attribute_metadata|
266
+
267
+ if attribute_metadata.key?(:class) && !attribute_metadata[:class].nil? && attribute_metadata[:class].value =~ /#{self.graph_name}/ && level == 0
268
+ cm = self.graph.shape_as_model(self.metadata[:attributes][attribute][:datatype].to_s).model(level + 1)
269
+ m[:attributes][attribute.to_sym] = cm[:attributes]
270
+ else
271
+ m[:attributes][attribute.to_sym] = { description: attribute_metadata[:comment]&.value,
272
+ mandatory: (attribute_metadata[:mincount].to_i > 0),
273
+ data_type: attribute_metadata[:datatype] }
274
+ end
275
+ end
276
+
277
+ m
278
+ end
279
+
280
+ def self.model_template(level = 0)
281
+ m = { type: self.name.tableize, attributes: {} }
282
+ self.metadata[:attributes].each do |attribute, attribute_metadata|
283
+
284
+ if attribute_metadata.key?(:class) && !attribute_metadata[:class].nil? && attribute_metadata[:class].value =~ /#{self.graph_name}/ && level == 0
285
+ cm = self.graph.shape_as_model(self.metadata[:attributes][attribute][:datatype].to_s).model_template(level + 1)
286
+ m[:attributes][attribute.to_sym] = cm[:attributes]
287
+ else
288
+ m[:attributes][attribute.to_sym] = ''
289
+ end
290
+ end
291
+
292
+ m
293
+ end
294
+
295
+ def self.model_before_read(&blk)
296
+ self.before_read_proc = blk
297
+ end
298
+
299
+ def self.model_after_read(&blk)
300
+ self.after_read_proc = blk
301
+ end
302
+
303
+ def self.model_before_create(&blk)
304
+ self.before_create_proc = blk
305
+ end
306
+
307
+ def self.model_after_create(&blk)
308
+ self.after_create_proc = blk
309
+ end
310
+
311
+ def self.model_before_update(&blk)
312
+ self.before_update_proc = blk
313
+ end
314
+
315
+ def self.model_after_update(&blk)
316
+ self.after_update_proc = blk
317
+ end
318
+
319
+ def self.model_before_delete(&blk)
320
+ self.before_delete_proc = blk
321
+ end
322
+
323
+ def self.model_after_delete(&blk)
324
+ self.after_delete_proc = blk
325
+ end
326
+
327
+ private
328
+
329
+ def as_graph(klass = self, resolve_all = true)
330
+ graph = RDF::Graph.new
331
+ graph.name = RDF::URI(self.class.graph_name)
332
+ id = build_ttl_objekt2(graph, klass, [], resolve_all)
333
+
334
+ graph
335
+ end
336
+
337
+ def build_ttl_objekt2(graph, klass, hierarchy = [], resolve_all = true)
338
+ hierarchy.push("#{klass.name}(#{klass.instance_variables.include?(:@id) ? klass.instance_variable_get("@id") : ''})")
339
+
340
+ graph_name = self.class.graph_name
341
+ klass_name = klass.class.name
342
+ klass_metadata = klass.class.metadata
343
+ uuid = klass.instance_variable_get("@id") || SecureRandom.uuid
344
+ id = RDF::URI("#{graph_name}#{klass_name.tableize}/#{uuid}")
345
+
346
+ graph << [id, RDF::RDFV.type, klass_metadata[:target_class]]
347
+
348
+ #load existing object and overwrite
349
+ original_klass = klass.query.filter({ filters: { id: [uuid] } }).find_all { |f| f.id == uuid }.first || nil
350
+
351
+ if original_klass.nil?
352
+ original_klass = klass
353
+ else
354
+ resolve_all = false
355
+ klass.instance_variables.map { |m| m.to_s.gsub(/^@/, '') }
356
+ .select { |s| !["model_name", "model_plural_name"]
357
+ .include?(s) }.each do |attribute, value|
358
+ data = klass.instance_variable_get("@#{attribute}")
359
+ original_data = original_klass.instance_variable_get("@#{attribute.to_s}")
360
+ original_klass.instance_variable_set("@#{attribute}", data) unless original_data.eql?(data)
361
+ end
362
+ end
363
+
364
+ make_graph(graph, hierarchy, id, original_klass, klass_metadata, resolve_all)
365
+
366
+ hierarchy.pop
367
+ id
368
+ end
369
+
370
+ def make_graph(graph, hierarchy, id, klass, klass_metadata, resolve_all)
371
+ klass_metadata[:attributes].each do |attribute, metadata|
372
+ data = klass.instance_variable_get("@#{attribute}")
373
+
374
+ raise Solis::Error::InvalidAttributeError,
375
+ "#{hierarchy.join('.')}.#{attribute} min=#{metadata[:mincount]} and max=#{metadata[:maxcount]}" if data.nil? &&
376
+ metadata[:mincount] > 0 &&
377
+ graph.query(RDF::Query.new({ attribute.to_sym => { RDF.type => metadata[:node] } })).size == 0
378
+
379
+ # skip if nil or an object that is empty
380
+ next if data.nil? || ([Hash, Array, String].include?(data.class) && data&.empty?)
381
+
382
+ case metadata[:datatype_rdf]
383
+ when 'http://www.w3.org/2001/XMLSchema#boolean'
384
+ data = false if data.nil?
385
+ when 'http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON'
386
+ data = data.to_json
387
+ end
388
+
389
+ #make it an object
390
+ unless metadata[:node_kind].nil?
391
+ model = self.class.graph.shape_as_model(metadata[:datatype].to_s)
392
+ if data.is_a?(Hash)
393
+ data = model.new(data)
394
+ elsif data.is_a?(Array)
395
+ data = data.map { |m| m.is_a?(Hash) ? model.new(m) : m }
396
+ end
397
+ end
398
+
399
+ data = [data] unless data.is_a?(Array)
400
+
401
+ data.each do |d|
402
+ if defined?(d.name) && self.class.graph.shape?(d.name)
403
+ if self.class.graph.shape_as_model(d.name.to_s).metadata[:attributes].select{|_,v| v[:node_kind].is_a?(RDF::URI)}.size > 0 &&
404
+ hierarchy.select{|s| s =~ /^#{d.name.to_s}/}.size == 0
405
+ internal_resolve = false
406
+ d = build_ttl_objekt2(graph, d, hierarchy, internal_resolve)
407
+ elsif self.class.graph.shape_as_model(d.name.to_s) && hierarchy.select{|s| s =~ /^#{d.name.to_s}/}.size == 0
408
+ internal_resolve = false
409
+ d = build_ttl_objekt2(graph, d, hierarchy, internal_resolve)
410
+ else
411
+ d = "#{klass.class.graph_name}#{attribute.tableize}/#{d.id}"
412
+ end
413
+ end
414
+
415
+ if d.is_a?(Array) && d.length == 1
416
+ d = d.first
417
+ end
418
+
419
+ d = if metadata[:datatype_rdf].eql?('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString')
420
+ if d.is_a?(Hash) && (d.keys - ["@language", "@value"]).size == 0
421
+ if d['@value'].is_a?(Array)
422
+ d_r = []
423
+ d['@value'].each do |v|
424
+ d_r << RDF::Literal.new(v, language: d['@language'])
425
+ end
426
+ d_r
427
+ else
428
+ RDF::Literal.new(d['@value'], language: d['@language'])
429
+ end
430
+ else
431
+ RDF::Literal.new(d, language: @language)
432
+ end
433
+ elsif metadata[:datatype_rdf].eql?('http://www.w3.org/2001/XMLSchema#anyURI') || metadata[:node].is_a?(RDF::URI)
434
+ RDF::URI(d)
435
+ elsif metadata[:datatype_rdf].eql?('http://www.w3.org/2006/time#DateTimeInterval')
436
+ begin
437
+ datatype = metadata[:datatype_rdf]
438
+ RDF::Literal.new(ISO8601::TimeInterval.parse(d).to_s, datatype: datatype)
439
+ rescue StandardError => e
440
+ raise Solis::Error::InvalidDatatypeError, "#{hierarchy.join('.')}.#{attribute}: #{e.message}"
441
+ end
442
+ else
443
+ datatype = RDF::Vocabulary.find_term(metadata[:datatype_rdf])
444
+ datatype = metadata[:node] if datatype.nil?
445
+ datatype = metadata[:datatype_rdf] if datatype.nil?
446
+ RDF::Literal.new(d, datatype: datatype)
447
+ end
448
+
449
+ unless d.valid?
450
+ LOGGER.warn("Invalid datatype for #{hierarchy.join('.')}.#{attribute}")
451
+ end
452
+
453
+ if d.is_a?(Array)
454
+ d.each do |v|
455
+ graph << [id, RDF::URI("#{metadata[:path]}"), v]
456
+ end
457
+ else
458
+ graph << [id, RDF::URI("#{metadata[:path]}"), d]
459
+ end
460
+ end
461
+ end
462
+ rescue StandardError => e
463
+ Solis::LOGGER.error(e.message)
464
+ raise e
465
+ end
466
+
467
+ def build_ttl_objekt(graph, klass, hierarchy = [], resolve_all = true)
468
+ hierarchy.push("#{klass.name}(#{klass.instance_variables.include?(:@id) ? klass.instance_variable_get("@id") : ''})")
469
+ sparql_endpoint = self.class.sparql_endpoint
470
+ if klass.instance_variables.include?(:@id) && hierarchy.length > 1
471
+ unless sparql_endpoint.nil?
472
+ existing_klass = klass.query.filter({ filters: { id: [klass.instance_variable_get("@id")] } }).find_all { |f| f.id == klass.instance_variable_get("@id") }
473
+ if !existing_klass.nil? && !existing_klass.empty? && existing_klass.first.is_a?(klass.class)
474
+ klass = existing_klass.first
475
+ end
476
+ end
477
+ end
478
+
479
+ uuid = klass.instance_variable_get("@id") || SecureRandom.uuid
480
+ id = RDF::URI("#{self.class.graph_name}#{klass.class.name.tableize}/#{uuid}")
481
+ graph << [id, RDF::RDFV.type, klass.class.metadata[:target_class]]
482
+
483
+ klass.class.metadata[:attributes].each do |attribute, metadata|
484
+ data = klass.instance_variable_get("@#{attribute}")
485
+ if data.nil? && metadata[:datatype_rdf].eql?('http://www.w3.org/2001/XMLSchema#boolean')
486
+ data = false
487
+ end
488
+
489
+ if metadata[:datatype_rdf].eql?("http://www.w3.org/1999/02/22-rdf-syntax-ns#JSON")
490
+ data = data.to_json
491
+ end
492
+
493
+ if data.nil? && metadata[:mincount] > 0
494
+ raise Solis::Error::InvalidAttributeError, "#{hierarchy.join('.')}.#{attribute} min=#{metadata[:mincount]} and max=#{metadata[:maxcount]}"
495
+ end
496
+
497
+ next if data.nil? || ([Hash, Array, String].include?(data.class) && data&.empty?)
498
+
499
+ data = [data] unless data.is_a?(Array)
500
+ model = nil
501
+ model = klass.class.graph.shape_as_model(klass.class.metadata[:attributes][attribute][:datatype].to_s) unless klass.class.metadata[:attributes][attribute][:node_kind].nil?
502
+
503
+ data.each do |d|
504
+ original_d = d
505
+ if model
506
+ target_node = model.metadata[:target_node].value.split('/').last.gsub(/Shape$/, '')
507
+ if model.ancestors[0..model.ancestors.find_index(Solis::Model) - 1].map { |m| m.name }.include?(target_node)
508
+ parent_model = model.graph.shape_as_model(target_node)
509
+ end
510
+ end
511
+
512
+ if model && d.is_a?(Hash)
513
+ #TODO: figure out in what use case we need the parent_model
514
+ # model_instance = if parent_model
515
+ # parent_model.new(d)
516
+ # else
517
+ # model.new(d)
518
+ # end
519
+
520
+ # model_instance = model.new(d)
521
+ model_instance = model.descendants.map { |m| m&.new(d) rescue nil }.compact.first || nil
522
+ model_instance = model.new(d) if model_instance.nil?
523
+
524
+ if resolve_all
525
+ d = build_ttl_objekt(graph, model_instance, hierarchy, false)
526
+ else
527
+ real_model = model_instance.query.filter({ filters: { id: model_instance.id } }).find_all { |f| f.id == model_instance.id }&.first
528
+ d = RDF::URI("#{self.class.graph_name}#{real_model ? real_model.name.tableize : model_instance.name.tableize}/#{model_instance.id}")
529
+ end
530
+ elsif model && d.is_a?(model)
531
+ if resolve_all
532
+ if parent_model
533
+ model_instance = parent_model.new({ id: d.id })
534
+ d = build_ttl_objekt(graph, model_instance, hierarchy, false)
535
+ else
536
+ d = build_ttl_objekt(graph, d, hierarchy, false)
537
+ end
538
+ else
539
+ real_model = model.new.query.filter({ filters: { id: d.id } }).find_all { |f| f.id == d.id }&.first
540
+ d = RDF::URI("#{self.class.graph_name}#{real_model ? real_model.name.tableize : model.name.tableize}/#{d.id}")
541
+ end
542
+ else
543
+ datatype = RDF::Vocabulary.find_term(metadata[:datatype_rdf] || metadata[:node])
544
+ if datatype && datatype.datatype?
545
+ d = if metadata[:datatype_rdf].eql?('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString')
546
+ RDF::Literal.new(d, language: self.class.language)
547
+ else
548
+ if metadata[:datatype_rdf].eql?('http://www.w3.org/2001/XMLSchema#anyURI')
549
+ RDF::URI(d)
550
+ else
551
+ RDF::Literal.new(d, datatype: datatype)
552
+ end
553
+ end
554
+ d = (d.object.value rescue d.object) unless d.valid?
555
+ end
556
+ end
557
+
558
+ graph << [id, RDF::URI("#{self.class.graph_name}#{attribute}"), d]
559
+ end
560
+ end
561
+ hierarchy.pop
562
+ id
563
+ end
564
+ end
565
+ end
@@ -0,0 +1,19 @@
1
+ module Solis
2
+ class Options
3
+ @instance = new
4
+ private_class_method :new
5
+
6
+ def self.instance
7
+ @instance
8
+ end
9
+
10
+ def set=(options)
11
+ @options = options
12
+ @options
13
+ end
14
+
15
+ def get
16
+ @options || {}
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,93 @@
1
+ require 'uuidtools'
2
+
3
+ module Solis
4
+ class Query
5
+ class Construct
6
+ def initialize(model)
7
+ @model = model
8
+ @sparql_endpoint = @model.class.sparql_endpoint
9
+ @sparql_client = SPARQL::Client.new(@sparql_endpoint, graph: @model.class.graph_name, read_timeout: 120)
10
+ @construct_cache = File.absolute_path(Solis::Options.instance.get[:cache])
11
+ @moneta = Moneta.new(:File, dir: "#{@construct_cache}/construct", expires: Solis::Options.instance.get[:cache_expire])
12
+ end
13
+
14
+ def exists?
15
+ File.exist?(file_path)
16
+ end
17
+
18
+ def load
19
+ construct_path = file_path
20
+ raise Solis::Error::NotFoundError, "Construct not found at #{construct_path} " unless exists?
21
+ File.read(construct_path)
22
+ end
23
+
24
+ def file_path
25
+ "#{ConfigFile.path}/constructs/#{@model.name.tableize.singularize}.sparql"
26
+ end
27
+
28
+ def file_path_hash
29
+ UUIDTools::UUID.sha1_create(UUIDTools::UUID_URL_NAMESPACE, file_path).to_s
30
+ end
31
+
32
+ def run
33
+ construct_query = load
34
+ sparql_repository = @sparql_endpoint
35
+
36
+ from_cache = Graphiti.context[:object].from_cache || '1'
37
+ if construct_query && construct_query =~ /construct/
38
+ if @moneta.key?(file_path_hash) && from_cache.eql?('1')
39
+ sparql_repository = @moneta[file_path_hash]
40
+ else
41
+ @sparql_client = SPARQL::Client.new(@sparql_endpoint, read_timeout: 120)
42
+ result = @sparql_client.query(construct_query)
43
+ repository=RDF::Repository.new
44
+ result.each {|s| repository << [s[:s], s[:p], s[:o]]}
45
+ sparql_repository = repository
46
+ @moneta.store(file_path_hash, repository, expires: ConfigFile[:solis][:cache_expire] || 86400)
47
+ end
48
+ elsif construct_query && construct_query =~ /insert/
49
+ unless @moneta.key?(file_path_hash)
50
+ clear_construct
51
+ result = @sparql_client.query(construct_query)
52
+ LOGGER.info(result[0]['callret-0'].value) unless result.empty?
53
+ @moneta.store(file_path_hash, repository, expires: ConfigFile[:solis][:cache_expire] || 86400) unless result[0]['callret-0'].value =~ /0 triples/
54
+ end
55
+ end
56
+
57
+ #SPARQL::Client.new(@sparql_endpoint, graph: @model.class.graph_name, read_timeout: 120)
58
+ SPARQL::Client.new(sparql_repository, read_timeout: 120)
59
+ end
60
+
61
+ private
62
+
63
+ def parsed_graph_name
64
+ URI.parse(@model.class.graph_name)
65
+ end
66
+
67
+ def construct_graph_name
68
+ "#{parsed_graph_name.scheme}://#{@model.name.underscore}.#{parsed_graph_name.host}/"
69
+ end
70
+
71
+ def created_at
72
+ created_at = nil
73
+ result = @sparql_client.query("select * from <#{construct_graph_name}> where {<#{construct_graph_name}_metadata> <#{construct_graph_name}created_at> ?_created_at}")
74
+ unless result.empty?
75
+ created_at = result[0]._created_at.object
76
+ end
77
+
78
+ created_at
79
+ end
80
+
81
+ def clear_construct
82
+ result = @sparql_client.query("clear graph <#{construct_graph_name}>")
83
+ LOGGER.info(result[0]['callret-0'].value)
84
+ end
85
+
86
+ def set_metadata
87
+ result = @sparql_client.query("insert into <#{construct_graph_name}> { <#{construct_graph_name}_metadata> <#{construct_graph_name}created_at> \"#{Time.now.xmlschema}\"^^xsd:dateTime}")
88
+ LOGGER.info(result[0]['callret-0'].value)
89
+ end
90
+
91
+ end
92
+ end
93
+ end