rdf-tabular 0.1.3.1 → 0.2.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.
@@ -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" => %(