resync 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +42 -0
  3. data/.rubocop.yml +23 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +2 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE.md +22 -0
  8. data/README.md +92 -0
  9. data/Rakefile +56 -0
  10. data/example.rb +100 -0
  11. data/lib/resync/capability_list.rb +85 -0
  12. data/lib/resync/change_dump.rb +15 -0
  13. data/lib/resync/change_dump_manifest.rb +15 -0
  14. data/lib/resync/change_list.rb +15 -0
  15. data/lib/resync/change_list_index.rb +26 -0
  16. data/lib/resync/link.rb +87 -0
  17. data/lib/resync/metadata.rb +112 -0
  18. data/lib/resync/resource.rb +72 -0
  19. data/lib/resync/resource_dump.rb +15 -0
  20. data/lib/resync/resource_dump_manifest.rb +15 -0
  21. data/lib/resync/resource_list.rb +15 -0
  22. data/lib/resync/resource_list_index.rb +15 -0
  23. data/lib/resync/shared/augmented.rb +76 -0
  24. data/lib/resync/shared/base_resource_list.rb +117 -0
  25. data/lib/resync/shared/descriptor.rb +135 -0
  26. data/lib/resync/shared/sitemap_index.rb +32 -0
  27. data/lib/resync/shared/sorted_resource_list.rb +60 -0
  28. data/lib/resync/source_description.rb +14 -0
  29. data/lib/resync/types/change.rb +14 -0
  30. data/lib/resync/types/change_frequency.rb +18 -0
  31. data/lib/resync/types.rb +6 -0
  32. data/lib/resync/version.rb +4 -0
  33. data/lib/resync/xml.rb +216 -0
  34. data/lib/resync/xml_parser.rb +65 -0
  35. data/lib/resync.rb +4 -0
  36. data/resync.gemspec +36 -0
  37. data/spec/acceptance/xml_parser_spec.rb +1049 -0
  38. data/spec/data/examples/README.md +1 -0
  39. data/spec/data/examples/example-1.xml +12 -0
  40. data/spec/data/examples/example-12.xml +25 -0
  41. data/spec/data/examples/example-13.xml +25 -0
  42. data/spec/data/examples/example-14.xml +23 -0
  43. data/spec/data/examples/example-15.xml +21 -0
  44. data/spec/data/examples/example-16.xml +24 -0
  45. data/spec/data/examples/example-17.xml +39 -0
  46. data/spec/data/examples/example-18.xml +25 -0
  47. data/spec/data/examples/example-19.xml +28 -0
  48. data/spec/data/examples/example-2.xml +18 -0
  49. data/spec/data/examples/example-20.xml +22 -0
  50. data/spec/data/examples/example-21.xml +31 -0
  51. data/spec/data/examples/example-22.xml +41 -0
  52. data/spec/data/examples/example-23.xml +41 -0
  53. data/spec/data/examples/example-24.xml +28 -0
  54. data/spec/data/examples/example-25.xml +21 -0
  55. data/spec/data/examples/example-26.xml +18 -0
  56. data/spec/data/examples/example-27.xml +36 -0
  57. data/spec/data/examples/example-28.xml +34 -0
  58. data/spec/data/examples/example-29.xml +27 -0
  59. data/spec/data/examples/example-3.xml +17 -0
  60. data/spec/data/examples/example-30.xml +18 -0
  61. data/spec/data/examples/example-31.xml +16 -0
  62. data/spec/data/examples/example-32.xml +22 -0
  63. data/spec/data/examples/example-33.xml +22 -0
  64. data/spec/data/examples/example-4.xml +10 -0
  65. data/spec/data/examples/example-5.xml +18 -0
  66. data/spec/data/examples/example-6.xml +21 -0
  67. data/spec/data/examples/example-7.xml +13 -0
  68. data/spec/data/examples/example-8.xml +12 -0
  69. data/spec/data/resourcesync.xsd +148 -0
  70. data/spec/data/siteindex.xsd +75 -0
  71. data/spec/data/sitemap.xsd +116 -0
  72. data/spec/rspec_custom_matchers.rb +89 -0
  73. data/spec/spec_helper.rb +31 -0
  74. data/spec/todo.rb +11 -0
  75. data/spec/unit/resync/capability_list_spec.rb +138 -0
  76. data/spec/unit/resync/change_dump_manifest_spec.rb +75 -0
  77. data/spec/unit/resync/change_dump_spec.rb +61 -0
  78. data/spec/unit/resync/change_list_index_spec.rb +49 -0
  79. data/spec/unit/resync/change_list_spec.rb +75 -0
  80. data/spec/unit/resync/link_spec.rb +93 -0
  81. data/spec/unit/resync/metadata_spec.rb +169 -0
  82. data/spec/unit/resync/resource_dump_manifest_spec.rb +59 -0
  83. data/spec/unit/resync/resource_dump_spec.rb +62 -0
  84. data/spec/unit/resync/resource_list_index_spec.rb +53 -0
  85. data/spec/unit/resync/resource_list_spec.rb +60 -0
  86. data/spec/unit/resync/resource_spec.rb +176 -0
  87. data/spec/unit/resync/shared/augmented_examples.rb +58 -0
  88. data/spec/unit/resync/shared/base_resource_list_examples.rb +103 -0
  89. data/spec/unit/resync/shared/descriptor_examples.rb +122 -0
  90. data/spec/unit/resync/shared/descriptor_spec.rb +33 -0
  91. data/spec/unit/resync/shared/sorted_list_examples.rb +134 -0
  92. data/spec/unit/resync/shared/uri_field_examples.rb +36 -0
  93. data/spec/unit/resync/source_description_spec.rb +55 -0
  94. data/spec/unit/resync/xml/timenode_spec.rb +48 -0
  95. data/spec/unit/resync/xml/xml_spec.rb +40 -0
  96. data/spec/unit/resync/xml_parser_spec.rb +82 -0
  97. metadata +340 -0
