solis 0.64.0

Sign up to get free protection for your applications and to get access to all the features.
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