solis 0.125.0 → 0.127.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c29d1cb5fcfe9ad5579f22c3971aa794edc602b6afa3679a2113e0caecd01aa4
4
- data.tar.gz: e067c8d0ad39a30dd49c921f5beded800d0f93de83817911c6fafd32486b89dd
3
+ metadata.gz: d7cf89eceec897d7b5fe15ef1669b031480a7aab2850bd5638cfac379e4798ae
4
+ data.tar.gz: 56a9ce9652925e1be2904dc150bba6d31f3f2ca13870641c883fb0dde65b341c
5
5
  SHA512:
6
- metadata.gz: 939e20fe0909c981ee02d29766b5ac43446db72d94df6a49ca68208a4a7c8d969a543c859b9f8c9e18bb843f5dd34e186356cdd245aa0abb8120b3bee0a7e4ad
7
- data.tar.gz: 5fb1ae2ea0fd011eb0aab4d306252a27f9f238a0b908fe458895bd1d238974da59ee57d4520dc14a34e49e2fe2fa60bb6b4d8366f3cd4b9fed4c63ce15594527
6
+ metadata.gz: 499ff31724baaa76c0ae960916d5b01c0f25f535e07e89fd0cba72cbc042fe271898b5c422842201463daef4903d1207bf4fdcbd248e3bbff346153f4dba1765
7
+ data.tar.gz: 264f64d77cf8b15d708da87cbf0602a6f8be5282c04a365f52966e865895032e9c08b4952e99abbcb6e5f4aa80fcbd20887e3e4d9c961398e350a4cbaff17b0b
@@ -0,0 +1,22 @@
1
+ module Solis
2
+ # Normalizes language tags so the same language always ends up under the same key,
3
+ # no matter if it came from a 'Label_XX' sheet column or from a parsed rdfs:label
4
+ # (RDF.rb lowercases language tags, ex. 'nl-BE' is read back as :'nl-be')
5
+ module LanguageTag
6
+ # 'nl' -> :nl, 'nl_be' -> :'nl-BE', 'zh_hant' -> :'zh-Hant'
7
+ def self.normalize(tag)
8
+ return nil if tag.nil? || tag.to_s.strip.empty?
9
+
10
+ primary, *subtags = tag.to_s.strip.split(/[-_]/)
11
+ subtags.map! do |subtag|
12
+ case subtag.length
13
+ when 2 then subtag.upcase
14
+ when 4 then subtag.capitalize
15
+ else subtag.downcase
16
+ end
17
+ end
18
+
19
+ ([primary.downcase] + subtags).join('-').to_sym
20
+ end
21
+ end
22
+ end
data/lib/solis/model.rb CHANGED
@@ -583,6 +583,7 @@ values ?s {<#{self.graph_id}>}
583
583
  end
584
584
 
585
585
  attribute_data = { name: attribute,
586
+ label: attribute_metadata[:label] || {},
586
587
  data_type: attribute_metadata[:datatype],
587
588
  mandatory: (attribute_metadata[:mincount].to_i > 0),
588
589
  repeatable: (attribute_metadata[:maxcount].to_i > 1 || attribute_metadata[:maxcount].nil?),
data/lib/solis/query.rb CHANGED
@@ -11,6 +11,15 @@ module Solis
11
11
  include Enumerable
12
12
  include Solis::QueryFilter
13
13
 
14
+ # XSD numeric datatypes must be ordered by value; wrapping them in STR() would sort
15
+ # them lexically ("100" < "9"). Every other datatype is STR()-wrapped in the outer
16
+ # ORDER BY (see #sort_key_expression) to dodge a Virtuoso collation bug.
17
+ XSD_NUMERIC_DATATYPES = %w[
18
+ integer decimal float double long int short byte
19
+ nonNegativeInteger nonPositiveInteger negativeInteger positiveInteger
20
+ unsignedLong unsignedInt unsignedShort unsignedByte
21
+ ].map { |t| "http://www.w3.org/2001/XMLSchema##{t}" }.freeze
22
+
14
23
  def self.run(entity, query, options = {})
15
24
  Solis::Query::Runner.run(entity, query, options)
16
25
  end
@@ -96,6 +105,13 @@ module Solis
96
105
  @filter = {values: ["VALUES ?type {#{target_class}}"], concepts: ['?concept a ?type .'] }
97
106
  @sort = 'ORDER BY ?concept'
98
107
  @sort_select = ''
108
+ # @sort_project carries the sort key(s) out of the inner (paginated) subquery so the
109
+ # outer query can re-sort on them; @sort_outer is that outer ORDER BY. A subquery's
110
+ # ORDER BY only decides which rows survive LIMIT/OFFSET — it does NOT propagate
111
+ # through the enclosing join — so the outer query must sort too. See #sort for the
112
+ # datatype-aware STR() handling that the outer ORDER BY needs.
113
+ @sort_project = ''
114
+ @sort_outer = ''
99
115
  @language = Graphiti.context[:object]&.language || Solis::Options.instance.get[:language] || 'en'
100
116
  @query_cache = self.class.shared_query_cache
101
117
  end
@@ -113,19 +129,31 @@ module Solis
113
129
  def sort(params)
114
130
  @sort = ''
115
131
  @sort_select = ''
132
+ @sort_project = ''
133
+ @sort_outer = ''
116
134
  if params.key?(:sort)
117
135
  i = 0
136
+ outer = ''
118
137
  params[:sort].each do |attribute, direction|
119
- path = @model.class.metadata[:attributes][attribute.to_s][:path]
120
- @sort_select += "optional {\n" if @model.class.metadata[:attributes][attribute.to_s][:mincount] == 0
138
+ meta = @model.class.metadata[:attributes][attribute.to_s]
139
+ path = meta[:path]
140
+ @sort_select += "optional {\n" if meta[:mincount] == 0
121
141
  @sort_select += "?concept <#{path}> ?__#{attribute} . "
122
- @sort_select += "}\n" if @model.class.metadata[:attributes][attribute.to_s][:mincount] == 0
142
+ @sort_select += "}\n" if meta[:mincount] == 0
123
143
  @sort += ',' if i.positive?
124
144
  @sort += "#{direction.to_s.upcase}(?__#{attribute})"
145
+ # Carry the sort key out of the subquery so the outer query can re-sort on it.
146
+ @sort_project += " ?__#{attribute}"
147
+ outer += ',' if i.positive?
148
+ outer += "#{direction.to_s.upcase}(#{sort_key_expression(attribute, meta)})"
125
149
  i += 1
126
150
  end
127
151
 
128
- @sort = "ORDER BY #{@sort}" if i.positive?
152
+ if i.positive?
153
+ @sort = "ORDER BY #{@sort}"
154
+ # ?s tiebreaker keeps each subject's triples contiguous and ties deterministic.
155
+ @sort_outer = "ORDER BY #{outer} ?s"
156
+ end
129
157
  end
130
158
 
131
159
  self
@@ -219,6 +247,11 @@ module Solis
219
247
  # object when the open `?s ?p ?o` pattern is combined with the disjunctive
220
248
  # language_filter, returning the rdf:type object for every row. Aliasing through
221
249
  # BIND blocks that optimizer rewrite and is a no-op on engines without the bug.
250
+ # Outer ordering: the inner subquery's ORDER BY only selects which rows survive
251
+ # LIMIT/OFFSET; it does NOT propagate through the enclosing join. The outer query
252
+ # therefore re-sorts on the same key(s) (@sort_outer, datatype-aware STR()). When
253
+ # no sort is requested, `order by ?s` gives a cheap deterministic default order.
254
+ outer_order = @sort_outer.empty? ? 'order by ?s' : @sort_outer
222
255
  query = %(
223
256
  #{prefixes}
224
257
  SELECT ?s ?p ?o WHERE {
@@ -229,7 +262,7 @@ SELECT ?s ?p ?o WHERE {
229
262
  }
230
263
  #{language_filter}
231
264
  }
232
- order by ?s
265
+ #{outer_order}
233
266
  )
234
267
 
235
268
  Solis::LOGGER.info(query) if ConfigFile[:debug]
@@ -274,8 +307,12 @@ order by ?s
274
307
  end
275
308
 
276
309
  def core_query(relationship)
310
+ # @sort_project re-exposes the sort key(s) bound in @sort_select so the OUTER query
311
+ # can ORDER BY them. NB: for a multi-valued sort attribute this turns DISTINCT into
312
+ # one row per (concept, value) pair, which would skew LIMIT/OFFSET — sort attributes
313
+ # are expected to be single-valued.
277
314
  core_query = %(
278
- SELECT distinct (?concept AS ?s) WHERE {
315
+ SELECT distinct (?concept AS ?s)#{@sort_project} WHERE {
279
316
  #{@filter[:values].join("\n")}
280
317
  #{relationship}
281
318
  #{@filter[:concepts].join("\n")}
@@ -286,6 +323,21 @@ order by ?s
286
323
  )
287
324
  end
288
325
 
326
+ # SPARQL expression to sort an attribute by in the OUTER query.
327
+ #
328
+ # The outer (post-join) ORDER BY on this Virtuoso build (08.03.3335) mis-collates
329
+ # language-tagged literals, so string-ish keys are wrapped in STR() — which fixes the
330
+ # ordering and is order-preserving for xsd:string, dates, gYear, booleans and EDTF.
331
+ # Numeric keys must NOT be wrapped: STR() would order them lexically ("100" < "9"),
332
+ # and numbers carry no language tag so they are unaffected by the collation bug.
333
+ def sort_key_expression(attribute, meta = @model.class.metadata[:attributes][attribute.to_s])
334
+ if XSD_NUMERIC_DATATYPES.include?(meta[:datatype_rdf].to_s)
335
+ "?__#{attribute}"
336
+ else
337
+ "STR(?__#{attribute})"
338
+ end
339
+ end
340
+
289
341
  def prefixes
290
342
  "
291
343
  PREFIX sh: <http://www.w3.org/ns/shacl#>
@@ -43,6 +43,11 @@ module Solis
43
43
  raise "_REFERENCES tab must have ['sheeturl', 'description', 'entityrange'] as a header at row 1" unless (%w[sheeturl description entityrange] - references.header).length == 0
44
44
  end
45
45
 
46
+ if sheets.key?('_CATEGORIES')
47
+ categories = sheets['_CATEGORIES']
48
+ raise "_CATEGORIES tab must have ['name'] as a header at row 1" unless (%w[name] - categories.header).length == 0
49
+ end
50
+
46
51
  sheets.each do |sheet_name, sheet|
47
52
  if sheet_name !~ /^_/
48
53
  entities = sheets[sheet_name]
@@ -113,6 +118,7 @@ module Solis
113
118
  category: e['categories'],
114
119
  plural: e['nameplural'],
115
120
  label: e['name'].to_s.strip,
121
+ labels: parse_labels(e),
116
122
  sub_class_of: e['subclassof'].nil? || e['subclassof'].empty? ? [] : [e['subclassof']],
117
123
  same_as: e['sameas'],
118
124
  properties: entity_data })
@@ -135,6 +141,26 @@ module Solis
135
141
  raise Solis::Error::GeneralError, e.message
136
142
  end
137
143
 
144
+ # Matches the 'Label_XX' columns (header is downcased by ::Sheet#header) where
145
+ # XX is a language tag, ex. 'Label_NL', 'Label_EN', 'Label_nl-BE'
146
+ LANGUAGE_LABEL_COLUMN = /\Alabel_(?<language>[a-z]{2,3}([-_][a-z0-9]{2,8})*)\z/.freeze
147
+
148
+ # Collects all 'Label_XX' columns of a row into { nl: 'Publiceren', en: 'Publish' }
149
+ def parse_labels(row)
150
+ labels = {}
151
+ return labels unless row.respond_to?(:each_pair)
152
+
153
+ row.each_pair do |column, value|
154
+ match = LANGUAGE_LABEL_COLUMN.match(column.to_s)
155
+ next if match.nil?
156
+ next if value.nil? || value.to_s.strip.empty?
157
+
158
+ labels[Solis::LanguageTag.normalize(match[:language])] = value.to_s.strip
159
+ end
160
+
161
+ labels
162
+ end
163
+
138
164
  def parse_entity_data(entity_name, graph_prefix, _graph_name, e, options = {})
139
165
  properties = {}
140
166
  entity_data = e
@@ -168,31 +194,9 @@ module Solis
168
194
  same_as: p['sameas'],
169
195
  order: p['order'],
170
196
  category: p['categories'],
197
+ label: parse_labels(p),
171
198
  description: p['description']
172
199
  }
