rof 1.0.7 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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