resync 0.1.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 (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