173
-
174
- # unless graph_prefix.eql?(datatype_prefix.to_sym)
175
- # prefixes = options[:prefixes]
176
- # if prefixes.key?(datatype_prefix.to_sym) && !prefixes[datatype_prefix.to_sym][:sheet_url].empty?
177
- # tmp = URI(prefixes[datatype_prefix.to_sym][:sheet_url]).path.split('/')
178
- # spreadsheet_id = tmp[tmp.index('d') + 1]
179
- #
180
- # processed_remote_sheet = {}
181
- # if prefixes[datatype_prefix.to_sym].key?(:data) && !prefixes[datatype_prefix.to_sym][:data].empty?
182
- # processed_remote_sheet = prefixes[datatype_prefix.to_sym][:data]
183
- # else
184
- # if options[:follow]
185
- # sleep 30
186
- # remote_sheet = read_sheets(options[:key], spreadsheet_id, { from_cache: true })
187
- # processed_remote_sheet = process_sheet(options[:key], remote_sheet, {follow: false})
188
- # prefixes[datatype_prefix.to_sym][:data] = processed_remote_sheet
189
- # end
190
- # end
191
- #
192
- # processed_remote_sheet
193
- # end
194
- # end
195
-
196
200
  end
197
201
  end
198
202
  end
@@ -266,12 +270,32 @@ hide empty members
266
270
  datatypes[datatype][as]
