rdf-tabular 0.1.3.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -3
- data/VERSION +1 -1
- data/etc/README +4 -0
- data/etc/csvw.jsonld +1 -1
- data/etc/earl.html +10544 -0
- data/etc/earl.jsonld +17068 -0
- data/etc/earl.ttl +7114 -0
- data/etc/template.haml +205 -0
- data/etc/well-known +4 -0
- data/lib/rdf/tabular.rb +10 -4
- data/lib/rdf/tabular/format.rb +11 -7
- data/lib/rdf/tabular/metadata.rb +761 -314
- data/lib/rdf/tabular/reader.rb +276 -144
- data/spec/format_spec.rb +11 -8
- data/spec/matchers.rb +4 -4
- data/spec/metadata_spec.rb +120 -36
- data/spec/reader_spec.rb +56 -18
- data/spec/spec_helper.rb +10 -2
- data/spec/suite_helper.rb +35 -18
- data/spec/suite_spec.rb +26 -24
- metadata +15 -3
data/spec/format_spec.rb
CHANGED
@@ -4,19 +4,22 @@ require 'spec_helper'
|
|
4
4
|
require 'rdf/spec/format'
|
5
5
|
|
6
6
|
describe RDF::Tabular::Format do
|
7
|
-
|
8
|
-
|
7
|
+
it_behaves_like 'an RDF::Format' do
|
8
|
+
let(:format_class) {RDF::Tabular::Format}
|
9
9
|
end
|
10
10
|
|
11
|
-
include RDF_Format
|
12
|
-
|
13
11
|
describe ".for" do
|
14
12
|
formats = [
|
15
13
|
:tabular,
|
16
14
|
'etc/doap.csv',
|
17
|
-
|
18
|
-
{:
|
19
|
-
{:
|
15
|
+
'etc/doap.tsv',
|
16
|
+
{file_name: 'etc/doap.csv'},
|
17
|
+
{file_name: 'etc/doap.tsv'},
|
18
|
+
{file_extension: 'csv'},
|
19
|
+
{file_extension: 'tsv'},
|
20
|
+
{content_type: 'text/csv'},
|
21
|
+
{content_type: 'text/tab-separated-values'},
|
22
|
+
{content_type: 'application/csvm+json'},
|
20
23
|
].each do |arg|
|
21
24
|
it "discovers with #{arg.inspect}" do
|
22
25
|
expect(RDF::Tabular::Format).to include RDF::Format.for(arg)
|
@@ -25,6 +28,6 @@ describe RDF::Tabular::Format do
|
|
25
28
|
end
|
26
29
|
|
27
30
|
describe "#to_sym" do
|
28
|
-
specify {expect(
|
31
|
+
specify {expect(described_class.to_sym).to eq :tabular}
|
29
32
|
end
|
30
33
|
end
|
data/spec/matchers.rb
CHANGED
@@ -47,8 +47,8 @@ RSpec::Matchers.define :be_equivalent_graph do |expected, info|
|
|
47
47
|
else
|
48
48
|
"Graph differs\n"
|
49
49
|
end +
|
50
|
-
"Expected:\n#{@expected.dump(:ttl, standard_prefixes: true, prefixes: prefixes)}" +
|
51
|
-
"Results:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: prefixes)}" +
|
50
|
+
"Expected:\n#{@expected.dump(:ttl, standard_prefixes: true, prefixes: prefixes, literal_shorthand: false)}" +
|
51
|
+
"Results:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: prefixes, literal_shorthand: false)}" +
|
52
52
|
(@info.metadata ? "\nMetadata:\n#{@info.metadata.to_json(JSON_STATE)}\n" : "") +
|
53
53
|
(@info.metadata && !@info.metadata.errors.empty? ? "\nMetadata Errors:\n#{@info.metadata.errors.join("\n")}\n" : "") +
|
54
54
|
(@info.debug ? "\nDebug:\n#{@info.debug}" : "")
|
@@ -85,7 +85,7 @@ RSpec::Matchers.define :pass_query do |expected, info|
|
|
85
85
|
"Query returned true (expected #{@info.result})"
|
86
86
|
end +
|
87
87
|
"\n#{@expected}" +
|
88
|
-
"\nResults:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: {'' => @info.action + '#'})}" +
|
88
|
+
"\nResults:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: {'' => @info.action + '#'}, literal_shorthand: false)}" +
|
89
89
|
(@info.metadata ? "\nMetadata:\n#{@info.metadata.to_json(JSON_STATE)}\n" : "") +
|
90
90
|
(@info.metadata && !@info.metadata.errors.empty? ? "\nMetadata Errors:\n#{@info.metadata.errors.join("\n")}\n" : "") +
|
91
91
|
"\nDebug:\n#{@info.debug}"
|
@@ -103,7 +103,7 @@ RSpec::Matchers.define :pass_query do |expected, info|
|
|
103
103
|
"Query returned true (expected #{@info.result})"
|
104
104
|
end +
|
105
105
|
"\n#{@expected}" +
|
106
|
-
"\nResults:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: {'' => @info.action + '#'})}" +
|
106
|
+
"\nResults:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: {'' => @info.action + '#'}, literal_shorthand: false)}" +
|
107
107
|
(@info.metadata ? "\nMetadata:\n#{@info.metadata.to_json(JSON_STATE)}\n" : "") +
|
108
108
|
(@info.metadata && !@info.metadata.errors.empty? ? "\nMetadata Errors:\n#{@info.metadata.errors.join("\n")}\n" : "") +
|
109
109
|
"\nDebug:\n#{@info.debug}"
|
data/spec/metadata_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe RDF::Tabular::Metadata do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
case file
|
17
|
-
when "metadata.json", "country-codes-and-names.csv-metadata.json"
|
17
|
+
when "csv-metadata.json", "country-codes-and-names.csv-metadata.json"
|
18
18
|
{status: 401}
|
19
19
|
else
|
20
20
|
{
|
@@ -37,7 +37,15 @@ describe RDF::Tabular::Metadata do
|
|
37
37
|
valid: (%w(anyAtomicType string token language Name NCName boolean gYear number binary datetime any xml html json) +
|
38
38
|
[{"base" => "string"}]
|
39
39
|
),
|
40
|
-
invalid: [1, true, "
|
40
|
+
invalid: [1, true, "http://example.org/",
|
41
|
+
{"base" => "foo"},
|
42
|
+
{"base" => "anyType"},
|
43
|
+
{"base" => "anySimpleType"},
|
44
|
+
{"base" => "IDREFS"},
|
45
|
+
],
|
46
|
+
errors: [{"@id" => "_:foo"},
|
47
|
+
{"@id" => "http://www.w3.org/2001/XMLSchema#string"},
|
48
|
+
]
|
41
49
|
},
|
42
50
|
default: {
|
43
51
|
valid: ["foo"],
|
@@ -68,8 +76,8 @@ describe RDF::Tabular::Metadata do
|
|
68
76
|
invalid: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"],
|
69
77
|
},
|
70
78
|
separator: {
|
71
|
-
valid: %w(, a | :) + [nil],
|
72
|
-
invalid: [1, false]
|
79
|
+
valid: %w(, a | : foo ::) + [nil],
|
80
|
+
invalid: [1, false]
|
73
81
|
},
|
74
82
|
"textDirection" => {
|
75
83
|
valid: %w(rtl ltr),
|
@@ -87,19 +95,25 @@ describe RDF::Tabular::Metadata do
|
|
87
95
|
context prop.to_s do
|
88
96
|
if allowed
|
89
97
|
it "validates" do
|
90
|
-
params
|
98
|
+
params.fetch(:valid, {}).each do |v|
|
91
99
|
subject.send("#{prop}=".to_sym, v)
|
92
100
|
expect(subject.errors).to be_empty
|
93
101
|
expect(subject.warnings).to be_empty
|
94
102
|
end
|
95
103
|
end
|
96
104
|
it "invalidates" do
|
97
|
-
params
|
105
|
+
params.fetch(:invalid, {}).each do |v|
|
98
106
|
subject.send("#{prop}=".to_sym, v)
|
99
107
|
expect(subject.errors).to be_empty
|
100
108
|
expect(subject.warnings).not_to be_empty
|
101
109
|
end
|
102
110
|
end
|
111
|
+
it "errors" do
|
112
|
+
params.fetch(:error, {}).each do |v|
|
113
|
+
subject.send("#{prop}=".to_sym, v)
|
114
|
+
expect(subject.errors).not_to be_empty
|
115
|
+
end
|
116
|
+
end
|
103
117
|
else
|
104
118
|
it "does not allow" do
|
105
119
|
params[:valid].each do |v|
|
@@ -588,12 +602,12 @@ describe RDF::Tabular::Metadata do
|
|
588
602
|
invalid: [1, true, nil]
|
589
603
|
},
|
590
604
|
tableDirection: {
|
591
|
-
valid: %w(rtl ltr
|
605
|
+
valid: %w(rtl ltr auto),
|
592
606
|
warning: %w(foo true 1)
|
593
607
|
},
|
594
608
|
transformations: {
|
595
|
-
valid: [[RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")]],
|
596
|
-
warning: [RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")] +
|
609
|
+
valid: [[RDF::Tabular::Transformation.new({url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"))]],
|
610
|
+
warning: [RDF::Tabular::Transformation.new({url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"))] +
|
597
611
|
%w(foo true 1)
|
598
612
|
},
|
599
613
|
dialect: {
|
@@ -643,7 +657,7 @@ describe RDF::Tabular::Metadata do
|
|
643
657
|
warning: [1, true, nil]
|
644
658
|
},
|
645
659
|
tableDirection: {
|
646
|
-
valid: %w(rtl ltr
|
660
|
+
valid: %w(rtl ltr auto),
|
647
661
|
warning: %w(foo true 1)
|
648
662
|
},
|
649
663
|
dialect: {
|
@@ -651,8 +665,8 @@ describe RDF::Tabular::Metadata do
|
|
651
665
|
warning: [1]
|
652
666
|
},
|
653
667
|
transformations: {
|
654
|
-
valid: [[RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")]],
|
655
|
-
warning: [RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")] +
|
668
|
+
valid: [[RDF::Tabular::Transformation.new({url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"))]],
|
669
|
+
warning: [RDF::Tabular::Transformation.new({url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"))] +
|
656
670
|
%w(foo true 1)
|
657
671
|
},
|
658
672
|
notes: {
|
@@ -1014,6 +1028,7 @@ describe RDF::Tabular::Metadata do
|
|
1014
1028
|
# Strings
|
1015
1029
|
"string with no constraints" => {base: "string", value: "foo", result: "foo"},
|
1016
1030
|
"string with matching length" => {base: "string", value: "foo", length: 3, result: "foo"},
|
1031
|
+
"string matching null when required" => {base: "string", value: "NULL", null: "NULL", required: true},
|
1017
1032
|
"string with wrong length" => {
|
1018
1033
|
base: "string",
|
1019
1034
|
value: "foo",
|
@@ -1040,36 +1055,31 @@ describe RDF::Tabular::Metadata do
|
|
1040
1055
|
},
|
1041
1056
|
"decimal with matching pattern" => {
|
1042
1057
|
base: "decimal",
|
1043
|
-
format: {pattern
|
1058
|
+
format: {"pattern" => '000'},
|
1044
1059
|
value: "123"
|
1045
1060
|
},
|
1046
1061
|
"decimal with wrong pattern" => {
|
1047
1062
|
base: "decimal",
|
1048
|
-
format: {pattern
|
1063
|
+
format: {"pattern" => '0000'},
|
1049
1064
|
value: "123",
|
1050
1065
|
errors: [/123 does not match pattern/]
|
1051
1066
|
},
|
1052
|
-
"decimal with implicit groupChar" => {
|
1053
|
-
base: "decimal",
|
1054
|
-
value: %("123,456.789"),
|
1055
|
-
result: "123456.789"
|
1056
|
-
},
|
1057
1067
|
"decimal with explicit groupChar" => {
|
1058
1068
|
base: "decimal",
|
1059
|
-
format: {groupChar
|
1069
|
+
format: {"groupChar" => ";"},
|
1060
1070
|
value: "123;456.789",
|
1061
1071
|
result: "123456.789"
|
1062
1072
|
},
|
1063
1073
|
"decimal with repeated groupChar" => {
|
1064
1074
|
base: "decimal",
|
1065
|
-
format: {groupChar
|
1075
|
+
format: {"groupChar" => ";"},
|
1066
1076
|
value: "123;;456.789",
|
1067
1077
|
result: "123;;456.789",
|
1068
1078
|
errors: [/has repeating/]
|
1069
1079
|
},
|
1070
1080
|
"decimal with explicit decimalChar" => {
|
1071
1081
|
base: "decimal",
|
1072
|
-
format: {decimalChar
|
1082
|
+
format: {"decimalChar" => ";"},
|
1073
1083
|
value: "123456;789",
|
1074
1084
|
result: "123456.789"
|
1075
1085
|
},
|
@@ -1081,11 +1091,13 @@ describe RDF::Tabular::Metadata do
|
|
1081
1091
|
},
|
1082
1092
|
"decimal with percent" => {
|
1083
1093
|
base: "decimal",
|
1094
|
+
format: {"groupChar" => ","},
|
1084
1095
|
value: "123456.789%",
|
1085
1096
|
result: "1234.56789"
|
1086
1097
|
},
|
1087
1098
|
"decimal with per-mille" => {
|
1088
1099
|
base: "decimal",
|
1100
|
+
format: {"groupChar" => ","},
|
1089
1101
|
value: "123456.789‰",
|
1090
1102
|
result: "123.456789"
|
1091
1103
|
},
|
@@ -1121,7 +1133,7 @@ describe RDF::Tabular::Metadata do
|
|
1121
1133
|
"NaN number" => {base: "number", value: "NaN"},
|
1122
1134
|
"INF number" => {base: "number", value: "INF"},
|
1123
1135
|
"-INF number" => {base: "number", value: "-INF"},
|
1124
|
-
"valid float" => {base: "float", value: "1234.
|
1136
|
+
"valid float" => {base: "float", value: "1234.456E7"},
|
1125
1137
|
"invalid float" => {base: "float", value: "1z", errors: ["1z is not a valid float"]},
|
1126
1138
|
"NaN float" => {base: "float", value: "NaN"},
|
1127
1139
|
"INF float" => {base: "float", value: "INF"},
|
@@ -1152,6 +1164,7 @@ describe RDF::Tabular::Metadata do
|
|
1152
1164
|
"validate date M.d.yyyy" => {base: "date", value: "3.22.2015", format: "M.d.yyyy", result: "2015-03-22"},
|
1153
1165
|
|
1154
1166
|
# Times
|
1167
|
+
"valid time HH:mm:ss.S" => {base: "time", value: "15:02:37.1", format: "HH:mm:ss.S", result: "15:02:37.1"},
|
1155
1168
|
"valid time HH:mm:ss" => {base: "time", value: "15:02:37", format: "HH:mm:ss", result: "15:02:37"},
|
1156
1169
|
"valid time HHmmss" => {base: "time", value: "150237", format: "HHmmss", result: "15:02:37"},
|
1157
1170
|
"valid time HH:mm" => {base: "time", value: "15:02", format: "HH:mm", result: "15:02:00"},
|
@@ -1159,6 +1172,7 @@ describe RDF::Tabular::Metadata do
|
|
1159
1172
|
|
1160
1173
|
# DateTimes
|
1161
1174
|
"valid dateTime yyyy-MM-ddTHH:mm:ss" => {base: "dateTime", value: "2015-03-15T15:02:37", format: "yyyy-MM-ddTHH:mm:ss", result: "2015-03-15T15:02:37"},
|
1175
|
+
"valid dateTime yyyy-MM-ddTHH:mm:ss.S" => {base: "dateTime", value: "2015-03-15T15:02:37.1", format: "yyyy-MM-ddTHH:mm:ss.S", result: "2015-03-15T15:02:37.1"},
|
1162
1176
|
"valid dateTime yyyy-MM-dd HH:mm:ss" => {base: "dateTime", value: "2015-03-15 15:02:37", format: "yyyy-MM-dd HH:mm:ss", result: "2015-03-15T15:02:37"},
|
1163
1177
|
"valid dateTime yyyyMMdd HHmmss" => {base: "dateTime", value: "20150315 150237", format: "yyyyMMdd HHmmss", result: "2015-03-15T15:02:37"},
|
1164
1178
|
"valid dateTime dd-MM-yyyy HH:mm" => {base: "dateTime", value: "15-03-2015 15:02", format: "dd-MM-yyyy HH:mm", result: "2015-03-15T15:02:00"},
|
@@ -1210,17 +1224,6 @@ describe RDF::Tabular::Metadata do
|
|
1210
1224
|
"valid Name" => {base: "Name", value: "someThing", result: RDF::Literal("someThing", datatype: RDF::XSD.Name)},
|
1211
1225
|
"valid NMTOKEN" => {base: "NMTOKEN", value: "someThing", result: RDF::Literal("someThing", datatype: RDF::XSD.NMTOKEN)},
|
1212
1226
|
|
1213
|
-
# Unsupported datatypes
|
1214
|
-
"anyType not allowed" => {base: "anyType", value: "some thing", errors: [/unsupported datatype/]},
|
1215
|
-
"anySimpleType not allowed" => {base: "anySimpleType", value: "some thing", errors: [/unsupported datatype/]},
|
1216
|
-
"ENTITIES not allowed" => {base: "ENTITIES", value: "some thing", errors: [/unsupported datatype/]},
|
1217
|
-
"IDREFS not allowed" => {base: "IDREFS", value: "some thing", errors: [/unsupported datatype/]},
|
1218
|
-
"NMTOKENS not allowed" => {base: "NMTOKENS", value: "some thing", errors: [/unsupported datatype/]},
|
1219
|
-
"ENTITY not allowed" => {base: "ENTITY", value: "something", errors: [/unsupported datatype/]},
|
1220
|
-
"ID not allowed" => {base: "ID", value: "something", errors: [/unsupported datatype/]},
|
1221
|
-
"IDREF not allowed" => {base: "IDREF", value: "something", errors: [/unsupported datatype/]},
|
1222
|
-
"NOTATION not allowed" => {base: "NOTATION", value: "some:thing", errors: [/unsupported datatype/]},
|
1223
|
-
|
1224
1227
|
# Aliases
|
1225
1228
|
"number is alias for double" => {base: "number", value: "1234.456E789", result: RDF::Literal("1234.456E789", datatype: RDF::XSD.double)},
|
1226
1229
|
"binary is alias for base64Binary" => {base: "binary", value: "Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g", result: RDF::Literal("Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g", datatype: RDF::XSD.base64Binary)},
|
@@ -1270,6 +1273,72 @@ describe RDF::Tabular::Metadata do
|
|
1270
1273
|
specify {expect(subject.value).to eql result}
|
1271
1274
|
end
|
1272
1275
|
end
|
1276
|
+
|
1277
|
+
context "Unsupported datatypes" do
|
1278
|
+
%w(anyType anySimpleType ENTITIES IDREFS NMTOKENS ENTITY ID IDREF NOTATAION foo).each do |base|
|
1279
|
+
it "detects #{base} as unsupported" do
|
1280
|
+
md = RDF::Tabular::Table.new({
|
1281
|
+
url: "http://example.com/table.csv",
|
1282
|
+
tableSchema: {
|
1283
|
+
columns: [{
|
1284
|
+
name: "name",
|
1285
|
+
datatype: base
|
1286
|
+
}]
|
1287
|
+
}
|
1288
|
+
})
|
1289
|
+
expect(md.warnings).not_to be_empty
|
1290
|
+
end
|
1291
|
+
end
|
1292
|
+
end
|
1293
|
+
end
|
1294
|
+
end
|
1295
|
+
|
1296
|
+
describe "#build_number_re" do
|
1297
|
+
subject {RDF::Tabular::Datatype.new({})}
|
1298
|
+
{
|
1299
|
+
'#,##0.##' => /^\d{1,}\.\d{,2}$/,
|
1300
|
+
'#,##0.###' => /^\d{1,}\.\d{,3}$/,
|
1301
|
+
'###0.#####' => /^\d{1,}\.\d{,5}$/,
|
1302
|
+
'###0.0000#' => /^\d{1,}\.\d{4,5}$/,
|
1303
|
+
'00000.0000' => /^\d{5}\.\d{4}$/,
|
1304
|
+
|
1305
|
+
'0' => /^\d{1}$/,
|
1306
|
+
'00' => /^\d{2}$/,
|
1307
|
+
'#' => /^\d*$/,
|
1308
|
+
'##' => /^\d*$/,
|
1309
|
+
|
1310
|
+
'.0' => /^\.\d{1}$/,
|
1311
|
+
'.00' => /^\.\d{2}$/,
|
1312
|
+
'.#' => /^\.\d{,1}$/,
|
1313
|
+
'.##' => /^\.\d{,2}$/,
|
1314
|
+
|
1315
|
+
'+0' => /^+\d{1}$/,
|
1316
|
+
'-0' => /^-\d{1}$/,
|
1317
|
+
'%0' => /^%\d{1}$/,
|
1318
|
+
'‰0' => /^‰\d{1}$/,
|
1319
|
+
'0%' => /^\d{1}%$/,
|
1320
|
+
'0‰' => /^\d{1}‰$/,
|
1321
|
+
'0.0%' => /^\d{1}\.\d{1}%$/,
|
1322
|
+
|
1323
|
+
'#0.0#E#0' => /^\d{1,}\.\d{1,2}E\d{1,2}$/,
|
1324
|
+
'#0.0#E+#' => /^\d{1,}\.\d{1,2}E+\d{,1}$/,
|
1325
|
+
'#0.0#E-00' => /^\d{1,}\.\d{1,2}E-\d{2}$/,
|
1326
|
+
'#0.0#E#0%' => /^\d{1,}\.\d{1,2}E\d{1,2}%$/,
|
1327
|
+
}.each do |pattern,regexp|
|
1328
|
+
it "generates #{regexp} for #{pattern}" do
|
1329
|
+
expect(subject.build_number_re(pattern, ",", ".")).to eql regexp
|
1330
|
+
end
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
%W{
|
1334
|
+
+%0
|
1335
|
+
0#
|
1336
|
+
0E0
|
1337
|
+
0-
|
1338
|
+
}.each do |pattern|
|
1339
|
+
it "recognizes bad pattern #{pattern}" do
|
1340
|
+
expect{subject.build_number_re(pattern, ",", ".")}.to raise_error(ArgumentError)
|
1341
|
+
end
|
1273
1342
|
end
|
1274
1343
|
end
|
1275
1344
|
|
@@ -1511,11 +1580,26 @@ describe RDF::Tabular::Metadata do
|
|
1511
1580
|
"url": "http://example.org/table1",
|
1512
1581
|
"tableSchema": {"columns": [{"titles": "foo"}]}
|
1513
1582
|
}),
|
1514
|
-
R:
|
1583
|
+
R: false
|
1584
|
+
},
|
1585
|
+
"tables with mismatch columns on name/titles" => {
|
1586
|
+
A: %({
|
1587
|
+
"@context": "http://www.w3.org/ns/csvw",
|
1588
|
+
"@type": "Table",
|
1589
|
+
"url": "http://example.org/table1",
|
1590
|
+
"tableSchema": {"columns": [{"name": "foo"}]}
|
1591
|
+
}),
|
1592
|
+
B: %({
|
1593
|
+
"@context": "http://www.w3.org/ns/csvw",
|
1594
|
+
"@type": "Table",
|
1595
|
+
"url": "http://example.org/table1",
|
1596
|
+
"tableSchema": {"columns": [{"titles": "bar"}]}
|
1597
|
+
}),
|
1598
|
+
R: false
|
1515
1599
|
},
|
1516
1600
|
}.each do |name, props|
|
1517
1601
|
it name do
|
1518
|
-
a = described_class.new(::JSON.parse(props[:A]))
|
1602
|
+
a = described_class.new(::JSON.parse(props[:A]), validate: true)
|
1519
1603
|
b = described_class.new(::JSON.parse(props[:B]))
|
1520
1604
|
if props[:R]
|
1521
1605
|
expect {a.verify_compatible!(b)}.not_to raise_error
|
data/spec/reader_spec.rb
CHANGED
@@ -3,12 +3,10 @@ require File.join(File.dirname(__FILE__), 'spec_helper')
|
|
3
3
|
require 'rdf/spec/reader'
|
4
4
|
|
5
5
|
describe RDF::Tabular::Reader do
|
6
|
-
let!(:doap) {File.expand_path("../../etc/doap.
|
7
|
-
let!(:doap_count) {
|
6
|
+
let!(:doap) {File.expand_path("../../etc/doap.csv", __FILE__)}
|
7
|
+
let!(:doap_count) {9}
|
8
8
|
|
9
9
|
before(:each) do
|
10
|
-
@reader = RDF::Tabular::Reader.new(StringIO.new(""), base_uri: "file:#{File.expand_path("..", __FILE__)}")
|
11
|
-
|
12
10
|
WebMock.stub_request(:any, %r(.*example.org.*)).
|
13
11
|
to_return(lambda {|request|
|
14
12
|
file = request.uri.to_s.split('/').last
|
@@ -18,15 +16,15 @@ describe RDF::Tabular::Reader do
|
|
18
16
|
else 'text/plain'
|
19
17
|
end
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
{status: 401}
|
24
|
-
else
|
19
|
+
path = File.expand_path("../data/#{file}", __FILE__)
|
20
|
+
if File.exist?(path)
|
25
21
|
{
|
26
|
-
body: File.read(
|
22
|
+
body: File.read(path),
|
27
23
|
status: 200,
|
28
24
|
headers: {'Content-Type' => content_type}
|
29
25
|
}
|
26
|
+
else
|
27
|
+
{status: 401}
|
30
28
|
end
|
31
29
|
})
|
32
30
|
|
@@ -34,7 +32,12 @@ describe RDF::Tabular::Reader do
|
|
34
32
|
end
|
35
33
|
|
36
34
|
# @see lib/rdf/spec/reader.rb in rdf-spec
|
37
|
-
#
|
35
|
+
# two failures specific to the way @input is handled in rdf-tabular make this a problem
|
36
|
+
#it_behaves_like 'an RDF::Reader' do
|
37
|
+
# let(:reader_input) {doap}
|
38
|
+
# let(:reader) {RDF::Tabular::Reader.new(StringIO.new(""))}
|
39
|
+
# let(:reader_count) {doap_count}
|
40
|
+
#end
|
38
41
|
|
39
42
|
it "should be discoverable" do
|
40
43
|
readers = [
|
@@ -67,7 +70,7 @@ describe RDF::Tabular::Reader do
|
|
67
70
|
expect_any_instance_of(RDF::Tabular::Dialect).to receive(:encoding=).with("ISO-8859-4")
|
68
71
|
RDF::Tabular::Reader.new(input) {|r| r.each_statement {}}
|
69
72
|
end
|
70
|
-
it "sets lang to de in metadata given Content-Language=de" do
|
73
|
+
it "sets lang to de in metadata given Content-Language=de", pending: "affecting some RSpec matcher" do
|
71
74
|
input = double("input", content_type: "text/csv", headers: {content_language: "de"}, charset: nil)
|
72
75
|
expect_any_instance_of(RDF::Tabular::Metadata).to receive(:lang=).with("de")
|
73
76
|
RDF::Tabular::Reader.new(input) {|r| r.each_statement {}}
|
@@ -82,12 +85,12 @@ describe RDF::Tabular::Reader do
|
|
82
85
|
context "non-file input" do
|
83
86
|
let(:expected) {
|
84
87
|
JSON.parse(%({
|
85
|
-
"
|
88
|
+
"tables": [
|
86
89
|
{
|
87
|
-
"url": "
|
90
|
+
"url": "",
|
88
91
|
"row": [
|
89
92
|
{
|
90
|
-
"url": "
|
93
|
+
"url": "#row=2",
|
91
94
|
"rownum": 1,
|
92
95
|
"describes": [
|
93
96
|
{
|
@@ -97,7 +100,7 @@ describe RDF::Tabular::Reader do
|
|
97
100
|
]
|
98
101
|
},
|
99
102
|
{
|
100
|
-
"url": "
|
103
|
+
"url": "#row=3",
|
101
104
|
"rownum": 2,
|
102
105
|
"describes": [
|
103
106
|
{
|
@@ -107,7 +110,7 @@ describe RDF::Tabular::Reader do
|
|
107
110
|
]
|
108
111
|
},
|
109
112
|
{
|
110
|
-
"url": "
|
113
|
+
"url": "#row=4",
|
111
114
|
"rownum": 3,
|
112
115
|
"describes": [
|
113
116
|
{
|
@@ -117,7 +120,7 @@ describe RDF::Tabular::Reader do
|
|
117
120
|
]
|
118
121
|
},
|
119
122
|
{
|
120
|
-
"url": "
|
123
|
+
"url": "#row=5",
|
121
124
|
"rownum": 4,
|
122
125
|
"describes": [
|
123
126
|
{
|
@@ -168,7 +171,7 @@ describe RDF::Tabular::Reader do
|
|
168
171
|
|
169
172
|
it "standard mode" do
|
170
173
|
expected = File.expand_path("../data/#{ttl}", __FILE__)
|
171
|
-
RDF::Reader.open(input, format: :tabular, base_uri: about, noProv: true,
|
174
|
+
RDF::Reader.open(input, format: :tabular, base_uri: about, noProv: true, debug: @debug) do |reader|
|
172
175
|
graph = RDF::Graph.new << reader
|
173
176
|
graph2 = RDF::Graph.load(expected, base_uri: about)
|
174
177
|
expect(graph).to be_equivalent_graph(graph2,
|
@@ -258,6 +261,41 @@ describe RDF::Tabular::Reader do
|
|
258
261
|
end
|
259
262
|
end
|
260
263
|
|
264
|
+
context "Primary Keys" do
|
265
|
+
it "has expected primary keys" do
|
266
|
+
RDF::Reader.open("http://example.org/countries.json", format: :tabular, validate: true) do |reader|
|
267
|
+
reader.each_statement {}
|
268
|
+
pks = reader.metadata.tables.first.object[:rows].map(&:primaryKey)
|
269
|
+
|
270
|
+
# Each entry is an array of cells
|
271
|
+
expect(pks.map {|r| r.map(&:value).join(",")}).to eql %w(AD AE AF)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
it "errors on duplicate primary keys" do
|
276
|
+
RDF::Reader.open("http://example.org/test232-metadata.json", format: :tabular, validate: true, errors: []) do |reader|
|
277
|
+
expect {reader.validate!}.to raise_error(RDF::Tabular::Error)
|
278
|
+
|
279
|
+
pks = reader.metadata.tables.first.object[:rows].map(&:primaryKey)
|
280
|
+
|
281
|
+
# Each entry is an array of cells
|
282
|
+
expect(pks.map {|r| r.map(&:value).join(",")}).to eql %w(1 1)
|
283
|
+
|
284
|
+
expect(reader.options[:errors]).to eq ["Table http://example.org/test232.csv has duplicate primary key 1"]
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context "Foreign Keys" do
|
290
|
+
let(:path) {File.expand_path("../data/countries.json", __FILE__)}
|
291
|
+
it "validates consistent foreign keys" do
|
292
|
+
RDF::Reader.open(path, format: :tabular, validate: true, warnings: []) do |reader|
|
293
|
+
reader.each_statement {}
|
294
|
+
expect(reader.options[:warnings]).to be_empty
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
261
299
|
context "Provenance" do
|
262
300
|
{
|
263
301
|
"country-codes-and-names.csv" => %(
|