spec/metadata_spec.rb in rdf-tabular-0.1.2 vs spec/metadata_spec.rb in rdf-tabular-0.1.3

- old
+ new

@@ -27,52 +27,56 @@ @debug = [] end shared_examples "inherited properties" do |allowed = true| { - null: { - valid: ["foo", %w(foo bar)], - invalid: [1, true, {}] + aboutUrl: { + valid: ["http://example.org/example.csv#row={_row}", "http://example.org/tree/{on%2Dstreet}/{GID}", "#row.{_row}"], + invalid: [1, true, nil, %w(foo bar)] }, + datatype: { + valid: (%w(anyAtomicType string token language Name NCName boolean gYear number binary datetime any xml html json) + + [{"base" => "string"}] + ), + invalid: [1, true, "foo", "anyType", "anySimpleType", "IDREFS"] + }, + default: { + valid: ["foo"], + invalid: [1, %w(foo bar), true, nil] + }, lang: { valid: %w(en en-US), invalid: %w(1 foo) }, - "textDirection" => { - valid: %w(rtl ltr), - invalid: %w(foo default) + null: { + valid: ["foo", %w(foo bar)], + invalid: [1, true, {}] }, - separator: { - valid: %w(, a | :) + [nil], - invalid: [1, false] + %w(foo ::) - }, ordered: { valid: [true, false], invalid: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"], }, - default: { - valid: ["foo"], - invalid: [1, %w(foo bar), true, nil] - }, - datatype: { - valid: (%w(anyAtomicType string token language Name NCName boolean gYear number binary datetime any xml html json) + - [{"base" => "string"}] - ), - invalid: [1, true, "foo", "anyType", "anySimpleType", "IDREFS"] - }, - aboutUrl: { - valid: ["http://example.org/example.csv#row={_row}", "http://example.org/tree/{on%2Dstreet}/{GID}", "#row.{_row}"], - invalid: [1, true, nil, %w(foo bar)] - }, propertyUrl: { valid: [ "http://example.org/example.csv#col={_name}", "http://example.org/tree/{on%2Dstreet}/{GID}", "#row.{_row}" ], invalid: [1, true, %w(foo bar)] }, + required: { + valid: [true, false], + invalid: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"], + }, + separator: { + valid: %w(, a | :) + [nil], + invalid: [1, false] + %w(foo ::) + }, + "textDirection" => { + valid: %w(rtl ltr), + invalid: %w(foo default) + }, valueUrl: { valid: [ "http://example.org/example.csv#row={_row}", "http://example.org/tree/{on%2Dstreet}/{GID}", "#row.{_row}" @@ -176,11 +180,11 @@ end end end describe RDF::Tabular::Column do - subject {described_class.new({"name" => "foo"}, base: RDF::URI("http://example.org/base"), debug: @debug)} + subject {described_class.new({"name" => "foo"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"), debug: @debug)} specify {is_expected.to be_valid} it_behaves_like("inherited properties") it_behaves_like("common properties") it "allows valid name" do @@ -188,11 +192,14 @@ name abc.123 _col.1 ).each {|v| expect(described_class.new("name" => v)).to be_valid} end it "detects invalid names" do - [1, true, nil, "_foo", "_col=1"].each {|v| expect(described_class.new("name" => v)).not_to be_valid} + [1, true, nil, "_foo", "_col=1"].each do |v| + md = described_class.new("name" => v) + expect(md.warnings).not_to be_empty + end end it "allows absence of name" do expect(described_class.new("@type" => "Column")).to be_valid expect(described_class.new("@type" => "Column").name).to eql '_col.0' @@ -201,16 +208,12 @@ its(:type) {is_expected.to eql :Column} { titles: { valid: ["foo", %w(foo bar), {"en" => "foo", "de" => "bar"}], - invalid: [1, true, nil] + warning: [1, true, nil] }, - required: { - valid: [true, false], - warning: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"], - }, suppressOutput: { valid: [true, false], warning: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"], }, virtual: { @@ -245,18 +248,18 @@ { string: ["foo", {"und" => ["foo"]}], }.each do |name, (input, output)| it name do subject.titles = input - expect(subject.titles).to produce(output) + expect(subject.normalize!.titles).to produce(output) end end end end describe RDF::Tabular::Schema do - subject {described_class.new({}, base: RDF::URI("http://example.org/base"), debug: @debug)} + subject {described_class.new({}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"), debug: @debug)} specify {is_expected.to be_valid} it_behaves_like("inherited properties") it_behaves_like("common properties") its(:type) {is_expected.to eql :Schema} @@ -272,11 +275,11 @@ expect(v.errors).to be_empty end it "is invalid with an invalid column" do v = described_class.new({"columns" => [{"name" => "_invalid"}]}, base: RDF::URI("http://example.org/base"), debug: @debug) - expect(v.errors).not_to be_empty + expect(v.warnings).not_to be_empty end it "is invalid with an non-unique columns" do v = described_class.new({"columns" => [column, column]}, base: RDF::URI("http://example.org/base"), debug: @debug) expect(v.errors).not_to be_empty @@ -423,33 +426,41 @@ end describe RDF::Tabular::Transformation do let(:targetFormat) {"http://example.org/targetFormat"} let(:scriptFormat) {"http://example.org/scriptFormat"} - subject {described_class.new({"url" => "http://example/", "targetFormat" => targetFormat, "scriptFormat" => scriptFormat}, base: RDF::URI("http://example.org/base"), debug: @debug)} + subject { + described_class.new({ + "url" => "http://example/", + "targetFormat" => targetFormat, + "scriptFormat" => scriptFormat}, + context: "http://www.w3.org/ns/csvw", + base: RDF::URI("http://example.org/base"), + debug: @debug) + } specify {is_expected.to be_valid} it_behaves_like("inherited properties", false) it_behaves_like("common properties") its(:type) {is_expected.to eql :Transformation} { source: { valid: %w(json rdf) + [nil], - invalid: [1, true, {}] + warning: [1, true, {}] }, }.each do |prop, params| context prop.to_s do it "validates" do params[:valid].each do |v| subject.send("#{prop}=".to_sym, v) expect(subject).to be_valid end end - it "invalidates" do - params[:invalid].each do |v| + it "warnings" do + params[:warning].each do |v| subject.send("#{prop}=".to_sym, v) - expect(subject).not_to be_valid + expect(subject.warnings).not_to be_empty end end end end @@ -457,24 +468,24 @@ { string: ["foo", {"und" => ["foo"]}], }.each do |name, (input, output)| it name do subject.titles = input - expect(subject.titles).to produce(output) + expect(subject.normalize!.titles).to produce(output) end end end end describe RDF::Tabular::Dialect do - subject {described_class.new({}, base: RDF::URI("http://example.org/base"), debug: @debug)} + subject {described_class.new({}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"), debug: @debug)} specify {is_expected.to be_valid} it_behaves_like("inherited properties", false) it_behaves_like("common properties", false) its(:type) {is_expected.to eql :Dialect} - described_class.const_get(:DIALECT_DEFAULTS).each do |p, v| + described_class.const_get(:DEFAULTS).each do |p, v| context "#{p}" do it "retrieves #{v.inspect} by default" do expect(subject.send(p)).to eql v end @@ -543,50 +554,53 @@ }) }, }.each do |name, props| it name do dialect = if props[:dialect] - described_class.new(props[:dialect], base: RDF::URI("http://example.org/base"), debug: @debug) + described_class.new(props[:dialect], debug: @debug) else subject end - result = dialect.embedded_metadata(props[:input]) + result = dialect.embedded_metadata(props[:input], nil, base: RDF::URI("http://example.org/base")) expect(::JSON.parse(result.to_json(JSON_STATE))).to produce(::JSON.parse(props[:result]), @debug) end end end end describe RDF::Tabular::Table do - subject {described_class.new({"url" => "http://example.org/table.csv"}, base: RDF::URI("http://example.org/base"), debug: @debug)} + subject {described_class.new({"url" => "http://example.org/table.csv"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"), debug: @debug)} specify {is_expected.to be_valid} it_behaves_like("inherited properties") it_behaves_like("common properties") its(:type) {is_expected.to eql :Table} + describe "#to_table_group" do + end + { tableSchema: { valid: [RDF::Tabular::Schema.new({})], - invalid: [1, true, nil] + warning: [1, true, nil] }, notes: { valid: [{}, [{}]], invalid: [1, true, nil] }, tableDirection: { valid: %w(rtl ltr default), - invalid: %w(foo true 1) + warning: %w(foo true 1) }, transformations: { valid: [[RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")]], - invalid: [RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")] + + warning: [RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")] + %w(foo true 1) }, dialect: { valid: [{skipRows: 1}], - invalid: ["http://location-of-dialect", "foo"] + warning: [1] }, suppressOutput: { valid: [true, false], warning: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"], }, @@ -615,34 +629,32 @@ end end describe RDF::Tabular::TableGroup do let(:table) {{"url" => "http://example.org/table.csv"}} - subject {described_class.new({"tables" => [table]}, base: RDF::URI("http://example.org/base"), debug: @debug)} + subject {described_class.new({"tables" => [table]}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"), debug: @debug)} specify {is_expected.to be_valid} it_behaves_like("inherited properties") it_behaves_like("common properties") its(:type) {is_expected.to eql :TableGroup} - - { tableSchema: { valid: [RDF::Tabular::Schema.new({})], - invalid: [1, true, nil] + warning: [1, true, nil] }, tableDirection: { valid: %w(rtl ltr default), - invalid: %w(foo true 1) + warning: %w(foo true 1) }, dialect: { valid: [{skipRows: 1}], - invalid: ["http://location-of-dialect", "foo"] + warning: [1] }, transformations: { valid: [[RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")]], - invalid: [RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")] + + warning: [RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")] + %w(foo true 1) }, notes: { valid: [{}, [{}]], invalid: [1, true, nil] @@ -658,11 +670,18 @@ it "invalidates" do params[:invalid].each do |v| subject.send("#{prop}=".to_sym, v) expect(subject).not_to be_valid end - end + end if params[:invalid] + it "warnings" do + params[:warning].each do |v| + subject.send("#{prop}=".to_sym, v) + expect(subject).to be_valid + expect(subject.warnings).not_to be_empty + end + end if params[:warning] end end end context "parses example metadata" do @@ -757,11 +776,10 @@ "scriptFormat Transformation" => [{"scriptFormat" => "foo"}, RDF::Tabular::Transformation], "source Transformation" => [{"source" => "foo"}, RDF::Tabular::Transformation], "columns Schema" => [{"columns" => []}, RDF::Tabular::Schema], "primaryKey Schema" => [{"primaryKey" => "foo"}, RDF::Tabular::Schema], "foreignKeys Schema" => [{"foreignKeys" => []}, RDF::Tabular::Schema], - "urlTemplate Schema" => [{"urlTemplate" => "foo"}, RDF::Tabular::Schema], "commentPrefix Dialect" => [{"commentPrefix" => "#"}, RDF::Tabular::Dialect], "delimiter Dialect" => [{"delimiter" => ","}, RDF::Tabular::Dialect], "doubleQuote Dialect" => [{"doubleQuote" => true}, RDF::Tabular::Dialect], "encoding Dialect" => [{"encoding" => "utf-8"}, RDF::Tabular::Dialect], "header Dialect" => [{"header" => true}, RDF::Tabular::Dialect], @@ -860,80 +878,54 @@ expect(rows[2].values.map(&:to_s)).to produce(%w(AF 33.93911 67.709953 Afghanistan), @debug) end context "URL expansion" do subject { - described_class.new(JSON.parse(%({ + JSON.parse(%({ "url": "https://example.org/countries.csv", "tableSchema": { "columns": [ {"titles": "addressCountry"}, {"titles": "latitude"}, {"titles": "longitude"}, {"titles": "name"} ] } - })), base: RDF::URI("http://example.org/base"), debug: @debug) + })) } let(:input) {RDF::Util::File.open_file("https://example.org/countries.csv")} { "default titles" => { aboutUrl: [RDF::Node, RDF::Node, RDF::Node, RDF::Node], propertyUrl: [nil, nil, nil, nil], valueUrl: [nil, nil, nil, nil], - md: {"url" => "https://example.org/countries.csv", "tableSchema" => { - "columns" => [ - {"titles" => "addressCountry"}, - {"titles" => "latitude"}, - {"titles" => "longitude"}, - {"titles" => "name"} - ] - } - } + md: {} }, "schema transformations" => { aboutUrl: %w(#addressCountry #latitude #longitude #name), propertyUrl: %w(?_name=addressCountry ?_name=latitude ?_name=longitude ?_name=name), valueUrl: %w(addressCountry latitude longitude name), md: { - "url" => "https://example.org/countries.csv", - "tableSchema" => { - "aboutUrl" => "{#_name}", - "propertyUrl" => '{?_name}', - "valueUrl" => '{_name}', - "columns" => [ - {"titles" => "addressCountry"}, - {"titles" => "latitude"}, - {"titles" => "longitude"}, - {"titles" => "name"} - ] - } + "aboutUrl" => "{#_name}", + "propertyUrl" => '{?_name}', + "valueUrl" => '{_name}' } }, "PNames" => { aboutUrl: [RDF::SCHEMA.addressCountry, RDF::SCHEMA.latitude, RDF::SCHEMA.longitude, RDF::SCHEMA.name], propertyUrl: [RDF::SCHEMA.addressCountry, RDF::SCHEMA.latitude, RDF::SCHEMA.longitude, RDF::SCHEMA.name], valueUrl: [RDF::SCHEMA.addressCountry, RDF::SCHEMA.latitude, RDF::SCHEMA.longitude, RDF::SCHEMA.name], md: { - "url" => "https://example.org/countries.csv", - "tableSchema" => { - "aboutUrl" => 'http://schema.org/{_name}', - "propertyUrl" => 'schema:{_name}', - "valueUrl" => 'schema:{_name}', - "columns" => [ - {"titles" => "addressCountry"}, - {"titles" => "latitude"}, - {"titles" => "longitude"}, - {"titles" => "name"} - ] - } + "aboutUrl" => "http://schema.org/{_name}", + "propertyUrl" => 'schema:{_name}', + "valueUrl" => 'schema:{_name}' } }, }.each do |name, props| context name do - let(:md) {RDF::Tabular::Table.new(props[:md]).merge(subject).tables.first} + let(:md) {RDF::Tabular::Table.new(subject.merge(props[:md]), base: RDF::URI("http://example.org/base")).normalize!} let(:cells) {md.to_enum(:each_row, input).to_a.first.values} let(:aboutUrls) {props[:aboutUrl].map {|u| u.is_a?(String) ? md.url.join(u) : u}} let(:propertyUrls) {props[:propertyUrl].map {|u| u.is_a?(String) ? md.url.join(u) : u}} let(:valueUrls) {props[:valueUrl].map {|u| u.is_a?(String) ? md.url.join(u) : u}} it "aboutUrl is #{props[:aboutUrl]}" do @@ -1046,40 +1038,40 @@ base: "decimal", value: "4" }, "decimal with matching pattern" => { base: "decimal", - pattern: '\d{3}', + format: {pattern: '\d{3}'}, value: "123" }, "decimal with wrong pattern" => { base: "decimal", - pattern: '\d{4}', + format: {pattern: '\d{4}'}, value: "123", errors: [/123 does not match pattern/] }, "decimal with implicit groupChar" => { base: "decimal", value: %("123,456.789"), result: "123456.789" }, "decimal with explicit groupChar" => { base: "decimal", - groupChar: ";", + format: {groupChar: ";"}, value: "123;456.789", result: "123456.789" }, "decimal with repeated groupChar" => { base: "decimal", - groupChar: ";", + format: {groupChar: ";"}, value: "123;;456.789", result: "123;;456.789", errors: [/has repeating/] }, "decimal with explicit decimalChar" => { base: "decimal", - decimalChar: ";", + format: {decimalChar: ";"}, value: "123456;789", result: "123456.789" }, "invalid decimal" => { base: "decimal", @@ -1147,11 +1139,11 @@ "validate date yyyy-MM-dd" => {base: "date", value: "2015-03-22", format: "yyyy-MM-dd", result: "2015-03-22"}, "validate date yyyyMMdd" => {base: "date", value: "20150322", format: "yyyyMMdd", result: "2015-03-22"}, "validate date dd-MM-yyyy" => {base: "date", value: "22-03-2015", format: "dd-MM-yyyy", result: "2015-03-22"}, "validate date d-M-yyyy" => {base: "date", value: "22-3-2015", format: "d-M-yyyy", result: "2015-03-22"}, "validate date MM-dd-yyyy" => {base: "date", value: "03-22-2015", format: "MM-dd-yyyy", result: "2015-03-22"}, - "validate date M/d/yyyy" => {base: "date", value: "3/22/2015", format: "M-d-yyyy", result: "2015-03-22"}, + "validate date M-d-yyyy" => {base: "date", value: "3-22-2015", format: "M-d-yyyy", result: "2015-03-22"}, "validate date dd/MM/yyyy" => {base: "date", value: "22/03/2015", format: "dd/MM/yyyy", result: "2015-03-22"}, "validate date d/M/yyyy" => {base: "date", value: "22/3/2015", format: "d/M/yyyy", result: "2015-03-22"}, "validate date MM/dd/yyyy" => {base: "date", value: "03/22/2015", format: "MM/dd/yyyy", result: "2015-03-22"}, "validate date M/d/yyyy" => {base: "date", value: "3/22/2015", format: "M/d/yyyy", result: "2015-03-22"}, "validate date dd.MM.yyyy" => {base: "date", value: "22.03.2015", format: "dd.MM.yyyy", result: "2015-03-22"}, @@ -1354,359 +1346,182 @@ context "transformation" do it "FIXME" end end - describe "#merge" do + describe "#verify_compatible!" do { "two tables with same id" => { A: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.org/table" + "url": "http://example.org/table", + "tableSchema": {"columns": []} }), - B: [%({ + B: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.org/table" - })], - R: %({ - "@type": "TableGroup", - "tables": [{ - "@type": "Table", - "url": "http://example.org/table" - }], - "@context": "http://www.w3.org/ns/csvw" - }) + "url": "http://example.org/table", + "tableSchema": {"columns": []} + }), + R: true }, "two tables with different id" => { A: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.org/table1" + "url": "http://example.org/table1", + "tableSchema": {"columns": []} }), - B: [%({ + B: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.org/table2" - })], - R: %({ - "@type": "TableGroup", - "tables": [{ - "@type": "Table", - "url": "http://example.org/table1" - }, { - "@type": "Table", - "url": "http://example.org/table2" - }], - "@context": "http://www.w3.org/ns/csvw" - }) - }, - "table and table-group" => { - A: %({ - "@type": "Table", - "url": "http://example.org/table1" + "url": "http://example.org/table2", + "tableSchema": {"columns": []} }), - B: [%({ - "@type": "TableGroup", - "tables": [{ - "@type": "Table", - "url": "http://example.org/table2" - }] - })], - R: %({ - "@type": "TableGroup", - "tables": [{ - "@type": "Table", - "url": "http://example.org/table1" - }, { - "@type": "Table", - "url": "http://example.org/table2" - }], - "@context": "http://www.w3.org/ns/csvw" - }) + R: false }, - "table-group and table" => { + "table-group and table with same url" => { A: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "TableGroup", "tables": [{ "@type": "Table", - "url": "http://example.org/table1" + "url": "http://example.org/table1", + "tableSchema": {"columns": []} }] }), - B: [%({ + B: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.org/table2" - })], - R: %({ - "@type": "TableGroup", - "tables": [{ - "@type": "Table", - "url": "http://example.org/table1" - }, { - "@type": "Table", - "url": "http://example.org/table2" - }], - "@context": "http://www.w3.org/ns/csvw" - }) + "url": "http://example.org/table1", + "tableSchema": {"columns": []} + }), + R: true }, - "table-group and two tables" => { + "table-group and table with different url" => { A: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "TableGroup", "tables": [{ "@type": "Table", - "url": "http://example.org/table1" + "url": "http://example.org/table1", + "tableSchema": {"columns": []} }] }), - B: [%({ + B: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", "url": "http://example.org/table2", - "dc:label": "foo" - }), %({ - "@type": "Table", - "url": "http://example.org/table2", - "dc:label": "bar" - })], - R: %({ + "tableSchema": {"columns": []} + }), + R: false + }, + "table-group with two tables" => { + A: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "TableGroup", "tables": [{ "@type": "Table", - "url": "http://example.org/table1" + "url": "http://example.org/table1", + "tableSchema": {"columns": []} }, { "@type": "Table", "url": "http://example.org/table2", - "dc:label": {"@value": "foo"} - }], - "@context": "http://www.w3.org/ns/csvw" - }) - }, - }.each do |name, props| - it name do - a = described_class.new(::JSON.parse(props[:A])) - b = props[:B].map {|md| described_class.new(::JSON.parse(md))} - r = described_class.new(::JSON.parse(props[:R])) - expect(a.merge(*b)).to produce(r, @debug) - end - end - - %w(Transformation Schema Transformation Column Dialect).each do |t| - it "does not merge into a #{t}" do - a = described_class.new({}, type: t.to_sym) - b = described_class.new({}, type: :TableGroup) - expect {a.merge(b)}.to raise_error - end - - it "does not merge from a #{t}" do - a = described_class.new({}, type: :TableGroup) - b = described_class.new({}, type: t.to_sym) - expect {a.merge(b)}.to raise_error - end - end - end - - describe "#merge!" do - { - "TableGroup with and without @id" => { - A: %({"@id": "http://example.org/foo", "tables": [], "@type": "TableGroup"}), - B: %({"tables": [], "@type": "TableGroup"}), - R: %({"@id": "http://example.org/foo", "tables": [], "@type": "TableGroup"}) - }, - "TableGroup with and without @type" => { - A: %({"tables": []}), - B: %({"tables": [], "@type": "TableGroup"}), - R: %({"tables": [], "@type": "TableGroup"}) - }, - "TableGroup with matching tables" => { - A: %({"tables": [{"url": "http://example.org/foo", "dc:title": "foo"}]}), - B: %({"tables": [{"url": "http://example.org/foo", "dc:description": "bar"}]}), - R: %({"tables": [{ - "url": "http://example.org/foo", - "dc:title": {"@value": "foo"}, - "dc:description": {"@value": "bar"} - }]}) - }, - "TableGroup with differing tables" => { - A: %({"tables": [{"url": "http://example.org/foo", "dc:title": "foo"}]}), - B: %({"tables": [{"url": "http://example.org/bar", "dc:description": "bar"}]}), - R: %({ - "tables": [ - {"url": "http://example.org/foo", "dc:title": {"@value": "foo"}}, - {"url": "http://example.org/bar", "dc:description": {"@value": "bar"}} - ]}) - }, - "Table with tableDirection always takes A" => { - A: %({"@type": "Table", "url": "http://example.com/foo", "tableDirection": "ltr"}), - B: %({"@type": "Table", "url": "http://example.com/foo", "tableDirection": "rtl"}), - R: %({"@type": "Table", "url": "http://example.com/foo", "tableDirection": "ltr"}), - }, - "Table with dialect merges A and B" => { - A: %({"@type": "Table", "url": "http://example.com/foo", "dialect": {"encoding": "utf-8"}}), - B: %({"@type": "Table", "url": "http://example.com/foo", "dialect": {"skipRows": 0}}), - R: %({"@type": "Table", "url": "http://example.com/foo", "dialect": {"encoding": "utf-8", "skipRows": 0}}), - }, - "Table with equivalent transformations uses A" => { - A: %({ - "@type": "Table", - "url": "http://example.com/foo", - "transformations": [{ - "url": "http://example.com/foo", - "targetFormat": "http://example.com/target", - "scriptFormat": "http://example.com/template", - "source": "json" + "tableSchema": {"columns": []} }] }), B: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "transformations": [{ - "url": "http://example.com/foo", - "targetFormat": "http://example.com/target", - "scriptFormat": "http://example.com/template", - "source": "html" - }] + "url": "http://example.org/table2", + "tableSchema": {"columns": []} }), - R: %({ - "@type": "Table", - "url": "http://example.com/foo", - "transformations": [{ - "url": "http://example.com/foo", - "targetFormat": "http://example.com/target", - "scriptFormat": "http://example.com/template", - "source": "json" - }] - }), + R: true }, - "Table with differing transformations appends B to A" => { + "tables with matching columns" => { A: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "transformations": [{ - "url": "http://example.com/foo", - "targetFormat": "http://example.com/target", - "scriptFormat": "http://example.com/template" - }] + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"name": "foo"}]} }), B: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "transformations": [{ - "url": "http://example.com/bar", - "targetFormat": "http://example.com/targetb", - "scriptFormat": "http://example.com/templateb" - }] + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"name": "foo"}]} }), - R: %({ + R: true + }, + "tables with virtual columns otherwise matching" => { + A: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "transformations": [{ - "url": "http://example.com/foo", - "targetFormat": "http://example.com/target", - "scriptFormat": "http://example.com/template" - }, { - "url": "http://example.com/bar", - "targetFormat": "http://example.com/targetb", - "scriptFormat": "http://example.com/templateb" - }] + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"name": "foo"}, {"virtual": true}]} }), - }, - "Table with common properties keeps A" => { - A: %({"@type": "Table", "url": "http://example.com/foo", "rdfs:label": "foo"}), - B: %({"@type": "Table", "url": "http://example.com/foo", "rdfs:label": "bar"}), - R: %({ + B: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "rdfs:label": {"@value": "foo"} + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"name": "foo"}]} }), + R: true }, - "Table with common properties in different languages keeps A" => { + "tables with differing columns" => { A: %({ - "@context": {"@language": "en"}, + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "rdfs:label": "foo" + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"name": "foo"}]} }), B: %({ - "@context": {"@language": "fr"}, - "@type": "Table", - "url": "http://example.com/foo", - "rdfs:label": "foo" - }), - R: %({ "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "rdfs:label": {"@value": "foo", "@language": "en"} + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"name": "bar"}]} }), + R: false }, - "Table with different languages merges A and B" => { + "tables with different column count" => { A: %({ - "@context": {"@language": "en"}, + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "tableSchema": { - "columns": [{"titles": "foo"}] - } + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"name": "foo"}, {"name": "bar"}]} }), B: %({ + "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "tableSchema": { - "columns": [{"titles": "foo"}] - } + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"name": "bar"}]} }), - R: %({ + R: false + }, + "tables with matching columns on name/titles" => { + A: %({ "@context": "http://www.w3.org/ns/csvw", "@type": "Table", - "url": "http://example.com/foo", - "tableSchema": { - "columns": [{"titles": {"en": ["foo"]}}] - } + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"name": "foo"}]} }), + B: %({ + "@context": "http://www.w3.org/ns/csvw", + "@type": "Table", + "url": "http://example.org/table1", + "tableSchema": {"columns": [{"titles": "foo"}]} + }), + R: true }, - "Schema with matching columns merges A and B" => { - A: %({"@type": "Schema", "columns": [{"name": "foo", "required": true}]}), - B: %({"@type": "Schema", "columns": [{"name": "foo", "required": false}]}), - R: %({"@type": "Schema", "columns": [{"name": "foo", "required": true}]}), - }, - "Schema with matching column titles" => { - A: %({"@type": "Schema", "columns": [{"titles": "Foo"}]}), - B: %({"@type": "Schema", "columns": [{"name": "foo", "titles": "Foo"}]}), - R: %({"@type": "Schema", "columns": [{"name": "foo", "titles": {"und": ["Foo"]}}]}), - }, - "Schema with primaryKey always takes A" => { - A: %({"@type": "Schema", "primaryKey": "foo"}), - B: %({"@type": "Schema", "primaryKey": "bar"}), - R: %({"@type": "Schema", "primaryKey": "foo"}), - }, - "Schema with matching foreignKey uses A" => { - A: %({"@type": "Schema", "columns": [{"name": "foo"}], "foreignKeys": [{"columnReference": "foo", "reference": {"columnReference": "foo"}}]}), - B: %({"@type": "Schema", "columns": [{"name": "foo"}], "foreignKeys": [{"columnReference": "foo", "reference": {"columnReference": "foo"}}]}), - R: %({"@type": "Schema", "columns": [{"name": "foo"}], "foreignKeys": [{"columnReference": "foo", "reference": {"columnReference": "foo"}}]}), - }, - "Schema with differing foreignKey uses A and B" => { - A: %({"@type": "Schema", "columns": [{"name": "foo"}, {"name": "bar"}], "foreignKeys": [{"columnReference": "foo", "reference": {"columnReference": "foo"}}]}), - B: %({"@type": "Schema", "columns": [{"name": "foo"}, {"name": "bar"}], "foreignKeys": [{"columnReference": "bar", "reference": {"columnReference": "bar"}}]}), - R: %({"@type": "Schema", "columns": [{"name": "foo"}, {"name": "bar"}], "foreignKeys": [{"columnReference": "foo", "reference": {"columnReference": "foo"}}, {"columnReference": "bar", "reference": {"columnReference": "bar"}}]}), - }, - "Schema with urlTemplate always takes A" => { - A: %({"@type": "Schema", "urlTemplate": "foo"}), - B: %({"@type": "Schema", "urlTemplate": "bar"}), - R: %({"@type": "Schema", "urlTemplate": "foo"}), - }, }.each do |name, props| it name do - a = described_class.new(::JSON.parse(props[:A]), debug: @debug) + a = described_class.new(::JSON.parse(props[:A])) b = described_class.new(::JSON.parse(props[:B])) - r = described_class.new(::JSON.parse(props[:R])) - m = a.merge!(b) - expect(m).to produce(r, @debug) - expect(a).to equal m - end - end - - %w(TableGroup Table Transformation Schema Transformation Column Dialect).each do |ta| - %w(TableGroup Table Transformation Schema Transformation Column Dialect).each do |tb| - next if ta == tb - it "does not merge #{tb} into #{ta}" do - a = described_class.new({}, type: ta.to_sym) - b = described_class.new({}, type: tb.to_sym) - expect {a.merge!(b)}.to raise_error + if props[:R] + expect {a.verify_compatible!(b)}.not_to raise_error + else + expect {a.verify_compatible!(b)}.to raise_error(RDF::Tabular::Error) end end end end end