267
271
  end
268
272
 
273
+ # Turns { nl: 'Publiceren', en: 'Publish' } into a single rdfs:label statement with
274
+ # one language tagged literal per language, ex.
275
+ # rdfs:label "Publiceren"@nl, "Publish"@en ;
276
+ def build_labels(labels, rdfs_prefix, indent)
277
+ return '' if labels.nil? || labels.empty?
278
+
279
+ literals = labels.map do |language, value|
280
+ %("#{value.to_s.gsub('"', "'").gsub(/\n|\r/, '')}"@#{language})
281
+ end
282
+
283
+ "\n#{indent}#{rdfs_prefix}:label #{literals.join(', ')} ;"
284
+ end
285
+
269
286
  def build_shacl(datas)
270
287
  shacl_prefix = datas.first[:ontologies][:all].select { |_, v| v[:uri] =~ /shacl/ }.keys.first
271
288
  shacl_prefix = 'sh' if shacl_prefix.nil?
272
289
 
290
+ rdfs_prefix = datas.first[:ontologies][:all].select { |_, v| v[:uri] =~ /rdf-schema/ }.keys.first
291
+
273
292
  out = header(datas.first)
274
293
 
294
+ if rdfs_prefix.nil?
295
+ rdfs_prefix = 'rdfs'
296
+ out += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
297
+ end
298
+
275
299
  datas.each do |data|
276
300
  data[:entities].each do |entity_name, metadata|
277
301
  graph_prefix = data[:ontologies][:base][:prefix]
@@ -300,7 +324,7 @@ hide empty members
300
324
  unless node.nil? || node.empty?
301
325
  "\n #{shacl_prefix}:node #{node} ;"
302
326
  end}
