rof 1.0.7 → 1.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.
- 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
|