rof 1.0.7 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +9 -7
- data/LICENSE +201 -16
- data/Rakefile +46 -0
- data/bin/csv_to_rof +1 -2
- data/bin/fedora_to_rof +7 -1
- data/bin/jsonld_to_rof +26 -0
- data/bin/osf_to_rof +6 -2
- data/bin/rof +5 -19
- data/lib/rof.rb +2 -6
- data/lib/rof/access.rb +1 -1
- data/lib/rof/cli.rb +104 -67
- data/lib/rof/compare_rof.rb +68 -39
- data/lib/rof/filter.rb +21 -0
- data/lib/rof/filters.rb +38 -0
- data/lib/rof/filters/bendo.rb +15 -17
- data/lib/rof/filters/date_stamp.rb +5 -4
- data/lib/rof/filters/file_to_url.rb +5 -3
- data/lib/rof/filters/label.rb +9 -7
- data/lib/rof/filters/work.rb +7 -5
- data/lib/rof/ingest.rb +5 -0
- data/lib/rof/osf_context.rb +2 -2
- data/lib/rof/rdf_context.rb +2 -0
- data/lib/rof/translator.rb +18 -0
- data/lib/rof/translators.rb +23 -0
- data/lib/rof/{translate_csv.rb → translators/csv_to_rof.rb} +4 -3
- data/lib/rof/translators/fedora_to_rof.rb +244 -0
- data/lib/rof/translators/jsonld_to_rof.rb +112 -0
- data/lib/rof/translators/jsonld_to_rof/accumulator.rb +175 -0
- data/lib/rof/translators/jsonld_to_rof/predicate_handler.rb +223 -0
- data/lib/rof/translators/jsonld_to_rof/predicate_object_handler.rb +125 -0
- data/lib/rof/translators/jsonld_to_rof/statement_handler.rb +91 -0
- data/lib/rof/translators/osf_to_rof.rb +191 -0
- data/lib/rof/utility.rb +44 -1
- data/lib/rof/version.rb +1 -1
- data/rof.gemspec +10 -2
- data/spec/coverage_helper.rb +17 -0
- data/spec/fixtures/for_utility_load_items_from_json_file/multiple_items.json +8 -0
- data/spec/fixtures/for_utility_load_items_from_json_file/parse_error.json +3 -0
- data/spec/fixtures/for_utility_load_items_from_json_file/single_item.json +3 -0
- data/spec/fixtures/jsonld_to_rof/0g354f18610.jsonld +113 -0
- data/spec/fixtures/jsonld_to_rof/0g354f18610.rof +96 -0
- data/spec/fixtures/jsonld_to_rof/2j62s467216.jsonld +113 -0
- data/spec/fixtures/jsonld_to_rof/2j62s467216.rof +93 -0
- data/spec/fixtures/jsonld_to_rof/2v23vt16z2z.jsonld +70 -0
- data/spec/fixtures/jsonld_to_rof/2v23vt16z2z.rof +87 -0
- data/spec/fixtures/jsonld_to_rof/cr56n01253w.jsonld +84 -0
- data/spec/fixtures/jsonld_to_rof/cr56n01253w.rof +95 -0
- data/spec/fixtures/jsonld_to_rof/h989r21069m.jsonld +84 -0
- data/spec/fixtures/jsonld_to_rof/h989r21069m.rof +98 -0
- data/spec/fixtures/jsonld_to_rof/js956d59913.jsonld +79 -0
- data/spec/fixtures/jsonld_to_rof/js956d59913.rof +89 -0
- data/spec/fixtures/jsonld_to_rof/m039k358q5c.jsonld +80 -0
- data/spec/fixtures/jsonld_to_rof/m039k358q5c.rof +64 -0
- data/spec/fixtures/jsonld_to_rof/nk322b9161g.jsonld +89 -0
- data/spec/fixtures/jsonld_to_rof/nk322b9161g.rof +69 -0
- data/spec/fixtures/jsonld_to_rof/p8418k7430d.jsonld +84 -0
- data/spec/fixtures/jsonld_to_rof/p8418k7430d.rof +67 -0
- data/spec/fixtures/jsonld_to_rof/xg94hm53h0c.jsonld +98 -0
- data/spec/fixtures/jsonld_to_rof/xg94hm53h0c.rof +110 -0
- data/spec/fixtures/jsonld_to_rof/zk51vd69n1r.jsonld +94 -0
- data/spec/fixtures/jsonld_to_rof/zk51vd69n1r.rof +121 -0
- data/spec/fixtures/osf/phz6b.tar.gz +0 -0
- data/spec/lib/rof/access_spec.rb +30 -23
- data/spec/lib/rof/cli_spec.rb +83 -60
- data/spec/lib/rof/compare_rof_spec.rb +35 -24
- data/spec/lib/rof/filter_spec.rb +10 -0
- data/spec/lib/rof/filters/bendo_spec.rb +42 -0
- data/spec/lib/rof/filters/date_stamp_spec.rb +9 -5
- data/spec/lib/rof/filters/file_to_url_spec.rb +7 -3
- data/spec/lib/rof/filters/label_spec.rb +121 -77
- data/spec/lib/rof/filters/work_spec.rb +7 -4
- data/spec/lib/rof/filters_spec.rb +14 -0
- data/spec/lib/rof/translator_spec.rb +15 -0
- data/spec/lib/rof/{translate_csv_spec.rb → translators/csv_to_rof_spec.rb} +14 -14
- data/spec/lib/rof/translators/fedora_to_rof_spec.rb +64 -0
- data/spec/lib/rof/translators/jsonld_to_rof/accumulator_spec.rb +121 -0
- data/spec/lib/rof/translators/jsonld_to_rof/predicate_handler_spec.rb +73 -0
- data/spec/lib/rof/translators/jsonld_to_rof/predicate_object_handler_spec.rb +48 -0
- data/spec/lib/rof/translators/jsonld_to_rof/statement_handler_spec.rb +40 -0
- data/spec/lib/rof/translators/jsonld_to_rof_spec.rb +120 -0
- data/spec/lib/rof/{osf_to_rof_spec.rb → translators/osf_to_rof_spec.rb} +55 -25
- data/spec/lib/rof/translators_spec.rb +14 -0
- data/spec/lib/rof/utility_spec.rb +47 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/support/an_rof_filter.rb +10 -0
- metadata +186 -15
- data/lib/rof/get_from_fedora.rb +0 -211
- data/lib/rof/osf_to_rof.rb +0 -123
- data/spec/lib/rof/get_from_fedora_spec.rb +0 -22
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe ROF::Translators::FedoraToRof do
|
4
|
+
let(:outfile) { double(close: true, write: true) }
|
5
|
+
let(:pid) { 'und:dev0012829m' }
|
6
|
+
let(:config) { { fedora_connection_information: fedora_connection_information } }
|
7
|
+
let(:fedora_connection_information) { { url: 'http://localhost:8080/fedora', user: 'fedoraAdmin', password: 'fedoraAdmin' } }
|
8
|
+
|
9
|
+
it 'will fail to initialize without a valid fedora connection' do
|
10
|
+
allow(Rubydora).to receive(:connect).with(fedora_connection_information).and_raise('Woof')
|
11
|
+
expect { described_class.new([pid], config) }.to raise_error('Woof')
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.call' do
|
15
|
+
it "retrieves a fedora object and converts it to ROF" do
|
16
|
+
expected_output = [{
|
17
|
+
"pid" => "und:dev0012829m",
|
18
|
+
"type" => "fobject",
|
19
|
+
"af-model" => "GenericFile",
|
20
|
+
"rels-ext" => {
|
21
|
+
"@context"=> ROF::RelsExtRefContext,
|
22
|
+
"@id" => "und:dev0012829m",
|
23
|
+
"isPartOf"=> "und:dev00128288"
|
24
|
+
},
|
25
|
+
"rights" => {
|
26
|
+
"read-groups" => ["registered"],
|
27
|
+
"edit" => ["dbrower"]
|
28
|
+
},
|
29
|
+
"properties-meta" => {"mime-type" => "text/xml"},
|
30
|
+
"properties" => "<fields>\n<depositor>batch_ingest</depositor>\n<owner>dbrower</owner>\n</fields>\n",
|
31
|
+
"content-meta" => {"label"=>"bonnie+chauncey", "mime-type"=>"application/octet-stream", "URL"=>"http://libvirt9.library.nd.edu:14000/item/dev0012826k/bonnie+chauncey"},
|
32
|
+
"metadata" => {
|
33
|
+
"@context"=> {
|
34
|
+
"bibo"=>"http://purl.org/ontology/bibo/",
|
35
|
+
"dc"=>"http://purl.org/dc/terms/",
|
36
|
+
"ebucore"=>"http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#",
|
37
|
+
"foaf"=>"http://xmlns.com/foaf/0.1/",
|
38
|
+
'hydramata-rel' => 'http://projecthydra.org/ns/relations#',
|
39
|
+
"mrel"=>"http://id.loc.gov/vocabulary/relators/",
|
40
|
+
"ms" => 'http://www.ndltd.org/standards/metadata/etdms/1.1/',
|
41
|
+
"nd"=>"https://library.nd.edu/ns/terms/",
|
42
|
+
"rdfs"=>"http://www.w3.org/2000/01/rdf-schema#",
|
43
|
+
'ths' => 'http://id.loc.gov/vocabulary/relators/',
|
44
|
+
"vracore"=>"http://purl.org/vra/",
|
45
|
+
"pav"=>"http://purl.org/pav/",
|
46
|
+
"dc:dateSubmitted" => {"@type" => "http://www.w3.org/2001/XMLSchema#date"},
|
47
|
+
"dc:created"=>{"@type"=>"http://www.w3.org/2001/XMLSchema#date"},
|
48
|
+
"dc:modified" => {"@type" => "http://www.w3.org/2001/XMLSchema#date"}
|
49
|
+
},
|
50
|
+
"@id" => "info:fedora/und:dev0012829m",
|
51
|
+
"dc:dateSubmitted" => "2016-04-12Z",
|
52
|
+
"dc:modified" => "2016-04-12Z",
|
53
|
+
"dc:title" => "bonnie+chauncey"
|
54
|
+
},
|
55
|
+
"bendo-item" => "dev0012826k",
|
56
|
+
"characterization-meta" => {"mime-type"=>"text/xml"},
|
57
|
+
"thumbnail-meta" => {"label"=>"File Datastream", "mime-type"=>"image/png"},
|
58
|
+
}]
|
59
|
+
VCR.use_cassette("fedora_to_rof1") do
|
60
|
+
expect(described_class.call([pid], config)).to eq(expected_output)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rof/translators/jsonld_to_rof/accumulator'
|
3
|
+
module ROF
|
4
|
+
module Translators
|
5
|
+
module JsonldToRof
|
6
|
+
RSpec.describe Accumulator do
|
7
|
+
describe '#add_pid' do
|
8
|
+
it 'sets the pid if non has been set' do
|
9
|
+
subject.add_pid('abcd')
|
10
|
+
expect(subject.to_rof.fetch('pid')).to eq('abcd')
|
11
|
+
end
|
12
|
+
it 'raises an exception if a different pid is set with the same accumulator' do
|
13
|
+
subject.add_pid('abcd')
|
14
|
+
expect { subject.add_pid('hijk') }.to raise_error(described_class::PidAlreadySetError)
|
15
|
+
end
|
16
|
+
it 'ignores attempts to set the same pid over and over' do
|
17
|
+
subject.add_pid('abcd')
|
18
|
+
expect { subject.add_pid('abcd') }.not_to change { subject.to_rof }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
describe '#add_predicate_location_and_value' do
|
22
|
+
it 'will register a pid if one is given' do
|
23
|
+
subject.add_predicate_location_and_value(['pid'], 1)
|
24
|
+
expect(subject.to_rof).to eq({ "pid" => 1 })
|
25
|
+
end
|
26
|
+
it 'will not allow another pid to be register if it is different' do
|
27
|
+
subject.add_pid('abc')
|
28
|
+
expect { subject.add_predicate_location_and_value(['pid'], 1) }.to raise_error(described_class::PidAlreadySetError)
|
29
|
+
end
|
30
|
+
it 'wraps values in an array' do
|
31
|
+
subject.add_predicate_location_and_value(['hello'], 1)
|
32
|
+
expect(subject.to_rof).to eq({ "hello" => [1] })
|
33
|
+
subject.add_predicate_location_and_value(['hello'], 2)
|
34
|
+
expect(subject.to_rof).to eq({ "hello" => [1, 2] })
|
35
|
+
end
|
36
|
+
it 'handles deep locations' do
|
37
|
+
subject.add_predicate_location_and_value(['hello', 'world'], "value")
|
38
|
+
expect(subject.to_rof).to eq({ "hello" => { "world" => ["value"] } })
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'handles locations at different levels' do
|
42
|
+
subject.add_predicate_location_and_value(['hello', 'world'], "value")
|
43
|
+
subject.add_predicate_location_and_value(['kitchen'], "another")
|
44
|
+
expect(subject.to_rof).to eq({ "hello" => { "world" => ["value"] }, "kitchen" => ["another"] } )
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'handling blank nodes' do
|
48
|
+
context 'for the same node' do
|
49
|
+
it 'collects values for the same location' do
|
50
|
+
blank_node = RDF::Node.new('_b0')
|
51
|
+
subject.add_blank_node(RDF::Statement.new(blank_node, nil, nil))
|
52
|
+
subject.add_predicate_location_and_value(['parent', 'child'], "value", blank_node)
|
53
|
+
subject.add_predicate_location_and_value(['parent', 'child'], "another", blank_node)
|
54
|
+
expect(subject.to_rof).to eq({
|
55
|
+
"parent" => [{
|
56
|
+
"child" => ["value", "another"]
|
57
|
+
}]
|
58
|
+
})
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'collects values for the same deep location' do
|
62
|
+
blank_node = RDF::Node.new('_b0')
|
63
|
+
subject.add_blank_node(RDF::Statement.new(blank_node, nil, nil))
|
64
|
+
subject.add_predicate_location_and_value(['parent', 'child', 'grandchild'], "value", blank_node)
|
65
|
+
subject.add_predicate_location_and_value(['parent', 'child', 'grandchild'], "another", blank_node)
|
66
|
+
expect(subject.to_rof).to eq({
|
67
|
+
"parent" => {
|
68
|
+
"child" => [{
|
69
|
+
"grandchild" => ["value", "another"]
|
70
|
+
}]
|
71
|
+
}
|
72
|
+
})
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'collects values for the same location' do
|
76
|
+
blank_node = RDF::Node.new('_b0')
|
77
|
+
subject.add_blank_node(RDF::Statement.new(blank_node, nil, nil))
|
78
|
+
subject.add_predicate_location_and_value(['parent'], "value", blank_node)
|
79
|
+
subject.add_predicate_location_and_value(['parent'], "another", blank_node)
|
80
|
+
expect(subject.to_rof).to eq({
|
81
|
+
"parent" => ["value", "another"]
|
82
|
+
})
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'merges different locations' do
|
86
|
+
blank_node = RDF::Node.new('_b0')
|
87
|
+
subject.add_blank_node(RDF::Statement.new(blank_node, nil, nil))
|
88
|
+
subject.add_predicate_location_and_value(['parent', 'child_one'], "value", blank_node)
|
89
|
+
subject.add_predicate_location_and_value(['parent', 'child_two'], "another", blank_node)
|
90
|
+
expect(subject.to_rof).to eq({
|
91
|
+
"parent" => [{
|
92
|
+
"child_one" => ["value"],
|
93
|
+
"child_two" => ["another"]
|
94
|
+
}]
|
95
|
+
})
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'for different nodes' do
|
100
|
+
it 'separates them in the JSON output' do
|
101
|
+
blank_node_1 = RDF::Node.new('_b0')
|
102
|
+
blank_node_2 = RDF::Node.new('_b1')
|
103
|
+
subject.add_blank_node(RDF::Statement.new(blank_node_1, nil, nil))
|
104
|
+
subject.add_blank_node(RDF::Statement.new(blank_node_2, nil, nil))
|
105
|
+
subject.add_predicate_location_and_value(['parent', 'child'], "value", blank_node_1)
|
106
|
+
subject.add_predicate_location_and_value(['parent', 'child'], "another", blank_node_2)
|
107
|
+
expect(subject.to_rof).to eq({
|
108
|
+
"parent" => [{
|
109
|
+
"child" => ["value"]
|
110
|
+
}, {
|
111
|
+
"child" => ["another"]
|
112
|
+
}]
|
113
|
+
})
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rof/translators/jsonld_to_rof/predicate_handler'
|
3
|
+
module ROF
|
4
|
+
module Translators
|
5
|
+
module JsonldToRof
|
6
|
+
RSpec.describe PredicateHandler do
|
7
|
+
around do |spec|
|
8
|
+
# Ensuring that we preserve duplication of
|
9
|
+
previous_registry = described_class.send(:registry)
|
10
|
+
described_class.send(:clear_registry!)
|
11
|
+
described_class.register('https://library.nd.edu/ns/terms/') do |handler|
|
12
|
+
handler.map('accessRead', to: ['rights', 'read'])
|
13
|
+
end
|
14
|
+
described_class.register('http://purl.org/dc/terms/') do |handler|
|
15
|
+
handler.namespace_prefix('dc:')
|
16
|
+
handler.map('dateTime', to: ['nested', 'dateTime'])
|
17
|
+
handler.within(['metadata'])
|
18
|
+
end
|
19
|
+
described_class.register('http://www.ndltd.org/standards/metadata/etdms/1.1/') do |handler|
|
20
|
+
handler.within(['ms:degree'])
|
21
|
+
handler.namespace_prefix('ms:')
|
22
|
+
handler.map('block-key') do |object, accumulator|
|
23
|
+
accumulator.add_predicate_location_and_value('from-block', object)
|
24
|
+
end
|
25
|
+
handler.map('something', to: ['metadata', 'ms:something'], force: true)
|
26
|
+
handler.map('something', to: ['another', 'somewhere'])
|
27
|
+
end
|
28
|
+
spec.run
|
29
|
+
described_class.send(:clear_registry!, previous_registry)
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '.call' do
|
33
|
+
let(:object) { 'my-object' }
|
34
|
+
let(:accumulator) { Accumulator.new }
|
35
|
+
|
36
|
+
it 'handles multiple map imperatives' do
|
37
|
+
described_class.call('http://www.ndltd.org/standards/metadata/etdms/1.1/something', object, accumulator)
|
38
|
+
expect(accumulator.to_rof).to eq({
|
39
|
+
"metadata" => { "ms:something" => [object] },
|
40
|
+
"ms:degree" => { "another" => { "ms:somewhere" => [object] } }
|
41
|
+
})
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'handles force option' do
|
45
|
+
described_class.call('https://library.nd.edu/ns/terms/accessRead', object, accumulator)
|
46
|
+
expect(accumulator.to_rof).to eq({ "rights" => { "read" => ["my-object"] } })
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'handles the block option' do
|
50
|
+
described_class.call('http://www.ndltd.org/standards/metadata/etdms/1.1/block-key', 'value' , accumulator)
|
51
|
+
expect(accumulator.to_rof).to eq({ "from-block" => ["value"] })
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'handles mixture of implicit and explicit terms using within and namespace_prefix' do
|
55
|
+
described_class.call('http://purl.org/dc/terms/title', 'my-title', accumulator)
|
56
|
+
described_class.call('http://purl.org/dc/terms/dateTime', 'my-dateTime', accumulator)
|
57
|
+
expect(accumulator.to_rof).to eq({
|
58
|
+
"metadata" => { "dc:title" => ["my-title"], "nested" => { "dc:dateTime" => ["my-dateTime"] } }
|
59
|
+
})
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with an unregistred url' do
|
63
|
+
it 'will raise an exception' do
|
64
|
+
expect do
|
65
|
+
expect { described_class.call('http://the.unhandled.com', 'my-title', accumulator) }.to raise_error(described_class::UnhandledPredicateError)
|
66
|
+
end.not_to change { accumulator.to_rof }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rof/translators/jsonld_to_rof/predicate_object_handler'
|
3
|
+
|
4
|
+
module ROF
|
5
|
+
module Translators
|
6
|
+
module JsonldToRof
|
7
|
+
RSpec.describe PredicateObjectHandler do
|
8
|
+
let(:accumulator) { Accumulator.new }
|
9
|
+
let(:rdf_predicate) { RDF::URI.new("http://purl.org/dc/terms/title") }
|
10
|
+
describe '.call' do
|
11
|
+
subject { described_class.call(rdf_predicate, rdf_object, accumulator) }
|
12
|
+
context 'for an RDF::URI' do
|
13
|
+
let(:rdf_object) { RDF::URI.new("http://curate.nd.edu/show/1234") }
|
14
|
+
it 'accumulates the value' do
|
15
|
+
subject
|
16
|
+
expect(accumulator.to_rof.fetch('metadata').fetch('dc:title')).to eq(['und:1234'])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
context 'for an RDF::Literal' do
|
20
|
+
let(:rdf_object) { RDF::Literal.new("Hello World") }
|
21
|
+
it 'accumulates the value' do
|
22
|
+
subject
|
23
|
+
expect(accumulator.to_rof.fetch('metadata').fetch('dc:title')).to eq(['Hello World'])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
context 'for an RDF::Node' do
|
27
|
+
let(:rdf_subject) { RDF::Node.new('_b0') }
|
28
|
+
let(:rdf_object) { RDF::Node.new('_b0') }
|
29
|
+
let(:rdf_predicate) { RDF::URI.new("http://www.ndltd.org/standards/metadata/etdms/1.1/") }
|
30
|
+
it 'expands the blank nodes already accumulated values' do
|
31
|
+
statement = RDF::Statement.new(subject: rdf_object, predicate: RDF::URI.new('http://www.ndltd.org/standards/metadata/etdms/1.1/name'), object: RDF::Literal.new('Awesome Sauce'))
|
32
|
+
accumulator.add_blank_node(statement)
|
33
|
+
subject
|
34
|
+
expect(accumulator.to_rof.fetch('metadata').fetch('ms:degree')).to eq([{ "ms:name" => ['Awesome Sauce'] }])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'for an unhandled object type' do
|
39
|
+
let(:rdf_object) { double }
|
40
|
+
it 'raises an error' do
|
41
|
+
expect { subject }.to raise_error(described_class::UnknownRdfObjectTypeError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rof/translators/jsonld_to_rof/statement_handler'
|
3
|
+
require 'rof/translators/jsonld_to_rof/accumulator'
|
4
|
+
|
5
|
+
module ROF
|
6
|
+
module Translators
|
7
|
+
module JsonldToRof
|
8
|
+
RSpec.describe StatementHandler do
|
9
|
+
let(:accumulator) { Accumulator.new }
|
10
|
+
let(:statement) { RDF::Statement.new(subject: rdf_subject, predicate: rdf_predicate, object: rdf_object) }
|
11
|
+
let(:rdf_predicate) { RDF::URI.new("http://purl.org/dc/terms/title") }
|
12
|
+
let(:rdf_object) { RDF::Literal.new("Hello World") }
|
13
|
+
describe '.call' do
|
14
|
+
subject { described_class.call(statement, accumulator) }
|
15
|
+
context 'for a URI subject' do
|
16
|
+
let(:pid) { 'abcd1234' }
|
17
|
+
let(:rdf_subject) { RDF::URI.new("https://curate.nd.edu/show/#{pid}") }
|
18
|
+
it 'accumulates the pid' do
|
19
|
+
subject
|
20
|
+
expect(accumulator.to_rof.fetch('pid')).to eq("und:#{pid}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
context 'for a Node subject' do
|
24
|
+
let(:rdf_subject) { RDF::Node.new("_b1") }
|
25
|
+
it 'does not accumulate a pid' do
|
26
|
+
subject
|
27
|
+
expect(accumulator.to_rof.key?('pid')).to eq(false)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context 'for a something else' do
|
31
|
+
let(:rdf_subject) { nil }
|
32
|
+
it 'raises an error' do
|
33
|
+
expect { subject }.to raise_error(described_class::UnhandledRdfSubjectError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ROF
|
4
|
+
module Translators
|
5
|
+
# ```console
|
6
|
+
# ssh app@sipity.library.nd.edu
|
7
|
+
# cd /mnt/curatend-batch/production/success
|
8
|
+
# find . -maxdepth 2 -name '*.rof' -type f | xargs grep -l <pid>
|
9
|
+
# ```
|
10
|
+
RSpec.describe JsonldToRof do
|
11
|
+
describe '.call' do
|
12
|
+
[
|
13
|
+
'2j62s467216',
|
14
|
+
'js956d59913',
|
15
|
+
'xg94hm53h0c',
|
16
|
+
'0g354f18610',
|
17
|
+
'2v23vt16z2z',
|
18
|
+
'h989r21069m',
|
19
|
+
'cr56n01253w',
|
20
|
+
'm039k358q5c',
|
21
|
+
'zk51vd69n1r',
|
22
|
+
'nk322b9161g',
|
23
|
+
'p8418k7430d'
|
24
|
+
].each do |noid|
|
25
|
+
context "with JSON-LD from NOID=#{noid} CurateND work that was ingested via the batch ingester" do
|
26
|
+
it 'will return ROF that is a subset of the ROF used by the batch ingestor' do
|
27
|
+
rof_generated_via_batch = normalize_rof(noid)
|
28
|
+
|
29
|
+
jsonld_from_curatend = JSON.load(File.read(File.join(GEM_ROOT, "spec/fixtures/jsonld_to_rof/#{noid}.jsonld")))
|
30
|
+
expected_output = Array.wrap(rof_generated_via_batch)
|
31
|
+
actual_output = described_class.call(jsonld_from_curatend, {})
|
32
|
+
keys = (actual_output.first.keys + expected_output.first.keys).uniq
|
33
|
+
expected_rof = {}
|
34
|
+
actual_rof = {}
|
35
|
+
keys.each do |key|
|
36
|
+
actual_metadata = normalize(actual_output.first.fetch(key, {}))
|
37
|
+
actual_rof[key] = actual_metadata
|
38
|
+
|
39
|
+
expected_metadata = normalize(expected_output.first.fetch(key, {}))
|
40
|
+
expected_rof[key] = expected_metadata
|
41
|
+
# We may have {} for one, and [] for another. In this case, both are empty, so we'll skip.
|
42
|
+
next if actual_metadata.empty? && expected_metadata.empty?
|
43
|
+
expect(actual_metadata).to eq(expected_metadata), "Mismatch on #{key}.\n\tJSON-LD: #{actual_metadata.inspect}\n\tROF: #{expected_metadata.inspect}"
|
44
|
+
end
|
45
|
+
comparer = ROF::CompareRof.new(actual_rof, expected_rof, skip_rels_ext_context: true)
|
46
|
+
expect(comparer.compare_rights).to eq(0)
|
47
|
+
expect(comparer.compare_rels_ext).to eq(0)
|
48
|
+
expect(comparer.compare_metadata).to eq(0)
|
49
|
+
expect(comparer.compare_everything_else).to eq(0)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def normalize_rof(noid)
|
55
|
+
path_to_rof = File.read(File.join(GEM_ROOT, "spec/fixtures/jsonld_to_rof/#{noid}.rof"))
|
56
|
+
|
57
|
+
# Normalizing some of the @context entries to reflect JSON-LD entries
|
58
|
+
if path_to_rof.include?('"ths": "http://id.loc.gov/vocabulary/relators/"')
|
59
|
+
path_to_rof.gsub!('"ths": "http://id.loc.gov/vocabulary/relators/"', '"mrel": "http://id.loc.gov/vocabulary/relators/"')
|
60
|
+
path_to_rof.gsub!('"ths:', '"mrel:')
|
61
|
+
end
|
62
|
+
rof = JSON.load(path_to_rof)
|
63
|
+
# Removing @id as they are superflous
|
64
|
+
rof[0]['metadata'].delete('@id') if rof[0].key?('metadata')
|
65
|
+
rof[0]['rels-ext'].delete('@id') if rof[0].key?('rels-ext')
|
66
|
+
rof
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# @todo For any key with an empty array, delete that key
|
71
|
+
# Responsible for normalizing a Hash or non-Hash
|
72
|
+
def normalize(input)
|
73
|
+
if input.is_a?(Hash)
|
74
|
+
returning_hash = {}
|
75
|
+
input.keys.sort.each do |key|
|
76
|
+
raw_value = input[key]
|
77
|
+
next if key == '@context' # Because Sipity's ROF context was messed up
|
78
|
+
Array.wrap(raw_value).each do |value|
|
79
|
+
returning_hash[key] ||= []
|
80
|
+
if value.is_a?(Hash)
|
81
|
+
hash = {}
|
82
|
+
value.keys.sort.each do |sorted_key|
|
83
|
+
hash[sorted_key] ||= []
|
84
|
+
Array.wrap(value[sorted_key]).sort.each do |sorted_value|
|
85
|
+
hash[sorted_key] << normalize_string(sorted_value.dup)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
value = hash
|
89
|
+
else
|
90
|
+
normalize_string(value)
|
91
|
+
end
|
92
|
+
returning_hash[key] << value
|
93
|
+
end
|
94
|
+
begin
|
95
|
+
returning_hash[key] = returning_hash[key].sort if returning_hash[key].respond_to?(:sort)
|
96
|
+
rescue ArgumentError
|
97
|
+
returning_hash[key]
|
98
|
+
end
|
99
|
+
# next unless returning_hash[key].present?
|
100
|
+
if returning_hash[key]
|
101
|
+
returning_hash[key] = returning_hash[key].reject(&:empty?)
|
102
|
+
returning_hash.delete(key) if returning_hash[key].empty?
|
103
|
+
end
|
104
|
+
end
|
105
|
+
returning_hash
|
106
|
+
else
|
107
|
+
Array.wrap(input).map { |obj| normalize_string(obj) }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def normalize_string(input)
|
112
|
+
# Forcing escaped unicode hexadecimal to the "human" readable format
|
113
|
+
input.gsub!(/\\u([0-F]{4})/) { '' << Regexp.last_match[1].to_i(16) }
|
114
|
+
input.gsub!("\n", '')
|
115
|
+
input
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|