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.
@@ -4,19 +4,22 @@ require 'spec_helper'
4
4
  require 'rdf/spec/format'
5
5
 
6
6
  describe RDF::Tabular::Format do
7
- before :each do
8
- @format_class = described_class
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
- {:file_name => 'etc/doap.csv'},
18
- {:file_extension => 'csv'},
19
- {:content_type => 'text/csv'},
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(@format_class.to_sym).to eq :tabular}
31
+ specify {expect(described_class.to_sym).to eq :tabular}
29
32
  end
30
33
  end
@@ -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}"
@@ -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, "foo", "anyType", "anySimpleType", "IDREFS"]
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] + %w(foo ::)
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[:valid].each do |v|
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[:invalid].each do |v|
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 default),
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 default),
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: '\d{3}'},
1058
+ format: {"pattern" => '000'},
1044
1059
  value: "123"
1045
1060
  },
1046
1061
  "decimal with wrong pattern" => {
1047
1062
  base: "decimal",
1048
- format: {pattern: '\d{4}'},
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.456E789"},
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: true
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
@@ -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.ttl", __FILE__)}
7
- let!(:doap_count) {File.open(doap).each_line.to_a.length}
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
- case file
22
- when "metadata.json", "country-codes-and-names.csv-metadata.json"
23
- {status: 401}
24
- else
19
+ path = File.expand_path("../data/#{file}", __FILE__)
20
+ if File.exist?(path)
25
21
  {
26
- body: File.read(File.expand_path("../data/#{file}", __FILE__)),
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
- #include RDF_Reader
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
- "table": [
88
+ "tables": [
86
89
  {
87
- "url": "http://example.org/default-metadata",
90
+ "url": "",
88
91
  "row": [
89
92
  {
90
- "url": "http://example.org/default-metadata#row=2",
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": "http://example.org/default-metadata#row=3",
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": "http://example.org/default-metadata#row=4",
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": "http://example.org/default-metadata#row=5",
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, validate: true, debug: @debug) do |reader|
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" => %(