@@ -0,0 +1,59 @@
1
+ require_relative 'shared/base_resource_list_examples'
2
+
3
+ module Resync
4
+ describe ResourceDumpManifest do
5
+ it_behaves_like BaseResourceList
6
+
7
+ describe 'links' do
8
+ it_behaves_like Augmented
9
+ end
10
+
11
+ describe 'XML conversion' do
12
+ describe '#from_xml' do
13
+ it 'parses an XML string' do
14
+ data = File.read('spec/data/examples/example-18.xml')
15
+ urlset = ResourceDumpManifest.load_from_xml(XML.element(data))
16
+
17
+ links = urlset.links
18
+ expect(links.size).to eq(1)
19
+ ln0 = links[0]
20
+ expect(ln0.rel).to eq('up')
21
+ expect(ln0.uri).to eq(URI('http://example.com/dataset1/capabilitylist.xml'))
22
+
23
+ md = urlset.metadata
24
+ expect(md.capability).to eq('resourcedump-manifest')
25
+ expect(md.at_time).to be_time(Time.utc(2013, 1, 3, 9))
26
+ expect(md.completed_time).to be_time(Time.utc(2013, 1, 3, 9, 2))
27
+
28
+ urls = urlset.resources
29
+ expect(urls.size).to eq(2)
30
+
31
+ expected_lastmods = [Time.utc(2013, 1, 2, 13), Time.utc(2013, 1, 2, 14)]
32
+ expected_hashes = [{ 'md5' => '1584abdf8ebdc9802ac0c6a7402c03b6' }, { 'md5' => '1e0d5cb8ef6ba40c99b14c0237be735e', 'sha-256' => '854f61290e2e197a11bc91063afce22e43f8ccc655237050ace766adc68dc784' }]
33
+ expected_lengths = [8876, 14_599]
34
+ expected_types = ['text/html', 'application/pdf']
35
+
36
+ (0..1).each do |i|
37
+ url = urls[i]
38
+ expect(url.uri).to eq(URI("http://example.com/res#{i + 1}"))
39
+ expect(url.modified_time).to be_time(expected_lastmods[i])
40
+ md = url.metadata
41
+ expect(md.hashes).to eq(expected_hashes[i])
42
+ expect(md.length).to eq(expected_lengths[i])
43
+ expect(md.mime_type).to be_mime_type(expected_types[i])
44
+ expect(md.path).to eq("/resources/res#{i + 1}")
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '#save_to_xml' do
50
+ it 'can round-trip to XML' do
51
+ data = File.read('spec/data/examples/example-18.xml')
52
+ manifest = ResourceDumpManifest.load_from_xml(XML.element(data))
53
+ xml = manifest.save_to_xml
54
+ expect(xml).to be_xml(data)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,62 @@
1
+ require_relative 'shared/base_resource_list_examples'
2
+
3
+ module Resync
4
+ describe ResourceDump do
5
+ it_behaves_like BaseResourceList
6
+
7
+ describe 'links' do
8
+ it_behaves_like Augmented
9
+ end
10
+
11
+ describe 'XML conversion' do
12
+ describe '#from_xml' do
13
+ it 'parses an XML string' do
14
+ xml = File.read('spec/data/examples/example-17.xml')
15
+ urlset = ResourceDump.load_from_xml(XML.element(xml))
16
+ links = urlset.links
17
+ expect(links.size).to eq(1)
18
+ ln0 = links[0]
19
+ expect(ln0.rel).to eq('up')
20
+ expect(ln0.uri).to eq(URI('http://example.com/dataset1/capabilitylist.xml'))
21
+
22
+ md = urlset.metadata
23
+ expect(md.capability).to eq('resourcedump')
24
+ expect(md.at_time).to be_time(Time.utc(2013, 1, 3, 9))
25
+ expect(md.completed_time).to be_time(Time.utc(2013, 1, 3, 9, 4))
26
+
27
+ urls = urlset.resources
28
+ expect(urls.size).to eq(3)
29
+
30
+ expected_lengths = [4765, 9875, 2298]
31
+ expected_ats = [Time.utc(2013, 1, 3, 9), Time.utc(2013, 1, 3, 9, 1), Time.utc(2013, 1, 3, 9, 3)]
32
+ expected_completeds = [Time.utc(2013, 1, 3, 9, 2), Time.utc(2013, 1, 3, 9, 3), Time.utc(2013, 1, 3, 9, 4)]
33
+
34
+ (0..2).each do |i|
35
+ url = urls[i]
36
+ expect(url.uri).to eq(URI("http://example.com/resourcedump-part#{i + 1}.zip"))
37
+ md = url.metadata
38
+ expect(md.mime_type).to be_mime_type('application/zip')
39
+ expect(md.length).to eq(expected_lengths[i])
40
+ expect(md.at_time).to be_time(expected_ats[i])
41
+ expect(md.completed_time).to be_time(expected_completeds[i])
42
+ links = url.links
43
+ expect(links.size).to eq(1)
44
+ ln = links[0]
45
+ expect(ln.rel).to eq('contents')
46
+ expect(ln.uri).to eq(URI("http://example.com/resourcedump_manifest-part#{i + 1}.xml"))
47
+ expect(ln.mime_type).to be_mime_type('application/xml')
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#save_to_xml' do
53
+ it 'can round-trip to XML' do
54
+ data = File.read('spec/data/examples/example-17.xml')
55
+ dump = ResourceDump.load_from_xml(XML.element(data))
56
+ xml = dump.save_to_xml
57
+ expect(xml).to be_xml(data)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,53 @@
1
+ require_relative 'shared/base_resource_list_examples'
2
+
3
+ module Resync
4
+ describe ResourceListIndex do
5
+ it_behaves_like BaseResourceList
6
+
7
+ describe 'links' do
8
+ it_behaves_like Augmented
9
+ end
10
+
11
+ describe 'XML conversion' do
12
+ describe '#from_xml' do
13
+ it 'parses an XML string' do
14
+ data = XML.element(File.read('spec/data/examples/example-15.xml'))
15
+ sitemapindex = ResourceListIndex.load_from_xml(data)
16
+
17
+ links = sitemapindex.links
18
+ expect(links.size).to eq(1)
19
+ ln0 = links[0]
20
+ expect(ln0.rel).to eq('up')
21
+ expect(ln0.uri).to eq(URI('http://example.com/dataset1/capabilitylist.xml'))
22
+
23
+ md = sitemapindex.metadata
24
+ expect(md.capability).to eq('resourcelist')
25
+ expect(md.at_time).to be_time(Time.utc(2013, 1, 3, 9))
26
+ expect(md.completed_time).to be_time(Time.utc(2013, 1, 3, 9, 10))
27
+
28
+ sitemaps = sitemapindex.resources
29
+ expect(sitemaps.size).to eq(3)
30
+
31
+ expected_times = [Time.utc(2013, 1, 3, 9), Time.utc(2013, 1, 3, 9, 3), Time.utc(2013, 1, 3, 9, 7)]
32
+ (0..2).each do |i|
33
+ sitemap = sitemaps[i]
34
+ expect(sitemap.uri).to eq(URI("http://example.com/resourcelist#{i + 1}.xml"))
35
+ md = sitemap.metadata
36
+ expect(md.at_time).to be_time(expected_times[i])
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#save_to_xml' do
42
+ it 'can round-trip to XML' do
43
+ data = XML.element(File.read('spec/data/examples/example-15.xml'))
44
+ list = ResourceListIndex.load_from_xml(XML.element(data), mapping: :sitemapindex)
45
+ xml = list.save_to_xml
46
+ expect(xml).to be_xml(data)
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,60 @@
1
+ require_relative 'shared/base_resource_list_examples'
2
+
3
+ module Resync
4
+ describe ResourceList do
5
+ it_behaves_like BaseResourceList
6
+
7
+ describe 'links' do
8
+ it_behaves_like Augmented
9
+ end
10
+
11
+ describe 'XML conversion' do
12
+ describe '#from_xml' do
13
+ it 'parses an XML string' do
14
+ xml = File.read('spec/data/examples/example-16.xml')
15
+ list = ResourceList.load_from_xml(XML.element(xml))
16
+ links = list.links
17
+ expect(links.size).to eq(2)
18
+ ln0 = links[0]
19
+ expect(ln0.rel).to eq('up')
20
+ expect(ln0.uri).to eq(URI('http://example.com/dataset1/capabilitylist.xml'))
21
+ ln1 = links[1]
22
+ expect(ln1.rel).to eq('index')
23
+ expect(ln1.uri).to eq(URI('http://example.com/dataset1/resourcelist-index.xml'))
24
+
25
+ md = list.metadata
26
+ expect(md.capability).to eq('resourcelist')
27
+ expect(md.at_time).to be_time(Time.utc(2013, 1, 3, 9))
28
+
29
+ urls = list.resources
30
+ expect(urls.size).to eq(2)
31
+
32
+ expected_lastmods = [Time.utc(2013, 1, 2, 13), Time.utc(2013, 1, 2, 14)]
33
+ expected_hashes = [{ 'md5' => '1584abdf8ebdc9802ac0c6a7402c8753' }, { 'md5' => '4556abdf8ebdc9802ac0c6a7402c9881' }]
34
+ expected_lengths = [4385, 883]
35
+ expected_types = ['application/pdf', 'image/png']
36
+
37
+ (0..1).each do |i|
38
+ url = urls[i]
39
+ expect(url.uri).to eq(URI("http://example.com/res#{i + 3}"))
40
+ expect(url.modified_time).to be_time(expected_lastmods[i])
41
+ md = url.metadata
42
+ expect(md.hashes).to eq(expected_hashes[i])
43
+ expect(md.length).to eq(expected_lengths[i])
44
+ expect(md.mime_type).to be_mime_type(expected_types[i])
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '#save_to_xml' do
50
+ it 'can round-trip to XML' do
51
+ data = File.read('spec/data/examples/example-16.xml')
52
+ list = ResourceList.load_from_xml(XML.element(data))
53
+ xml = list.save_to_xml
54
+ expect(xml).to be_xml(data)
55
+ end
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,176 @@
1
+ require 'spec_helper'
2
+ require_relative 'shared/uri_field_examples'
3
+ require_relative 'shared/augmented_examples'
4
+
5
+ module Resync
6
+ describe Resource do
7
+ describe '#new' do
8
+
9
+ describe 'uri' do
10
+ it_behaves_like 'a URI field'
11
+ end
12
+
13
+ describe 'modified_time' do
14
+ it 'accepts a modified_time timestamp' do
15
+ lastmod = Time.utc(1997, 7, 16, 19, 20, 30.45)
16
+ resource = Resource.new(uri: 'http://example.org', modified_time: lastmod)
17
+ expect(resource.modified_time).to be_time(lastmod)
18
+ end
19
+
20
+ it 'defaults to nil if no modified_time timestamp specified' do
21
+ resource = Resource.new(uri: 'http://example.org')
22
+ expect(resource.modified_time).to be_nil
23
+ end
24
+ end
25
+
26
+ describe 'links' do
27
+ def required_arguments
28
+ { uri: 'http://example.org' }
29
+ end
30
+ it_behaves_like Augmented
31
+ end
32
+
33
+ describe 'metadata' do
34
+ it 'accepts metadata' do
35
+ md = Metadata.new
36
+ resource = Resource.new(uri: 'http://example.org', metadata: md)
37
+ expect(resource.metadata).to eq(md)
38
+ end
39
+
40
+ it 'defaults to nil if metadata not specified' do
41
+ resource = Resource.new(uri: 'http://example.org')
42
+ expect(resource.metadata).to be_nil
43
+ end
44
+ end
45
+
46
+ describe 'changefreq' do
47
+ it 'accepts a change frequency' do
48
+ cf = Types::ChangeFrequency::DAILY
49
+ resource = Resource.new(uri: 'http://example.org', changefreq: cf)
50
+ expect(resource.changefreq).to eq(cf)
51
+ end
52
+
53
+ it 'defaults to nil if no change frequency specified' do
54
+ resource = Resource.new(uri: 'http://example.org')
55
+ expect(resource.changefreq).to be_nil
56
+ end
57
+ end
58
+
59
+ describe 'priority' do
60
+ it 'accepts a priority' do
61
+ priority = 1.234
62
+ resource = Resource.new(uri: 'http://example.org', priority: priority)
63
+ expect(resource.priority).to eq(priority)
64
+ end
65
+
66
+ it 'defaults to nil if no priority specified' do
67
+ resource = Resource.new(uri: 'http://example.org')
68
+ expect(resource.priority).to be_nil
69
+ end
70
+ end
71
+ end
72
+
73
+ describe 'capability' do
74
+ it 'extracts the capability from the metadata' do
75
+ md = Metadata.new(capability: 'changelist')
76
+ resource = Resource.new(uri: 'http://example.org', metadata: md)
77
+ expect(resource.capability).to eq('changelist')
78
+ end
79
+
80
+ it 'returns nil if no metadata was specified' do
81
+ resource = Resource.new(uri: 'http://example.org')
82
+ expect(resource.capability).to be_nil
83
+ end
84
+ end
85
+
86
+ describe 'XML conversion' do
87
+ describe '#from_xml' do
88
+ it 'parses an XML string' do
89
+ xml = '<url xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:rs="http://www.openarchives.org/rs/terms/">
90
+ <loc>http://example.com/res1</loc>
91
+ <lastmod>2013-01-03T18:00:00Z</lastmod>
92
+ <rs:md change="updated"
93
+ hash="md5:1584abdf8ebdc9802ac0c6a7402c03b6"
94
+ length="8876"
95
+ type="text/html"/>
96
+ <rs:ln rel="duplicate"
97
+ pri="1"
98
+ href="http://mirror1.example.com/res1"
99
+ modified="2013-01-03T18:00:00Z"/>
100
+ <rs:ln rel="duplicate"
101
+ pri="2"
102
+ href="http://mirror2.example.com/res1"
103
+ modified="2013-01-03T18:00:00Z"/>
104
+ <rs:ln rel="duplicate"
105
+ pri="3"
106
+ href="gsiftp://gridftp.example.com/res1"
107
+ modified="2013-01-03T18:00:00Z"/>
108
+ </url>'
109
+ resource = Resource.load_from_xml(XML.element(xml))
110
+ expect(resource).to be_a(Resource)
111
+ expect(resource.uri).to eq(URI('http://example.com/res1'))
112
+ expect(resource.modified_time).to be_time(Time.utc(2013, 1, 3, 18))
113
+ metadata = resource.metadata
114
+ expect(metadata.change).to eq(Types::Change::UPDATED)
115
+ expect(metadata.hash('md5')).to eq('1584abdf8ebdc9802ac0c6a7402c03b6')
116
+ expect(metadata.length).to eq(8_876)
117
+ expect(metadata.mime_type).to be_mime_type('text/html')
118
+ links = resource.links
119
+ expect(links.size).to eq(3)
120
+ expected_uris = [URI('http://mirror1.example.com/res1'),
121
+ URI('http://mirror2.example.com/res1'),
122
+ URI('gsiftp://gridftp.example.com/res1')]
123
+
124
+ (0..2).each do |i|
125
+ ln = links[i]
126
+ expect(ln.rel).to eq('duplicate')
127
+ expect(ln.priority).to eq(i + 1)
128
+ expect(ln.uri).to eq(expected_uris[i])
129
+ expect(ln.modified_time).to be_time(Time.utc(2013, 1, 3, 18))
130
+ end
131
+ end
132
+ end
133
+
134
+ it 'can round-trip to XML' do
135
+ data = '<url xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:rs="http://www.openarchives.org/rs/terms/">
136
+ <loc>http://example.com/res1</loc>
137
+ <lastmod>2013-01-03T18:00:00Z</lastmod>
138
+ <changefreq>daily</changefreq>
139
+ <rs:ln rel="duplicate"
140
+ pri="1"
141
+ href="http://mirror1.example.com/res1"
142
+ modified="2013-01-03T18:00:00Z"/>
143
+ <rs:ln rel="duplicate"
144
+ pri="2"
145
+ href="http://mirror2.example.com/res1"
146
+ modified="2013-01-03T18:00:00Z"/>
147
+ <rs:md change="updated"
148
+ hash="md5:1584abdf8ebdc9802ac0c6a7402c03b6"
149
+ length="8876"
150
+ type="text/html"/>
151
+ <rs:ln rel="duplicate"
152
+ pri="3"
153
+ href="gsiftp://gridftp.example.com/res1"
154
+ modified="2013-01-03T18:00:00Z"/>
155
+ </url>'
156
+ resource = Resource.load_from_xml(XML.element(data))
157
+
158
+ # Since resource isn't a root element, these won't be hacked in as in BaseResourceList#pre_save()
159
+ xml = resource.save_to_xml
160
+ xml.add_namespace('http://www.sitemaps.org/schemas/sitemap/0.9')
161
+ xml.add_namespace('rs', 'http://www.openarchives.org/rs/terms/')
162
+
163
+ expect(xml).to be_xml(data)
164
+ end
165
+
166
+ it 'doesn\'t have side effects that prevent <sitemapindex> parsing' do
167
+ Resource.xml_mapping_nodes(mapping: :sitemapindex)
168
+
169
+ data = File.read('spec/data/examples/example-8.xml')
170
+ root = REXML::Document.new(data).root
171
+ sitemapindex = XMLParser.parse(root)
172
+ expect(sitemapindex).to be_a(ResourceListIndex)
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,58 @@
1
+ module Resync
2
+ # TODO: Figure out if 'up' is mandatory for all resource lists, or just CapabilityList
3
+ # TODO: Consider requiring this in base_resource_list_examples again, if 'up' is mandatory
4
+ RSpec.shared_examples Augmented do
5
+
6
+ # TODO: Find a better way to express this
7
+ def new_instance(**args)
8
+ required_args = (defined? required_arguments) ? required_arguments : {}
9
+ args = required_args.merge(args)
10
+ described_class.new(**args)
11
+ end
12
+
13
+ describe 'links' do
14
+ it 'accepts a list of links' do
15
+ links = [Link.new(rel: 'describedby', uri: 'http://example.org/'), Link.new(rel: 'duplicate', uri: 'http://example.com/')]
16
+ list = new_instance(links: links)
17
+ expect(list.links).to eq(links)
18
+ end
19
+
20
+ it 'defaults to an empty list if no links are specified' do
21
+ list = new_instance
22
+ expect(list.links).to eq([])
23
+ end
24
+
25
+ end
26
+
27
+ describe '#links_for' do
28
+ it 'can retrieve a list of links by rel' do
29
+ links = [
30
+ Link.new(rel: 'describedby', uri: 'http://example.org/desc1'),
31
+ Link.new(rel: 'duplicate', uri: 'http://example.com/dup1'),
32
+ Link.new(rel: 'describedby', uri: 'http://example.org/desc2'),
33
+ Link.new(rel: 'duplicate', uri: 'http://example.com/dup2')
34
+ ]
35
+ list = new_instance(links: links)
36
+ expect(list.links_for(rel: 'describedby')).to eq([links[0], links[2]])
37
+ expect(list.links_for(rel: 'duplicate')).to eq([links[1], links[3]])
38
+ expect(list.links_for(rel: 'elvis')).to eq([])
39
+ end
40
+ end
41
+
42
+ describe '#link_for' do
43
+ it 'can retrieve the first link for a rel' do
44
+ links = [
45
+ Link.new(rel: 'describedby', uri: 'http://example.org/desc1'),
46
+ Link.new(rel: 'duplicate', uri: 'http://example.com/dup1'),
47
+ Link.new(rel: 'describedby', uri: 'http://example.org/desc2'),
48
+ Link.new(rel: 'duplicate', uri: 'http://example.com/dup2')
49
+ ]
50
+ list = new_instance(links: links)
51
+ expect(list.link_for(rel: 'describedby')).to eq(links[0])
52
+ expect(list.link_for(rel: 'duplicate')).to eq(links[1])
53
+ expect(list.link_for(rel: 'elvis')).to eq(nil)
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+ require_relative 'augmented_examples'
3
+
4
+ module Resync
5
+ RSpec.shared_examples BaseResourceList do
6
+
7
+ # ------------------------------------------------------
8
+ # "Virtual" fixture methods
9
+
10
+ # TODO: Find a better way to express this
11
+ def resource_list
12
+ (defined? valid_resources) ? valid_resources : [Resource.new(uri: 'http://example.org/'), Resource.new(uri: 'http://example.com/')]
13
+ end
14
+
15
+ # TODO: Find a better way to express this
16
+ def new_instance(**args)
17
+ required_args = (defined? required_arguments) ? required_arguments : {}
18
+ args = required_args.merge(args)
19
+ described_class.new(**args)
20
+ end
21
+
22
+ # ------------------------------------------------------
23
+ # Tests
24
+
25
+ describe '#new' do
26
+ describe 'resources' do
27
+ it 'accepts a list of resources' do
28
+ puts defined? valid_resources
29
+ resources = resource_list
30
+ list = new_instance(resources: resources)
31
+ expect(list.resources).to eq(resources)
32
+ end
33
+
34
+ it 'defaults to an empty list if no resources are specified' do
35
+ list = new_instance
36
+ expect(list.resources).to eq([])
37
+ end
38
+ end
39
+
40
+ describe 'metadata' do
41
+ it 'accepts metadata' do
42
+ metadata = Metadata.new(capability: described_class::CAPABILITY)
43
+ list = new_instance(metadata: metadata)
44
+ expect(list.metadata).to eq(metadata)
45
+ end
46
+
47
+ it 'defaults (otherwise empty) metadata with capability CAPABILITY if no metadata specified' do
48
+ list = new_instance
49
+ metadata = list.metadata
50
+ expect(metadata.capability).to eq(described_class::CAPABILITY)
51
+ end
52
+
53
+ it 'fails if metadata does not have capability CAPABILITY' do
54
+ expect { new_instance(metadata: Metadata.new) }.to raise_error(ArgumentError)
55
+ expect { new_instance(metadata: Metadata.new(capability: "not_#{described_class::CAPABILITY}")) }.to raise_error(ArgumentError)
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '#resources_for' do
61
+ it 'can retrieve a list of resources by capability' do
62
+ unless defined? valid_resources
63
+ resources = [
64
+ Resource.new(uri: 'http://example.com/dataset1/resourcedump1.xml', metadata: Metadata.new(capability: 'resourcedump')),
65
+ Resource.new(uri: 'http://example.com/dataset1/changelist1.xml', metadata: Metadata.new(capability: 'changelist')),
66
+ Resource.new(uri: 'http://example.com/dataset1/resourcedump2.xml', metadata: Metadata.new(capability: 'resourcedump')),
67
+ Resource.new(uri: 'http://example.com/dataset1/changelist2.xml', metadata: Metadata.new(capability: 'changelist'))
68
+ ]
69
+ list = new_instance(resources: resources)
70
+ expect(list.resources_for(capability: 'resourcedump')).to eq([resources[0], resources[2]])
71
+ expect(list.resources_for(capability: 'changelist')).to eq([resources[1], resources[3]])
72
+ expect(list.resources_for(capability: 'changedump')).to eq([])
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#resource_for' do
78
+ it 'can retrieve the first resource for a capability' do
79
+ unless defined? valid_resources
80
+ resources = [
81
+ Resource.new(uri: 'http://example.com/dataset1/resourcedump1.xml', metadata: Metadata.new(capability: 'resourcedump')),
82
+ Resource.new(uri: 'http://example.com/dataset1/changelist1.xml', metadata: Metadata.new(capability: 'changelist')),
83
+ Resource.new(uri: 'http://example.com/dataset1/resourcedump2.xml', metadata: Metadata.new(capability: 'resourcedump')),
84
+ Resource.new(uri: 'http://example.com/dataset1/changelist2.xml', metadata: Metadata.new(capability: 'changelist'))
85
+ ]
86
+ list = new_instance(resources: resources)
87
+ expect(list.resource_for(capability: 'resourcedump')).to eq(resources[0])
88
+ expect(list.resource_for(capability: 'changelist')).to eq(resources[1])
89
+ expect(list.resource_for(capability: 'changedump')).to eq(nil)
90
+ end
91
+ end
92
+ end
93
+
94
+ describe 'capability' do
95
+ it 'extracts the capability' do
96
+ metadata = Metadata.new(capability: described_class::CAPABILITY)
97
+ list = new_instance(metadata: metadata)
98
+ expect(list.capability).to eq(described_class::CAPABILITY)
99
+ end
100
+ end
101
+
102
+ end
103
+ end