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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +9 -7
  3. data/LICENSE +201 -16
  4. data/Rakefile +46 -0
  5. data/bin/csv_to_rof +1 -2
  6. data/bin/fedora_to_rof +7 -1
  7. data/bin/jsonld_to_rof +26 -0
  8. data/bin/osf_to_rof +6 -2
  9. data/bin/rof +5 -19
  10. data/lib/rof.rb +2 -6
  11. data/lib/rof/access.rb +1 -1
  12. data/lib/rof/cli.rb +104 -67
  13. data/lib/rof/compare_rof.rb +68 -39
  14. data/lib/rof/filter.rb +21 -0
  15. data/lib/rof/filters.rb +38 -0
  16. data/lib/rof/filters/bendo.rb +15 -17
  17. data/lib/rof/filters/date_stamp.rb +5 -4
  18. data/lib/rof/filters/file_to_url.rb +5 -3
  19. data/lib/rof/filters/label.rb +9 -7
  20. data/lib/rof/filters/work.rb +7 -5
  21. data/lib/rof/ingest.rb +5 -0
  22. data/lib/rof/osf_context.rb +2 -2
  23. data/lib/rof/rdf_context.rb +2 -0
  24. data/lib/rof/translator.rb +18 -0
  25. data/lib/rof/translators.rb +23 -0
  26. data/lib/rof/{translate_csv.rb → translators/csv_to_rof.rb} +4 -3
  27. data/lib/rof/translators/fedora_to_rof.rb +244 -0
  28. data/lib/rof/translators/jsonld_to_rof.rb +112 -0
  29. data/lib/rof/translators/jsonld_to_rof/accumulator.rb +175 -0
  30. data/lib/rof/translators/jsonld_to_rof/predicate_handler.rb +223 -0
  31. data/lib/rof/translators/jsonld_to_rof/predicate_object_handler.rb +125 -0
  32. data/lib/rof/translators/jsonld_to_rof/statement_handler.rb +91 -0
  33. data/lib/rof/translators/osf_to_rof.rb +191 -0
  34. data/lib/rof/utility.rb +44 -1
  35. data/lib/rof/version.rb +1 -1
  36. data/rof.gemspec +10 -2
  37. data/spec/coverage_helper.rb +17 -0
  38. data/spec/fixtures/for_utility_load_items_from_json_file/multiple_items.json +8 -0
  39. data/spec/fixtures/for_utility_load_items_from_json_file/parse_error.json +3 -0
  40. data/spec/fixtures/for_utility_load_items_from_json_file/single_item.json +3 -0
  41. data/spec/fixtures/jsonld_to_rof/0g354f18610.jsonld +113 -0
  42. data/spec/fixtures/jsonld_to_rof/0g354f18610.rof +96 -0
  43. data/spec/fixtures/jsonld_to_rof/2j62s467216.jsonld +113 -0
  44. data/spec/fixtures/jsonld_to_rof/2j62s467216.rof +93 -0
  45. data/spec/fixtures/jsonld_to_rof/2v23vt16z2z.jsonld +70 -0
  46. data/spec/fixtures/jsonld_to_rof/2v23vt16z2z.rof +87 -0
  47. data/spec/fixtures/jsonld_to_rof/cr56n01253w.jsonld +84 -0
  48. data/spec/fixtures/jsonld_to_rof/cr56n01253w.rof +95 -0
  49. data/spec/fixtures/jsonld_to_rof/h989r21069m.jsonld +84 -0
  50. data/spec/fixtures/jsonld_to_rof/h989r21069m.rof +98 -0
  51. data/spec/fixtures/jsonld_to_rof/js956d59913.jsonld +79 -0
  52. data/spec/fixtures/jsonld_to_rof/js956d59913.rof +89 -0
  53. data/spec/fixtures/jsonld_to_rof/m039k358q5c.jsonld +80 -0
  54. data/spec/fixtures/jsonld_to_rof/m039k358q5c.rof +64 -0
  55. data/spec/fixtures/jsonld_to_rof/nk322b9161g.jsonld +89 -0
  56. data/spec/fixtures/jsonld_to_rof/nk322b9161g.rof +69 -0
  57. data/spec/fixtures/jsonld_to_rof/p8418k7430d.jsonld +84 -0
  58. data/spec/fixtures/jsonld_to_rof/p8418k7430d.rof +67 -0
  59. data/spec/fixtures/jsonld_to_rof/xg94hm53h0c.jsonld +98 -0
  60. data/spec/fixtures/jsonld_to_rof/xg94hm53h0c.rof +110 -0
  61. data/spec/fixtures/jsonld_to_rof/zk51vd69n1r.jsonld +94 -0
  62. data/spec/fixtures/jsonld_to_rof/zk51vd69n1r.rof +121 -0
  63. data/spec/fixtures/osf/phz6b.tar.gz +0 -0
  64. data/spec/lib/rof/access_spec.rb +30 -23
  65. data/spec/lib/rof/cli_spec.rb +83 -60
  66. data/spec/lib/rof/compare_rof_spec.rb +35 -24
  67. data/spec/lib/rof/filter_spec.rb +10 -0
  68. data/spec/lib/rof/filters/bendo_spec.rb +42 -0
  69. data/spec/lib/rof/filters/date_stamp_spec.rb +9 -5
  70. data/spec/lib/rof/filters/file_to_url_spec.rb +7 -3
  71. data/spec/lib/rof/filters/label_spec.rb +121 -77
  72. data/spec/lib/rof/filters/work_spec.rb +7 -4
  73. data/spec/lib/rof/filters_spec.rb +14 -0
  74. data/spec/lib/rof/translator_spec.rb +15 -0
  75. data/spec/lib/rof/{translate_csv_spec.rb → translators/csv_to_rof_spec.rb} +14 -14
  76. data/spec/lib/rof/translators/fedora_to_rof_spec.rb +64 -0
  77. data/spec/lib/rof/translators/jsonld_to_rof/accumulator_spec.rb +121 -0
  78. data/spec/lib/rof/translators/jsonld_to_rof/predicate_handler_spec.rb +73 -0
  79. data/spec/lib/rof/translators/jsonld_to_rof/predicate_object_handler_spec.rb +48 -0
  80. data/spec/lib/rof/translators/jsonld_to_rof/statement_handler_spec.rb +40 -0
  81. data/spec/lib/rof/translators/jsonld_to_rof_spec.rb +120 -0
  82. data/spec/lib/rof/{osf_to_rof_spec.rb → translators/osf_to_rof_spec.rb} +55 -25
  83. data/spec/lib/rof/translators_spec.rb +14 -0
  84. data/spec/lib/rof/utility_spec.rb +47 -1
  85. data/spec/spec_helper.rb +1 -1
  86. data/spec/support/an_rof_filter.rb +10 -0
  87. metadata +186 -15
  88. data/lib/rof/get_from_fedora.rb +0 -211
  89. data/lib/rof/osf_to_rof.rb +0 -123
  90. 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