303
- #{shacl_prefix}:name "#{label}" ;
327
+ #{shacl_prefix}:name "#{label}" ;#{build_labels(metadata[:labels], rdfs_prefix, ' ' * 4)}
304
328
  )
305
329
  metadata[:properties].each do |property, property_metadata|
306
330
  attribute = property.to_s.strip
@@ -314,15 +338,16 @@ hide empty members
314
338
  order = property_metadata.key?(:order) && property_metadata[:order] ? property_metadata[:order]&.strip : nil
315
339
  category = property_metadata.key?(:category) && property_metadata[:category] ? property_metadata[:category]&.strip : nil
316
340
  category = category.nil? || category.empty? ? nil : category
341
+ labels = build_labels(property_metadata[:label], rdfs_prefix, ' ' * 17)
317
342
 
318
343
  unless category.nil?
319
- group = "#{category.classify}Group"
344
+ group = "#{category.camelize}Group"
320
345
  groups[group] = category unless groups.key?(group)
321
346
  end
322
347
 
323
348
  if datatype =~ /^#{graph_prefix}:/ || datatype =~ /^<#{graph_name}/
324
349
  out += %( #{shacl_prefix}:property [#{shacl_prefix}:path #{path} ;
325
- #{shacl_prefix}:name "#{attribute}" ;
350
+ #{shacl_prefix}:name "#{attribute}" ;#{labels}
326
351
  #{shacl_prefix}:description "#{description}" ;#{order.nil? ? '' : "\n #{shacl_prefix}:order #{order} ;"}
327
352
  #{category.nil? ? '' : "\n #{shacl_prefix}:group #{graph_prefix}:#{group} ;"}
328
353
  #{shacl_prefix}:nodeKind #{shacl_prefix}:IRI ;
@@ -332,7 +357,7 @@ hide empty members
332
357
  else
333
358
  if datatype.eql?('rdf:langString') && max_count.eql?('1')
334
359
  out += %( #{shacl_prefix}:property [#{shacl_prefix}:path #{path} ;
335
- #{shacl_prefix}:name "#{attribute}";
360
+ #{shacl_prefix}:name "#{attribute}";#{labels}
336
361
  #{shacl_prefix}:description "#{description}" ;#{order.nil? ? '' : "\n #{shacl_prefix}:order #{order} ;"}
337
362
  #{shacl_prefix}:uniqueLang true ;
338
363
  #{shacl_prefix}:datatype #{datatype} ;#{min_count =~ /\d+/ ? "\n #{shacl_prefix}:minCount #{min_count} ;" : ''}
@@ -340,7 +365,7 @@ hide empty members
340
365
  )
341
366
  elsif datatype.eql?('rdf:langString')
342
367
  out += %( #{shacl_prefix}:property [#{shacl_prefix}:path #{path} ;
343
- #{shacl_prefix}:name "#{attribute}";
368
+ #{shacl_prefix}:name "#{attribute}";#{labels}
344
369
  #{shacl_prefix}:description "#{description}" ;#{order.nil? ? '' : "\n #{shacl_prefix}:order #{order} ;"}
345
370
  #{category.nil? ? '' : "\n #{shacl_prefix}:group #{graph_prefix}:#{group} ;"}
346
371
  #{shacl_prefix}:datatype #{datatype} ;#{min_count =~ /\d+/ ? "\n #{shacl_prefix}:minCount #{min_count} ;" : ''}#{max_count =~ /\d+/ ? "\n #{shacl_prefix}:maxCount #{max_count} ;" : ''}
@@ -348,7 +373,7 @@ hide empty members
348
373
  )
349
374
  else
350
375
  out += %( #{shacl_prefix}:property [#{shacl_prefix}:path #{path} ;
351
- #{shacl_prefix}:name "#{attribute}";
376
+ #{shacl_prefix}:name "#{attribute}";#{labels}
352
377
  #{shacl_prefix}:description "#{description}" ;#{order.nil? ? '' : "\n #{shacl_prefix}:order #{order} ;"}
353
378
  #{shacl_prefix}:datatype #{datatype} ;#{min_count =~ /\d+/ ? "\n #{shacl_prefix}:minCount #{min_count} ;" : ''}#{max_count =~ /\d+/ ? "\n #{shacl_prefix}:maxCount #{max_count} ;" : ''}
354
379
  ] ;
@@ -362,8 +387,8 @@ hide empty members
362
387
  groups.each do |group, category|
363
388
  out += %(
364
389
  #{graph_prefix}:#{group}
365
- a sh:PropertyGroup ;
366
- rdfs:label "#{category}" .
390
+ a #{shacl_prefix}:PropertyGroup ;
391
+ #{rdfs_prefix}:label "#{category}" .
367
392
 
368
393
  )
369
394
  end
@@ -489,7 +514,7 @@ hide empty members
489
514
  puts e.message
490
515
  end
491
516
 
492
- def build_inflections(datas)
517
+ def build_inflections(datas, categories = [])
493
518
  inflections = {}
494
519
  datas.each do |data|
495
520
  data[:entities].each do |entity, metadata|
@@ -498,6 +523,16 @@ hide empty members
498
523
  end
499
524
  end
500
525
 
526
+ # categories are uninflected, singular and plural are the same word.
527
+ # an entity by the same name keeps its own plural.
528
+ # categories.each do |category|
529
+ # name = category.to_s.strip
530
+ # next if name.empty?
531
+ #
532
+ # inflections[name.to_sym] = name unless inflections.key?(name.to_sym) || inclections.value?(name.to_s)
533
+ # inflections[name.underscore.to_sym] = name.underscore unless inflections.key?(name.underscore.to_sym) || inclections.value?(name.to_s)
534
+ # end
535
+
501
536
  inflections.to_json
502
537
  rescue StandardError => e
503
538
  puts e.message
@@ -743,6 +778,11 @@ hide empty members
743
778
 
744
779
  options[:prefixes] = prefixes
745
780
  options[:metadata] = metadata
781
+
782
+ # like _REFERENCES, _CATEGORIES only lives in the main sheet
783
+ categories = sheet_data.is_a?(Hash) && sheet_data.key?('_CATEGORIES') ? sheet_data['_CATEGORIES'].map { |c| c['name'] } : []
784
+ Solis::LOGGER.info("Found #{categories.length} categories") unless categories.empty?
785
+
746
786
  #TODO: cleanup
747
787
  if sheet_data.is_a?(Hash)
748
788
  raise "No _REFERENCES sheet found" unless sheet_data.key?("_REFERENCES")
@@ -786,7 +826,7 @@ hide empty members
786
826
  Solis::LOGGER.info('Generating SCHEMA')
787
827
  schema = build_schema(datas)
788
828
  Solis::LOGGER.info('Generating INFLECTIONS')
789
- inflections = build_inflections(datas)
829
+ inflections = build_inflections(datas, categories)
790
830
  Solis::LOGGER.info('Generating JSON SCHEMA')
791
831
  json_schema = build_json_schema(shacl)
792
832
 
data/lib/solis/shape.rb CHANGED
@@ -14,6 +14,8 @@ module Solis
14
14
  parse_solution(shapes, solution)
15
15
  end
16
16
 
17
+ add_labels(shapes, graph)
18
+
17
19
  # shapes = add_missing_attributes(shapes)
18
20
  shapes
19
21
  rescue Solis::Error::GeneralError => e
@@ -137,6 +139,47 @@ module Solis
137
139
  shapes[shape_name] = shape
138
140
  end
139
141
 
142
+ # Reads the rdfs:label statements of every node shape and property shape into
143
+ # { nl: 'Publiceren', en: 'Publish' }. This is a second pass on purpose: adding
144
+ # rdfs:label to the main query would multiply every solution by the number of
145
+ # languages.
146
+ def add_labels(shapes, graph)
147
+ graph.query([nil, RDF.type, RDF::Vocab::SHACL.NodeShape]) do |node_shape|
148
+ shape_name = name_of(graph, node_shape.subject)
149
+ shape = shapes[shape_name]
150
+ next if shape.nil?
151
+
152
+ shape[:label] = labels_of(graph, node_shape.subject)
153
+
154
+ graph.query([node_shape.subject, RDF::Vocab::SHACL.property, nil]) do |property|
155
+ attribute = shape[:attributes][name_of(graph, property.object)]
156
+ next if attribute.nil?
157
+
158
+ attribute[:label] = labels_of(graph, property.object)
159
+ end
160
+ end
161
+
162
+ shapes
163
+ end
164
+
165
+ def name_of(graph, subject)
166
+ graph.query([subject, RDF::Vocab::SHACL.name, nil]).first&.object&.value
167
+ end
168
+
169
+ def labels_of(graph, subject)
170
+ labels = {}
171
+
172
+ graph.query([subject, RDF::RDFS.label, nil]).each do |statement|
173
+ literal = statement.object
174
+ language = Solis::LanguageTag.normalize(literal.language) if literal.is_a?(RDF::Literal)
175
+ next if language.nil?
176
+
177
+ labels[language] = literal.value
178
+ end
179
+
180
+ labels
181
+ end
182
+
140
183
  def query
141
184
  SPARQL.parse %(
142
185
  PREFIX sh: <http://www.w3.org/ns/shacl#>
data/lib/solis/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Solis
2
- VERSION = "0.125.0"
2
+ VERSION = "0.127.0"
3
3
  end
data/lib/solis.rb CHANGED
@@ -7,6 +7,7 @@ require "solis/version"
7
7
  require 'solis/config_file'
8
8
  require "solis/error"
9
9
  require 'solis/rdf_edtf_literal'
10
+ require 'solis/language_tag'
10
11
  require 'solis/graph'
11
12
  require 'solis/shape'
12
13
 
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.125.0
4
+ version: 0.127.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mehmet Celik
@@ -307,6 +307,7 @@ files:
307
307
  - lib/solis/error/not_found_error.rb
308
308
  - lib/solis/error/query_error.rb
309
309
  - lib/solis/graph.rb
310
+ - lib/solis/language_tag.rb
310
311
  - lib/solis/model.rb
311
312
  - lib/solis/model.rb.ok
312
313
  - lib/solis/options.rb