rdf-tabular 0.1.1 → 0.1.2
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 +4 -4
- data/README.md +209 -0
- data/VERSION +1 -1
- data/etc/csvw.jsonld +20 -53
- data/etc/doap.csv-metadata.json +23 -23
- data/lib/rdf/tabular/csvw.rb +121 -80
- data/lib/rdf/tabular/metadata.rb +173 -148
- data/lib/rdf/tabular/reader.rb +149 -99
- data/spec/metadata_spec.rb +110 -113
- data/spec/reader_spec.rb +102 -0
- data/spec/suite_helper.rb +2 -1
- metadata +90 -89
data/lib/rdf/tabular/reader.rb
CHANGED
@@ -22,7 +22,7 @@ module RDF::Tabular
|
|
22
22
|
##
|
23
23
|
# Initializes the RDF::Tabular Reader instance.
|
24
24
|
#
|
25
|
-
# @param [Util::File::RemoteDoc, IO, StringIO, Array<Array<String
|
25
|
+
# @param [Util::File::RemoteDoc, IO, StringIO, Array<Array<String>>, String] input
|
26
26
|
# An opened file possibly JSON Metadata,
|
27
27
|
# or an Array used as an internalized array of arrays
|
28
28
|
# @param [Hash{Symbol => Object}] options
|
@@ -41,7 +41,7 @@ module RDF::Tabular
|
|
41
41
|
@options[:base] ||= input.base_uri if input.respond_to?(:base_uri)
|
42
42
|
@options[:base] ||= input.path if input.respond_to?(:path)
|
43
43
|
@options[:base] ||= input.filename if input.respond_to?(:filename)
|
44
|
-
if RDF::URI(@options[:base]).relative? && File.exist?(@options[:base])
|
44
|
+
if RDF::URI(@options[:base]).relative? && File.exist?(@options[:base].to_s)
|
45
45
|
@options[:base] = "file:/#{File.expand_path(@options[:base])}"
|
46
46
|
end
|
47
47
|
|
@@ -52,7 +52,12 @@ module RDF::Tabular
|
|
52
52
|
# Minimal implies noProv
|
53
53
|
@options[:noProv] ||= @options[:minimal]
|
54
54
|
|
55
|
-
|
55
|
+
#byebug if input.is_a?(Array)
|
56
|
+
@input = case input
|
57
|
+
when String then StringIO.new(input)
|
58
|
+
when Array then StringIO.new(input.map {|r| r.join(",")}.join("\n"))
|
59
|
+
else input
|
60
|
+
end
|
56
61
|
|
57
62
|
depth do
|
58
63
|
# If input is JSON, then the input is the metadata
|
@@ -69,19 +74,23 @@ module RDF::Tabular
|
|
69
74
|
elsif @options[:no_found_metadata]
|
70
75
|
# Extract embedded metadata and merge
|
71
76
|
table_metadata = @options[:metadata]
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
if
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
dialect = table_metadata.dialect.dup
|
78
|
+
|
79
|
+
# HTTP flags for setting header values
|
80
|
+
dialect.header = false if (input.headers.fetch(:content_type, '').split(';').include?('header=absent') rescue false)
|
81
|
+
dialect.encoding = input.charset if (input.charset rescue nil)
|
82
|
+
dialect.separator = "\t" if (input.content_type == "text/tsv" rescue nil)
|
83
|
+
|
84
|
+
embedded_metadata = dialect.embedded_metadata(input, @options)
|
85
|
+
if lang = (input.headers[:content_language] rescue "")
|
86
|
+
embedded_metadata.lang = lang unless lang.include?(',')
|
80
87
|
end
|
81
88
|
|
89
|
+
@metadata = table_metadata.dup.merge!(embedded_metadata)
|
90
|
+
else
|
82
91
|
# It's tabluar data. Find metadata and proceed as if it was specified in the first place
|
83
|
-
@
|
84
|
-
@input = @metadata
|
92
|
+
@options[:original_input] = @input
|
93
|
+
@input = @metadata = Metadata.for_input(@input, @options)
|
85
94
|
end
|
86
95
|
|
87
96
|
debug("Reader#initialize") {"input: #{input}, metadata: #{metadata.inspect}"}
|
@@ -109,67 +118,86 @@ module RDF::Tabular
|
|
109
118
|
if input.is_a?(Metadata)
|
110
119
|
debug("each_statement: metadata") {input.inspect}
|
111
120
|
|
112
|
-
# Validate metadata
|
113
|
-
input.validate!
|
114
|
-
|
115
121
|
depth do
|
116
122
|
# Get Metadata to invoke and open referenced files
|
117
123
|
case input.type
|
118
124
|
when :TableGroup
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
125
|
+
begin
|
126
|
+
# Validate metadata
|
127
|
+
input.validate!
|
128
|
+
|
129
|
+
# Use resolved @id of TableGroup, if available
|
130
|
+
table_group = input.id || RDF::Node.new
|
131
|
+
add_statement(0, table_group, RDF.type, CSVW.TableGroup) unless minimal?
|
132
|
+
|
133
|
+
# Common Properties
|
134
|
+
input.each do |key, value|
|
135
|
+
next unless key.to_s.include?(':') || key == :notes
|
136
|
+
input.common_properties(table_group, key, value) do |statement|
|
137
|
+
add_statement(0, statement)
|
138
|
+
end
|
139
|
+
end unless minimal?
|
140
|
+
|
141
|
+
# If we were originally given tabular data as input, simply use that, rather than opening the table URL. This allows buffered data to be used as input
|
142
|
+
if input.tables.empty? && options[:original_input]
|
143
|
+
table_resource = RDF::Node.new
|
144
|
+
add_statement(0, table_group, CSVW.table, table_resource) unless minimal?
|
145
|
+
Reader.new(options[:original_input], options.merge(
|
146
|
+
metadata: Table.new({url: options.fetch(:base, "http://example.org/default-metadata")}),
|
147
|
+
no_found_metadata: true,
|
148
|
+
table_resource: table_resource
|
149
|
+
)) do |r|
|
150
|
+
r.each_statement(&block)
|
151
|
+
end
|
152
|
+
else
|
153
|
+
input.each_table do |table|
|
154
|
+
next if table.suppressOutput
|
155
|
+
table_resource = table.id || RDF::Node.new
|
156
|
+
add_statement(0, table_group, CSVW.table, table_resource) unless minimal?
|
157
|
+
Reader.open(table.url, options.merge(
|
158
|
+
format: :tabular,
|
159
|
+
metadata: table,
|
160
|
+
base: table.url,
|
161
|
+
no_found_metadata: true,
|
162
|
+
table_resource: table_resource
|
163
|
+
)) do |r|
|
164
|
+
r.each_statement(&block)
|
165
|
+
end
|
166
|
+
end
|
143
167
|
end
|
144
|
-
end
|
145
168
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
169
|
+
# Provenance
|
170
|
+
if prov?
|
171
|
+
activity = RDF::Node.new
|
172
|
+
add_statement(0, table_group, RDF::PROV.wasGeneratedBy, activity)
|
173
|
+
add_statement(0, activity, RDF.type, RDF::PROV.Activity)
|
174
|
+
add_statement(0, activity, RDF::PROV.wasAssociatedWith, RDF::URI("http://rubygems.org/gems/rdf-tabular"))
|
175
|
+
add_statement(0, activity, RDF::PROV.startedAtTime, RDF::Literal::DateTime.new(start_time))
|
176
|
+
add_statement(0, activity, RDF::PROV.endedAtTime, RDF::Literal::DateTime.new(Time.now))
|
177
|
+
|
178
|
+
unless (urls = input.tables.map(&:url)).empty?
|
179
|
+
usage = RDF::Node.new
|
180
|
+
add_statement(0, activity, RDF::PROV.qualifiedUsage, usage)
|
181
|
+
add_statement(0, usage, RDF.type, RDF::PROV.Usage)
|
182
|
+
urls.each do |url|
|
183
|
+
add_statement(0, usage, RDF::PROV.entity, RDF::URI(url))
|
184
|
+
end
|
185
|
+
add_statement(0, usage, RDF::PROV.hadRole, CSVW.csvEncodedTabularData)
|
161
186
|
end
|
162
|
-
add_statement(0, usage, RDF::PROV.hadRole, CSVW.csvEncodedTabularData)
|
163
|
-
end
|
164
187
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
188
|
+
unless Array(input.filenames).empty?
|
189
|
+
usage = RDF::Node.new
|
190
|
+
add_statement(0, activity, RDF::PROV.qualifiedUsage, usage)
|
191
|
+
add_statement(0, usage, RDF.type, RDF::PROV.Usage)
|
192
|
+
Array(input.filenames).each do |fn|
|
193
|
+
add_statement(0, usage, RDF::PROV.entity, RDF::URI(fn))
|
194
|
+
end
|
195
|
+
add_statement(0, usage, RDF::PROV.hadRole, CSVW.tabularMetadata)
|
171
196
|
end
|
172
|
-
|
197
|
+
end
|
198
|
+
ensure
|
199
|
+
if validate? && !input.warnings.empty?
|
200
|
+
$stderr.puts "Warnings: #{input.warnings.join("\n")}"
|
173
201
|
end
|
174
202
|
end
|
175
203
|
when :Table
|
@@ -216,6 +244,7 @@ module RDF::Tabular
|
|
216
244
|
unless minimal?
|
217
245
|
add_statement(row.sourceNumber, table_resource, CSVW.row, row_resource)
|
218
246
|
add_statement(row.sourceNumber, row_resource, CSVW.rownum, row.number)
|
247
|
+
add_statement(row.sourceNumber, row_resource, RDF.type, CSVW.Row)
|
219
248
|
add_statement(row.sourceNumber, row_resource, CSVW.url, row.id)
|
220
249
|
end
|
221
250
|
row.values.each_with_index do |cell, index|
|
@@ -281,7 +310,7 @@ module RDF::Tabular
|
|
281
310
|
# @option options [::JSON::State] :state used when dumping
|
282
311
|
# @option options [Boolean] :atd output Abstract Table representation instead
|
283
312
|
# @return [String]
|
284
|
-
def to_json(options =
|
313
|
+
def to_json(options = @options)
|
285
314
|
io = case options
|
286
315
|
when IO, StringIO then options
|
287
316
|
when Hash then options[:io]
|
@@ -326,40 +355,61 @@ module RDF::Tabular
|
|
326
355
|
# Get Metadata to invoke and open referenced files
|
327
356
|
case input.type
|
328
357
|
when :TableGroup
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
tables = []
|
333
|
-
table_group = {}
|
334
|
-
table_group['@id'] = input.id.to_s if input.id
|
335
|
-
|
336
|
-
# Common Properties
|
337
|
-
input.each do |key, value|
|
338
|
-
next unless key.to_s.include?(':') || key == :notes
|
339
|
-
table_group[key] = input.common_properties(nil, key, value)
|
340
|
-
table_group[key] = [table_group[key]] if key == :notes && !table_group[key].is_a?(Array)
|
341
|
-
end
|
358
|
+
begin
|
359
|
+
# Validate metadata
|
360
|
+
input.validate!
|
342
361
|
|
343
|
-
|
362
|
+
tables = []
|
363
|
+
table_group = {}
|
364
|
+
table_group['@id'] = input.id.to_s if input.id
|
344
365
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
366
|
+
# Common Properties
|
367
|
+
input.each do |key, value|
|
368
|
+
next unless key.to_s.include?(':') || key == :notes
|
369
|
+
table_group[key] = input.common_properties(nil, key, value)
|
370
|
+
table_group[key] = [table_group[key]] if key == :notes && !table_group[key].is_a?(Array)
|
371
|
+
end
|
372
|
+
|
373
|
+
table_group['table'] = tables
|
374
|
+
|
375
|
+
if input.tables.empty? && options[:original_input]
|
376
|
+
md = Table.new({url: options.fetch(:base, "http://example.org/default-metadata")})
|
377
|
+
Reader.new(options[:original_input], options.merge(
|
378
|
+
metadata: md,
|
379
|
+
base: options.fetch(:base, "http://example.org/default-metadata"),
|
380
|
+
minimal: minimal?,
|
381
|
+
no_found_metadata: true
|
382
|
+
)) do |r|
|
383
|
+
case table = r.to_hash(options)
|
384
|
+
when Array then tables += table
|
385
|
+
when Hash then tables << table
|
386
|
+
end
|
387
|
+
end
|
388
|
+
else
|
389
|
+
input.each_table do |table|
|
390
|
+
next if table.suppressOutput
|
391
|
+
Reader.open(table.url, options.merge(
|
392
|
+
format: :tabular,
|
393
|
+
metadata: table,
|
394
|
+
base: table.url,
|
395
|
+
minimal: minimal?,
|
396
|
+
no_found_metadata: true
|
397
|
+
)) do |r|
|
398
|
+
case table = r.to_hash(options)
|
399
|
+
when Array then tables += table
|
400
|
+
when Hash then tables << table
|
401
|
+
end
|
402
|
+
end
|
357
403
|
end
|
358
404
|
end
|
359
|
-
end
|
360
405
|
|
361
|
-
|
362
|
-
|
406
|
+
# Result is table_group or array
|
407
|
+
minimal? ? tables : table_group
|
408
|
+
ensure
|
409
|
+
if validate? && !input.warnings.empty?
|
410
|
+
$stderr.puts "Warnings: #{input.warnings.join("\n")}"
|
411
|
+
end
|
412
|
+
end
|
363
413
|
when :Table
|
364
414
|
table = nil
|
365
415
|
Reader.open(input.url, options.merge(
|
@@ -442,12 +492,12 @@ module RDF::Tabular
|
|
442
492
|
cell.value.object
|
443
493
|
when cell.value.is_a?(RDF::Literal::Boolean)
|
444
494
|
cell.value.object
|
445
|
-
|
495
|
+
when cell.value
|
446
496
|
cell.value
|
447
497
|
end
|
448
498
|
|
449
499
|
# Add or merge value
|
450
|
-
merge_compacted_value(co, prop, value)
|
500
|
+
merge_compacted_value(co, prop, value) unless value.nil?
|
451
501
|
end
|
452
502
|
|
453
503
|
# Check for nesting
|
@@ -488,7 +538,7 @@ module RDF::Tabular
|
|
488
538
|
when :TableGroup
|
489
539
|
table_group = input.to_atd
|
490
540
|
|
491
|
-
input.
|
541
|
+
input.each_table do |table|
|
492
542
|
Reader.open(table.url, options.merge(
|
493
543
|
format: :tabular,
|
494
544
|
metadata: table,
|
@@ -499,7 +549,7 @@ module RDF::Tabular
|
|
499
549
|
table = r.to_atd(options)
|
500
550
|
|
501
551
|
# Fill in columns and rows in table_group entry from returned table
|
502
|
-
t = table_group[:
|
552
|
+
t = table_group[:tables].detect {|tab| tab["url"] == table["url"]}
|
503
553
|
t["columns"] = table["columns"]
|
504
554
|
t["rows"] = table["rows"]
|
505
555
|
end
|
data/spec/metadata_spec.rb
CHANGED
@@ -86,20 +86,22 @@ describe RDF::Tabular::Metadata do
|
|
86
86
|
params[:valid].each do |v|
|
87
87
|
subject.send("#{prop}=".to_sym, v)
|
88
88
|
expect(subject.errors).to be_empty
|
89
|
+
expect(subject.warnings).to be_empty
|
89
90
|
end
|
90
91
|
end
|
91
92
|
it "invalidates" do
|
92
93
|
params[:invalid].each do |v|
|
93
94
|
subject.send("#{prop}=".to_sym, v)
|
94
|
-
subject.
|
95
|
-
expect(subject.
|
95
|
+
expect(subject.errors).to be_empty
|
96
|
+
expect(subject.warnings).not_to be_empty
|
96
97
|
end
|
97
98
|
end
|
98
99
|
else
|
99
100
|
it "does not allow" do
|
100
101
|
params[:valid].each do |v|
|
101
102
|
subject.send("#{prop}=".to_sym, v)
|
102
|
-
expect(subject.errors).
|
103
|
+
expect(subject.errors).to be_empty
|
104
|
+
expect(subject.warnings).not_to be_empty
|
103
105
|
end
|
104
106
|
end
|
105
107
|
end
|
@@ -144,7 +146,8 @@ describe RDF::Tabular::Metadata do
|
|
144
146
|
it "Does not allow unknown prefxies or unprefixed names" do
|
145
147
|
invalid.each do |v|
|
146
148
|
subject[v.to_sym] = "foo"
|
147
|
-
expect(subject.errors).
|
149
|
+
expect(subject.errors).to be_empty
|
150
|
+
expect(subject.warnings).not_to be_empty
|
148
151
|
end
|
149
152
|
end
|
150
153
|
|
@@ -167,7 +170,8 @@ describe RDF::Tabular::Metadata do
|
|
167
170
|
it "Does not allow defined prefixed names and absolute URIs" do
|
168
171
|
(valid + invalid).each do |v|
|
169
172
|
subject[v.to_sym] = "foo"
|
170
|
-
expect(subject.errors).
|
173
|
+
expect(subject.errors).to be_empty
|
174
|
+
expect(subject.warnings).not_to be_empty
|
171
175
|
end
|
172
176
|
end
|
173
177
|
end
|
@@ -197,21 +201,21 @@ describe RDF::Tabular::Metadata do
|
|
197
201
|
its(:type) {is_expected.to eql :Column}
|
198
202
|
|
199
203
|
{
|
200
|
-
|
204
|
+
titles: {
|
201
205
|
valid: ["foo", %w(foo bar), {"en" => "foo", "de" => "bar"}],
|
202
206
|
invalid: [1, true, nil]
|
203
207
|
},
|
204
208
|
required: {
|
205
209
|
valid: [true, false],
|
206
|
-
|
210
|
+
warning: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"],
|
207
211
|
},
|
208
212
|
suppressOutput: {
|
209
213
|
valid: [true, false],
|
210
|
-
|
214
|
+
warning: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"],
|
211
215
|
},
|
212
216
|
virtual: {
|
213
217
|
valid: [true, false],
|
214
|
-
|
218
|
+
warning: [nil, 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0", "foo"],
|
215
219
|
},
|
216
220
|
}.each do |prop, params|
|
217
221
|
context prop.to_s do
|
@@ -226,17 +230,24 @@ describe RDF::Tabular::Metadata do
|
|
226
230
|
subject.send("#{prop}=".to_sym, v)
|
227
231
|
expect(subject).not_to be_valid
|
228
232
|
end
|
229
|
-
end
|
233
|
+
end if params[:invalid]
|
234
|
+
it "warnings" do
|
235
|
+
params[:warning].each do |v|
|
236
|
+
subject.send("#{prop}=".to_sym, v)
|
237
|
+
expect(subject).to be_valid
|
238
|
+
expect(subject.warnings).not_to be_empty
|
239
|
+
end
|
240
|
+
end if params[:warning]
|
230
241
|
end
|
231
242
|
end
|
232
243
|
|
233
|
-
context "
|
244
|
+
context "titles" do
|
234
245
|
{
|
235
246
|
string: ["foo", {"und" => ["foo"]}],
|
236
247
|
}.each do |name, (input, output)|
|
237
248
|
it name do
|
238
|
-
subject.
|
239
|
-
expect(subject.
|
249
|
+
subject.titles = input
|
250
|
+
expect(subject.titles).to produce(output)
|
240
251
|
end
|
241
252
|
end
|
242
253
|
end
|
@@ -307,7 +318,7 @@ describe RDF::Tabular::Metadata do
|
|
307
318
|
describe "foreignKeys" do
|
308
319
|
subject {
|
309
320
|
RDF::Tabular::TableGroup.new({
|
310
|
-
|
321
|
+
tables: [{
|
311
322
|
url: "a",
|
312
323
|
tableSchema: {
|
313
324
|
"@id" => "a_s",
|
@@ -350,7 +361,7 @@ describe RDF::Tabular::Metadata do
|
|
350
361
|
}
|
351
362
|
}.each do |name, fk|
|
352
363
|
it name do
|
353
|
-
subject.
|
364
|
+
subject.tables.first.tableSchema.foreignKeys << fk
|
354
365
|
expect(subject.normalize!.errors).to be_empty
|
355
366
|
end
|
356
367
|
end
|
@@ -403,7 +414,7 @@ describe RDF::Tabular::Metadata do
|
|
403
414
|
},
|
404
415
|
}.each do |name, fk|
|
405
416
|
it name do
|
406
|
-
subject.
|
417
|
+
subject.tables.first.tableSchema.foreignKeys << fk
|
407
418
|
expect(subject.normalize!.errors).not_to be_empty
|
408
419
|
end
|
409
420
|
end
|
@@ -442,13 +453,13 @@ describe RDF::Tabular::Metadata do
|
|
442
453
|
end
|
443
454
|
end
|
444
455
|
|
445
|
-
context "
|
456
|
+
context "titles" do
|
446
457
|
{
|
447
458
|
string: ["foo", {"und" => ["foo"]}],
|
448
459
|
}.each do |name, (input, output)|
|
449
460
|
it name do
|
450
|
-
subject.
|
451
|
-
expect(subject.
|
461
|
+
subject.titles = input
|
462
|
+
expect(subject.titles).to produce(output)
|
452
463
|
end
|
453
464
|
end
|
454
465
|
end
|
@@ -485,10 +496,10 @@ describe RDF::Tabular::Metadata do
|
|
485
496
|
"tableSchema": {
|
486
497
|
"@type": "Schema",
|
487
498
|
"columns": [
|
488
|
-
{"
|
489
|
-
{"
|
490
|
-
{"
|
491
|
-
{"
|
499
|
+
{"titles": {"und": ["countryCode"]}},
|
500
|
+
{"titles": {"und": ["latitude"]}},
|
501
|
+
{"titles": {"und": ["longitude"]}},
|
502
|
+
{"titles": {"und": ["name"]}}
|
492
503
|
]
|
493
504
|
}
|
494
505
|
})
|
@@ -503,10 +514,10 @@ describe RDF::Tabular::Metadata do
|
|
503
514
|
"tableSchema": {
|
504
515
|
"@type": "Schema",
|
505
516
|
"columns": [
|
506
|
-
{"
|
507
|
-
{"
|
508
|
-
{"
|
509
|
-
{"
|
517
|
+
{"titles": {"und": ["AD"]}},
|
518
|
+
{"titles": {"und": ["42.546245"]}},
|
519
|
+
{"titles": {"und": ["1.601554"]}},
|
520
|
+
{"titles": {"und": ["Andorra"]}}
|
510
521
|
]
|
511
522
|
},
|
512
523
|
"rdfs:comment": ["countryCode,latitude,longitude,name"]
|
@@ -522,29 +533,11 @@ describe RDF::Tabular::Metadata do
|
|
522
533
|
"tableSchema": {
|
523
534
|
"@type": "Schema",
|
524
535
|
"columns": [
|
525
|
-
{"
|
526
|
-
{"
|
527
|
-
{"
|
528
|
-
{"
|
529
|
-
{"
|
530
|
-
]
|
531
|
-
}
|
532
|
-
})
|
533
|
-
},
|
534
|
-
"headerColumnCount" => {
|
535
|
-
input: "https://example.org/tree-ops.csv",
|
536
|
-
dialect: {headerColumnCount: 1},
|
537
|
-
result: %({
|
538
|
-
"@context": "http://www.w3.org/ns/csvw",
|
539
|
-
"@type": "Table",
|
540
|
-
"url": "https://example.org/tree-ops.csv",
|
541
|
-
"tableSchema": {
|
542
|
-
"@type": "Schema",
|
543
|
-
"columns": [
|
544
|
-
{"title": {"und": ["On Street"]}},
|
545
|
-
{"title": {"und": ["Species"]}},
|
546
|
-
{"title": {"und": ["Trim Cycle"]}},
|
547
|
-
{"title": {"und": ["Inventory Date"]}}
|
536
|
+
{"titles": {"und": ["GID"]}},
|
537
|
+
{"titles": {"und": ["On Street"]}},
|
538
|
+
{"titles": {"und": ["Species"]}},
|
539
|
+
{"titles": {"und": ["Trim Cycle"]}},
|
540
|
+
{"titles": {"und": ["Inventory Date"]}}
|
548
541
|
]
|
549
542
|
}
|
550
543
|
})
|
@@ -595,7 +588,7 @@ describe RDF::Tabular::Metadata do
|
|
595
588
|
},
|
596
589
|
suppressOutput: {
|
597
590
|
valid: [true, false],
|
598
|
-
|
591
|
+
warning: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"],
|
599
592
|
},
|
600
593
|
}.each do |prop, params|
|
601
594
|
context prop.to_s do
|
@@ -610,14 +603,21 @@ describe RDF::Tabular::Metadata do
|
|
610
603
|
subject.send("#{prop}=".to_sym, v)
|
611
604
|
expect(subject).not_to be_valid
|
612
605
|
end
|
613
|
-
end
|
606
|
+
end if params[:invalid]
|
607
|
+
it "warnings" do
|
608
|
+
params[:warning].each do |v|
|
609
|
+
subject.send("#{prop}=".to_sym, v)
|
610
|
+
expect(subject).to be_valid
|
611
|
+
expect(subject.warnings).not_to be_empty
|
612
|
+
end
|
613
|
+
end if params[:warning]
|
614
614
|
end
|
615
615
|
end
|
616
616
|
end
|
617
617
|
|
618
618
|
describe RDF::Tabular::TableGroup do
|
619
619
|
let(:table) {{"url" => "http://example.org/table.csv"}}
|
620
|
-
subject {described_class.new({"
|
620
|
+
subject {described_class.new({"tables" => [table]}, base: RDF::URI("http://example.org/base"), debug: @debug)}
|
621
621
|
specify {is_expected.to be_valid}
|
622
622
|
|
623
623
|
it_behaves_like("inherited properties")
|
@@ -749,7 +749,7 @@ describe RDF::Tabular::Metadata do
|
|
749
749
|
"@type Schema" => [{"@type" => "Schema"}, RDF::Tabular::Schema],
|
750
750
|
"@type Column" => [{"@type" => "Column"}, RDF::Tabular::Column],
|
751
751
|
"@type Dialect" => [{"@type" => "Dialect"}, RDF::Tabular::Dialect],
|
752
|
-
"
|
752
|
+
"tables TableGroup" => [{"tables" => []}, RDF::Tabular::TableGroup],
|
753
753
|
"dialect Table" => [{"dialect" => {}}, RDF::Tabular::Table],
|
754
754
|
"tableSchema Table" => [{"tableSchema" => {}}, RDF::Tabular::Table],
|
755
755
|
"transformations Table" => [{"transformations" => []}, RDF::Tabular::Table],
|
@@ -765,9 +765,8 @@ describe RDF::Tabular::Metadata do
|
|
765
765
|
"doubleQuote Dialect" => [{"doubleQuote" => true}, RDF::Tabular::Dialect],
|
766
766
|
"encoding Dialect" => [{"encoding" => "utf-8"}, RDF::Tabular::Dialect],
|
767
767
|
"header Dialect" => [{"header" => true}, RDF::Tabular::Dialect],
|
768
|
-
"headerColumnCount Dialect" => [{"headerColumnCount" => 0}, RDF::Tabular::Dialect],
|
769
768
|
"headerRowCount Dialect" => [{"headerRowCount" => 1}, RDF::Tabular::Dialect],
|
770
|
-
"
|
769
|
+
"lineTerminators Dialect" => [{"lineTerminators" => "\r\n"}, RDF::Tabular::Dialect],
|
771
770
|
"quoteChar Dialect" => [{"quoteChar" => "\""}, RDF::Tabular::Dialect],
|
772
771
|
"skipBlankRows Dialect" => [{"skipBlankRows" => true}, RDF::Tabular::Dialect],
|
773
772
|
"skipColumns Dialect" => [{"skipColumns" => 0}, RDF::Tabular::Dialect],
|
@@ -792,19 +791,19 @@ describe RDF::Tabular::Metadata do
|
|
792
791
|
"@type": "Schema",
|
793
792
|
"columns": [{
|
794
793
|
"name": "countryCode",
|
795
|
-
"
|
794
|
+
"titles": "countryCode",
|
796
795
|
"propertyUrl": "https://example.org/countries.csv#countryCode"
|
797
796
|
}, {
|
798
797
|
"name": "latitude",
|
799
|
-
"
|
798
|
+
"titles": "latitude",
|
800
799
|
"propertyUrl": "https://example.org/countries.csv#latitude"
|
801
800
|
}, {
|
802
801
|
"name": "longitude",
|
803
|
-
"
|
802
|
+
"titles": "longitude",
|
804
803
|
"propertyUrl": "https://example.org/countries.csv#longitude"
|
805
804
|
}, {
|
806
805
|
"name": "name",
|
807
|
-
"
|
806
|
+
"titles": "name",
|
808
807
|
"propertyUrl": "https://example.org/countries.csv#name"
|
809
808
|
}]
|
810
809
|
}
|
@@ -867,10 +866,10 @@ describe RDF::Tabular::Metadata do
|
|
867
866
|
"url": "https://example.org/countries.csv",
|
868
867
|
"tableSchema": {
|
869
868
|
"columns": [
|
870
|
-
{"
|
871
|
-
{"
|
872
|
-
{"
|
873
|
-
{"
|
869
|
+
{"titles": "addressCountry"},
|
870
|
+
{"titles": "latitude"},
|
871
|
+
{"titles": "longitude"},
|
872
|
+
{"titles": "name"}
|
874
873
|
]
|
875
874
|
}
|
876
875
|
})), base: RDF::URI("http://example.org/base"), debug: @debug)
|
@@ -878,16 +877,16 @@ describe RDF::Tabular::Metadata do
|
|
878
877
|
let(:input) {RDF::Util::File.open_file("https://example.org/countries.csv")}
|
879
878
|
|
880
879
|
{
|
881
|
-
"default
|
880
|
+
"default titles" => {
|
882
881
|
aboutUrl: [RDF::Node, RDF::Node, RDF::Node, RDF::Node],
|
883
882
|
propertyUrl: [nil, nil, nil, nil],
|
884
883
|
valueUrl: [nil, nil, nil, nil],
|
885
884
|
md: {"url" => "https://example.org/countries.csv", "tableSchema" => {
|
886
885
|
"columns" => [
|
887
|
-
{"
|
888
|
-
{"
|
889
|
-
{"
|
890
|
-
{"
|
886
|
+
{"titles" => "addressCountry"},
|
887
|
+
{"titles" => "latitude"},
|
888
|
+
{"titles" => "longitude"},
|
889
|
+
{"titles" => "name"}
|
891
890
|
]
|
892
891
|
}
|
893
892
|
}
|
@@ -903,10 +902,10 @@ describe RDF::Tabular::Metadata do
|
|
903
902
|
"propertyUrl" => '{?_name}',
|
904
903
|
"valueUrl" => '{_name}',
|
905
904
|
"columns" => [
|
906
|
-
{"
|
907
|
-
{"
|
908
|
-
{"
|
909
|
-
{"
|
905
|
+
{"titles" => "addressCountry"},
|
906
|
+
{"titles" => "latitude"},
|
907
|
+
{"titles" => "longitude"},
|
908
|
+
{"titles" => "name"}
|
910
909
|
]
|
911
910
|
}
|
912
911
|
}
|
@@ -922,17 +921,17 @@ describe RDF::Tabular::Metadata do
|
|
922
921
|
"propertyUrl" => 'schema:{_name}',
|
923
922
|
"valueUrl" => 'schema:{_name}',
|
924
923
|
"columns" => [
|
925
|
-
{"
|
926
|
-
{"
|
927
|
-
{"
|
928
|
-
{"
|
924
|
+
{"titles" => "addressCountry"},
|
925
|
+
{"titles" => "latitude"},
|
926
|
+
{"titles" => "longitude"},
|
927
|
+
{"titles" => "name"}
|
929
928
|
]
|
930
929
|
}
|
931
930
|
}
|
932
931
|
},
|
933
932
|
}.each do |name, props|
|
934
933
|
context name do
|
935
|
-
let(:md) {RDF::Tabular::Table.new(props[:md]).merge(subject).
|
934
|
+
let(:md) {RDF::Tabular::Table.new(props[:md]).merge(subject).tables.first}
|
936
935
|
let(:cells) {md.to_enum(:each_row, input).to_a.first.values}
|
937
936
|
let(:aboutUrls) {props[:aboutUrl].map {|u| u.is_a?(String) ? md.url.join(u) : u}}
|
938
937
|
let(:propertyUrls) {props[:propertyUrl].map {|u| u.is_a?(String) ? md.url.join(u) : u}}
|
@@ -961,8 +960,6 @@ describe RDF::Tabular::Metadata do
|
|
961
960
|
"headerRowCount" => {dialect: {headerRowCount: 0}},
|
962
961
|
"skipRows + headerRowCount" => {dialect: {skipRows: 1, headerRowCount: 0}},
|
963
962
|
"skipColumns" => {dialect: {skipColumns: 1}},
|
964
|
-
"headerColumnCount" => {dialect: {headerColumnCount: 0}},
|
965
|
-
"skipColumns + headerColumnCount" => {dialect: {skipColumns: 1, headerColumnCount: 0}},
|
966
963
|
}.each do |name, props|
|
967
964
|
context name do
|
968
965
|
subject {
|
@@ -973,19 +970,19 @@ describe RDF::Tabular::Metadata do
|
|
973
970
|
"@type": "Schema",
|
974
971
|
"columns": [{
|
975
972
|
"name": "countryCode",
|
976
|
-
"
|
973
|
+
"titles": "countryCode",
|
977
974
|
"propertyUrl": "https://example.org/countries.csv#countryCode"
|
978
975
|
}, {
|
979
976
|
"name": "latitude",
|
980
|
-
"
|
977
|
+
"titles": "latitude",
|
981
978
|
"propertyUrl": "https://example.org/countries.csv#latitude"
|
982
979
|
}, {
|
983
980
|
"name": "longitude",
|
984
|
-
"
|
981
|
+
"titles": "longitude",
|
985
982
|
"propertyUrl": "https://example.org/countries.csv#longitude"
|
986
983
|
}, {
|
987
984
|
"name": "name",
|
988
|
-
"
|
985
|
+
"titles": "name",
|
989
986
|
"propertyUrl": "https://example.org/countries.csv#name"
|
990
987
|
}]
|
991
988
|
}
|
@@ -995,7 +992,7 @@ describe RDF::Tabular::Metadata do
|
|
995
992
|
}
|
996
993
|
let(:rows) {subject.to_enum(:each_row, input).to_a}
|
997
994
|
let(:rowOffset) {props[:dialect].fetch(:skipRows, 0) + props[:dialect].fetch(:headerRowCount, 1)}
|
998
|
-
let(:columnOffset) {props[:dialect].fetch(:skipColumns, 0)
|
995
|
+
let(:columnOffset) {props[:dialect].fetch(:skipColumns, 0)}
|
999
996
|
it "has expected number attributes" do
|
1000
997
|
nums = [1, 2, 3, 4]
|
1001
998
|
nums = nums.first(nums.length - rowOffset)
|
@@ -1372,7 +1369,7 @@ describe RDF::Tabular::Metadata do
|
|
1372
1369
|
})],
|
1373
1370
|
R: %({
|
1374
1371
|
"@type": "TableGroup",
|
1375
|
-
"
|
1372
|
+
"tables": [{
|
1376
1373
|
"@type": "Table",
|
1377
1374
|
"url": "http://example.org/table"
|
1378
1375
|
}],
|
@@ -1390,7 +1387,7 @@ describe RDF::Tabular::Metadata do
|
|
1390
1387
|
})],
|
1391
1388
|
R: %({
|
1392
1389
|
"@type": "TableGroup",
|
1393
|
-
"
|
1390
|
+
"tables": [{
|
1394
1391
|
"@type": "Table",
|
1395
1392
|
"url": "http://example.org/table1"
|
1396
1393
|
}, {
|
@@ -1407,14 +1404,14 @@ describe RDF::Tabular::Metadata do
|
|
1407
1404
|
}),
|
1408
1405
|
B: [%({
|
1409
1406
|
"@type": "TableGroup",
|
1410
|
-
"
|
1407
|
+
"tables": [{
|
1411
1408
|
"@type": "Table",
|
1412
1409
|
"url": "http://example.org/table2"
|
1413
1410
|
}]
|
1414
1411
|
})],
|
1415
1412
|
R: %({
|
1416
1413
|
"@type": "TableGroup",
|
1417
|
-
"
|
1414
|
+
"tables": [{
|
1418
1415
|
"@type": "Table",
|
1419
1416
|
"url": "http://example.org/table1"
|
1420
1417
|
}, {
|
@@ -1427,7 +1424,7 @@ describe RDF::Tabular::Metadata do
|
|
1427
1424
|
"table-group and table" => {
|
1428
1425
|
A: %({
|
1429
1426
|
"@type": "TableGroup",
|
1430
|
-
"
|
1427
|
+
"tables": [{
|
1431
1428
|
"@type": "Table",
|
1432
1429
|
"url": "http://example.org/table1"
|
1433
1430
|
}]
|
@@ -1438,7 +1435,7 @@ describe RDF::Tabular::Metadata do
|
|
1438
1435
|
})],
|
1439
1436
|
R: %({
|
1440
1437
|
"@type": "TableGroup",
|
1441
|
-
"
|
1438
|
+
"tables": [{
|
1442
1439
|
"@type": "Table",
|
1443
1440
|
"url": "http://example.org/table1"
|
1444
1441
|
}, {
|
@@ -1451,7 +1448,7 @@ describe RDF::Tabular::Metadata do
|
|
1451
1448
|
"table-group and two tables" => {
|
1452
1449
|
A: %({
|
1453
1450
|
"@type": "TableGroup",
|
1454
|
-
"
|
1451
|
+
"tables": [{
|
1455
1452
|
"@type": "Table",
|
1456
1453
|
"url": "http://example.org/table1"
|
1457
1454
|
}]
|
@@ -1467,7 +1464,7 @@ describe RDF::Tabular::Metadata do
|
|
1467
1464
|
})],
|
1468
1465
|
R: %({
|
1469
1466
|
"@type": "TableGroup",
|
1470
|
-
"
|
1467
|
+
"tables": [{
|
1471
1468
|
"@type": "Table",
|
1472
1469
|
"url": "http://example.org/table1"
|
1473
1470
|
}, {
|
@@ -1505,29 +1502,29 @@ describe RDF::Tabular::Metadata do
|
|
1505
1502
|
describe "#merge!" do
|
1506
1503
|
{
|
1507
1504
|
"TableGroup with and without @id" => {
|
1508
|
-
A: %({"@id": "http://example.org/foo", "
|
1509
|
-
B: %({"
|
1510
|
-
R: %({"@id": "http://example.org/foo", "
|
1505
|
+
A: %({"@id": "http://example.org/foo", "tables": [], "@type": "TableGroup"}),
|
1506
|
+
B: %({"tables": [], "@type": "TableGroup"}),
|
1507
|
+
R: %({"@id": "http://example.org/foo", "tables": [], "@type": "TableGroup"})
|
1511
1508
|
},
|
1512
1509
|
"TableGroup with and without @type" => {
|
1513
|
-
A: %({"
|
1514
|
-
B: %({"
|
1515
|
-
R: %({"
|
1510
|
+
A: %({"tables": []}),
|
1511
|
+
B: %({"tables": [], "@type": "TableGroup"}),
|
1512
|
+
R: %({"tables": [], "@type": "TableGroup"})
|
1516
1513
|
},
|
1517
|
-
"TableGroup with matching
|
1518
|
-
A: %({"
|
1519
|
-
B: %({"
|
1520
|
-
R: %({"
|
1514
|
+
"TableGroup with matching tables" => {
|
1515
|
+
A: %({"tables": [{"url": "http://example.org/foo", "dc:title": "foo"}]}),
|
1516
|
+
B: %({"tables": [{"url": "http://example.org/foo", "dc:description": "bar"}]}),
|
1517
|
+
R: %({"tables": [{
|
1521
1518
|
"url": "http://example.org/foo",
|
1522
1519
|
"dc:title": {"@value": "foo"},
|
1523
1520
|
"dc:description": {"@value": "bar"}
|
1524
1521
|
}]})
|
1525
1522
|
},
|
1526
|
-
"TableGroup with differing
|
1527
|
-
A: %({"
|
1528
|
-
B: %({"
|
1523
|
+
"TableGroup with differing tables" => {
|
1524
|
+
A: %({"tables": [{"url": "http://example.org/foo", "dc:title": "foo"}]}),
|
1525
|
+
B: %({"tables": [{"url": "http://example.org/bar", "dc:description": "bar"}]}),
|
1529
1526
|
R: %({
|
1530
|
-
"
|
1527
|
+
"tables": [
|
1531
1528
|
{"url": "http://example.org/foo", "dc:title": {"@value": "foo"}},
|
1532
1529
|
{"url": "http://example.org/bar", "dc:description": {"@value": "bar"}}
|
1533
1530
|
]})
|
@@ -1642,14 +1639,14 @@ describe RDF::Tabular::Metadata do
|
|
1642
1639
|
"@type": "Table",
|
1643
1640
|
"url": "http://example.com/foo",
|
1644
1641
|
"tableSchema": {
|
1645
|
-
"columns": [{"
|
1642
|
+
"columns": [{"titles": "foo"}]
|
1646
1643
|
}
|
1647
1644
|
}),
|
1648
1645
|
B: %({
|
1649
1646
|
"@type": "Table",
|
1650
1647
|
"url": "http://example.com/foo",
|
1651
1648
|
"tableSchema": {
|
1652
|
-
"columns": [{"
|
1649
|
+
"columns": [{"titles": "foo"}]
|
1653
1650
|
}
|
1654
1651
|
}),
|
1655
1652
|
R: %({
|
@@ -1657,7 +1654,7 @@ describe RDF::Tabular::Metadata do
|
|
1657
1654
|
"@type": "Table",
|
1658
1655
|
"url": "http://example.com/foo",
|
1659
1656
|
"tableSchema": {
|
1660
|
-
"columns": [{"
|
1657
|
+
"columns": [{"titles": {"en": ["foo"]}}]
|
1661
1658
|
}
|
1662
1659
|
}),
|
1663
1660
|
},
|
@@ -1667,9 +1664,9 @@ describe RDF::Tabular::Metadata do
|
|
1667
1664
|
R: %({"@type": "Schema", "columns": [{"name": "foo", "required": true}]}),
|
1668
1665
|
},
|
1669
1666
|
"Schema with matching column titles" => {
|
1670
|
-
A: %({"@type": "Schema", "columns": [{"
|
1671
|
-
B: %({"@type": "Schema", "columns": [{"name": "foo", "
|
1672
|
-
R: %({"@type": "Schema", "columns": [{"name": "foo", "
|
1667
|
+
A: %({"@type": "Schema", "columns": [{"titles": "Foo"}]}),
|
1668
|
+
B: %({"@type": "Schema", "columns": [{"name": "foo", "titles": "Foo"}]}),
|
1669
|
+
R: %({"@type": "Schema", "columns": [{"name": "foo", "titles": {"und": ["Foo"]}}]}),
|
1673
1670
|
},
|
1674
1671
|
"Schema with primaryKey always takes A" => {
|
1675
1672
|
A: %({"@type": "Schema", "primaryKey": "foo"}),
|