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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d976bb155b49ce7339df955bfa20d30392d22b5e
4
+ data.tar.gz: 3c3fdaebce38202912ce7d56805a429578f77b1d
5
+ SHA512:
6
+ metadata.gz: d567ccc2977f4c46ea357765bb46b72d40a8ee20673c2fdd9f76e66f3cc1e5ca18c16f7fe9cbeb51788121b1f33b70bec6e0ba7b71fcf09eae431ee4a3875afa
7
+ data.tar.gz: f97193935fc1dc19de8e715a9502e6afc2cd150528eb71a471acb41be24bd7a95ab8e139f9245026d1ee45607e79aeb05ce231a6902599261994659ca14ed3ae
data/.gitignore ADDED
@@ -0,0 +1,42 @@
1
+ # Ruby defaults
2
+
3
+ /.bundle/
4
+ /.yardoc
5
+ /Gemfile.lock
6
+ /_yardoc/
7
+ /coverage/
8
+ /doc/
9
+ /pkg/
10
+ /spec/reports/
11
+ /tmp/
12
+ *.bundle
13
+ *.so
14
+ *.o
15
+ *.a
16
+ mkmf.log
17
+
18
+ # Rails engine
19
+
20
+ .bundle/
21
+ log/*.log
22
+ spec/dummy/db/*.sqlite3
23
+ spec/dummy/db/*.sqlite3-journal
24
+ spec/dummy/log/*.log
25
+ spec/dummy/tmp/
26
+ spec/dummy/.sass-cache
27
+
28
+ # IntellJ
29
+
30
+ *.iml
31
+ *.ipr
32
+ *.iws
33
+ .rakeTasks
34
+ .idea
35
+
36
+ # Emacs
37
+
38
+ *~
39
+
40
+ # Mac OS
41
+
42
+ .DS_Store
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ # Disable line-length check; it's too easy for the cure to be worse than the disease
2
+ Metrics/LineLength:
3
+ Enabled: False
4
+
5
+ # Disable problematic module documentation check (see https://github.com/bbatsov/rubocop/issues/947)
6
+ Style/Documentation:
7
+ Enabled: false
8
+
9
+ # Allow one line around class body (Style/EmptyLines will still disallow two or more)
10
+ Style/EmptyLinesAroundClassBody:
11
+ Enabled: false
12
+
13
+ # Allow one line around module body (Style/EmptyLines will still disallow two or more)
14
+ Style/EmptyLinesAroundModuleBody:
15
+ Enabled: false
16
+
17
+ # Allow one line around block body (Style/EmptyLines will still disallow two or more)
18
+ Style/EmptyLinesAroundBlockBody:
19
+ Enabled: false
20
+
21
+ # Allow %r notation for regexes with a single / character
22
+ Style/RegexpLiteral:
23
+ MaxSlashes: 0
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.2
data/.travis.yml ADDED
@@ -0,0 +1,2 @@
1
+ language: ruby
2
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 The Regents of the University of California
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # resync
2
+
3
+ A Ruby gem for working with the [ResourceSync](http://www.openarchives.org/rs/1.0/resourcesync) web synchronization framework.
4
+
5
+ It consists of the following:
6
+
7
+ - Classes corresponding to the major document types defined in the ResourceSync specification, such as [Resource Lists](http://www.openarchives.org/rs/1.0/resourcesync#ResourceList), [Change Lists](http://www.openarchives.org/rs/1.0/resourcesync#ChangeList), [Source Descriptions](http://www.openarchives.org/rs/1.0/resourcesync#SourceDesc) and so on. Each of these classes has a `load_from_xml` method that can parse the corresponding XML document (as an `REXML::Element`), and a `save_to_xml` method that can serialize an instance of that class to XML (as an `REXML::Element`).
8
+ - Classes for the [major sub-structures](http://www.openarchives.org/rs/1.0/resourcesync#DocumentFormats) of those documents, such as the `<url>` and `<sitemap>` tags (subsumed under the [Resource](lib/resync/resource.rb) class) defined by the Sitemap specification, as well as the ResourceSync-specific `<rs:ln>` and `<rs:md>` tags (the [Link](lib/resync/link.rb) and [Metadata](lib/resync/metadata.rb) classes, respectively).
9
+ - An [XMLParser](lib/resync/xml_parser.rb) class that can take a ResourceSync-augmented Sitemap document (in the form of an `REXML::Element`, an `REXML::Document`, a string, an `IO`, or something sufficiently `IO`-like that `REXML::Document` can parse it) and produce an instance of the appropriate class based on the `capability` attribute in the root element's metadata.
10
+
11
+ ## Status
12
+
13
+ This is a work in progress. We welcome bug reports and feature requests (particularly on the document creation side, which our use cases haven't really explored).
14
+
15
+ ## Usage
16
+
17
+ ### Parsing a ResourceSync document
18
+
19
+ ```ruby
20
+ require 'resync'
21
+
22
+ data = File.read('my-capability-list.xml')
23
+ capability_list = Resync::XMLParser.parse(data)
24
+ ```
25
+
26
+ ### Writing a ResourceSync document
27
+
28
+ ```ruby
29
+ require 'resync'
30
+
31
+ change_list = Resync::ChangeList.new(
32
+ links: [ Resync::Link.new(rel='up', href='http://example.com/my-dataset/my-capability-list.xml') ],
33
+ metadata: Resync::Metadata.new(
34
+ capability = 'changelist',
35
+ from_time = Time.utc(2013, 1, 3)
36
+ )
37
+ resources: [
38
+ # ... generate list of changes here ...
39
+ ]
40
+ )
41
+ xml = change_list.save_to_xml
42
+ formatter = REXML::Formatters::Pretty.new
43
+ formatter.write(xml, $stdout)
44
+ ```
45
+
46
+ ## See also
47
+
48
+ [resync-client](https://github.com/dmolesUC3/resync-client), a Ruby client library for ResourceSync.
49
+
50
+ ## Limitations
51
+
52
+ ### Structural inconvenience and unnecessary repetition
53
+
54
+ There are certain well-specified relationships between elements: most document types should always have a link with an `up` relationship, many resources should have metadata with a defined `capability` attribute, and so on. In some cases there are convenience getters for these attributes on the 'parent' object (e.g. you can ask for the `capability` directly without violating the law of Demeter), but there generally aren't corresponding convenience setters, or convenience initializer parameters.
55
+
56
+ Document types (`ChangeList`, `ResourceList`, etc.) will create a `Metadata` with the appropriate capability for themselves if none is specified, but if they're initialized with one that doesn't declare a capability, they'll raise an exception rather than fill it in (just as they'll raise an exception if the wrong capability is specified).
57
+
58
+ ### Logical relationships between elements
59
+
60
+ A `ChangeList` should contain only resources with `Metadata` declaring a `change` type. The resources in a `ResourceDumpManifest` should each declare a `path` indicating their locations in the ZIP file. `resync` doesn't currently do anything to enforce, validate, or assist in compliance with these and similar restrictions.
61
+
62
+ (An exception: document types will complain if initialized with `Metadata` having the wrong capability.)
63
+
64
+ ### Time attribute requirements
65
+
66
+ The required/forbidden time attributes defined in Appendix A,
67
+ "[Time Attribute Requirements](http://www.openarchives.org/rs/1.0/resourcesync#TimeAttributeReqs)",
68
+ of the ResourceSync specification are not enforced; it's possible to
69
+ create, e.g., a `ResourceList` with a `from_time` on its metadata, or a `ChangeList` with members whose metadata does not declare a `modified_time`, even though both scenarios are forbidden by the specification.
70
+
71
+ ### Value restrictions from XML schemata
72
+
73
+ The [ResourceSync schema](http://www.openarchives.org/rs/0.9.1/resourcesync.xsd) defines restrictions on the values of several attributes:
74
+
75
+ - Path values must start with a slash, must not end with a slash
76
+ - Priorities must be positive and < 1,000,000
77
+ - Link relation types must conform with [RFC 5988](http://tools.ietf.org/html/rfc5988)
78
+
79
+ The [Sitemap](http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd) and [Sitemap index](http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd) schemas also define some restrictions:
80
+
81
+ - URIs have a minimum length of 12 and a max of 2048 characters.
82
+ - Priorities must be in the range 0.0-1.0 (inclusive)
83
+
84
+ None of these restrictions are currently enforced by `resync`.
85
+
86
+ ### Element order
87
+
88
+ When reading a ResourceSync document from XML and writing it back out, `<rs:ln>` elements will always appear before `<rs:md>` elements, regardless of their order in the original source.
89
+
90
+ ### Namespace weirdness
91
+
92
+ The [XML::Mapping](https://github.com/multi-io/xml-mapping) library `resync` uses doesn't support namespaces, so namespace handling in `resync` is a bit hacky. In particular, you may see strange behavior when using `<rs:ln>`, `<rs:md>`, `<url>`, or `<sitemap>` tags outside the context of a `<urlset>`/`<sitemapindex>`.
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ # ------------------------------------------------------------
2
+ # RSpec
3
+
4
+ require 'rspec/core'
5
+ require 'rspec/core/rake_task'
6
+
7
+ namespace :spec do
8
+
9
+ desc 'Run all unit tests'
10
+ RSpec::Core::RakeTask.new(:unit) do |task|
11
+ task.rspec_opts = %w(--color --format documentation --order default)
12
+ task.pattern = 'unit/**/*_spec.rb'
13
+ end
14
+
15
+ desc 'Run all acceptance tests'
16
+ RSpec::Core::RakeTask.new(:acceptance) do |task|
17
+ ENV['COVERAGE'] = nil
18
+ task.rspec_opts = %w(--color --format documentation --order default)
19
+ task.pattern = 'acceptance/**/*_spec.rb'
20
+ end
21
+
22
+ task all: [:unit, :acceptance]
23
+ end
24
+
25
+ desc 'Run all tests'
26
+ task spec: 'spec:all'
27
+
28
+ # ------------------------------------------------------------
29
+ # Coverage
30
+
31
+ desc 'Run all unit tests with coverage'
32
+ task :coverage do
33
+ ENV['COVERAGE'] = 'true'
34
+ Rake::Task['spec:unit'].execute
35
+ end
36
+
37
+ # ------------------------------------------------------------
38
+ # RuboCop
39
+
40
+ require 'rubocop/rake_task'
41
+ RuboCop::RakeTask.new
42
+
43
+ # ------------------------------------------------------------
44
+ # TODOs
45
+
46
+ desc 'List TODOs (from spec/todo.rb)'
47
+ RSpec::Core::RakeTask.new(:todo) do |task|
48
+ task.rspec_opts = %w(--color --format documentation --order default)
49
+ task.pattern = 'todo.rb'
50
+ end
51
+
52
+ # ------------------------------------------------------------
53
+ # Defaults
54
+
55
+ desc 'Run unit tests, check test coverage, run acceptance tests, check code style'
56
+ task default: [:coverage, 'spec:acceptance', :rubocop]
data/example.rb ADDED
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Note: This assumes we're running from the root of the resync project
4
+ $LOAD_PATH << File.dirname(__FILE__)
5
+ require 'lib/resync'
6
+
7
+ # ------------------------------------------------------------
8
+ # Reading a capability list
9
+
10
+ puts "\n------------------------------------------------------------"
11
+ puts "A capability list:\n"
12
+
13
+ capabilitylist_xml = '<?xml version="1.0" encoding="UTF-8"?>
14
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
15
+ xmlns:rs="http://www.openarchives.org/rs/terms/">
16
+ <rs:ln rel="describedby"
17
+ href="http://example.com/info_about_set1_of_resources.xml"/>
18
+ <rs:ln rel="up"
19
+ href="http://example.com/resourcesync_description.xml"/>
20
+ <rs:md capability="capabilitylist"/>
21
+ <url>
22
+ <loc>http://example.com/dataset1/resourcelist.xml</loc>
23
+ <rs:md capability="resourcelist"/>
24
+ </url>
25
+ <url>
26
+ <loc>http://example.com/dataset1/resourcedump.xml</loc>
27
+ <rs:md capability="resourcedump"/>
28
+ </url>
29
+ <url>
30
+ <loc>http://example.com/dataset1/changelist.xml</loc>
31
+ <rs:md capability="changelist"/>
32
+ </url>
33
+ <url>
34
+ <loc>http://example.com/dataset1/changedump.xml</loc>
35
+ <rs:md capability="changedump"/>
36
+ </url>
37
+ </urlset>'
38
+
39
+ capability_list = Resync::XMLParser.parse(capabilitylist_xml)
40
+ puts ' Links:'
41
+ capability_list.links.each do |l|
42
+ puts " #{l.rel}: #{l.uri}"
43
+ end
44
+ puts ' Resources:'
45
+ capability_list.resources.each do |r|
46
+ puts " #{r.uri} (#{r.capability})"
47
+ end
48
+
49
+ # ------------------------------------------------------------
50
+ # Creating a changelist
51
+
52
+ puts "\n------------------------------------------------------------"
53
+ puts "A change list:\n\n"
54
+
55
+ change_list = Resync::ChangeList.new(
56
+ links: [
57
+ Resync::Link.new(
58
+ rel: 'up',
59
+ uri: 'http://example.com/dataset1/capabilitylist.xml'
60
+ )
61
+ ],
62
+ metadata: Resync::Metadata.new(
63
+ capability: 'changelist',
64
+ from_time: Time.utc(2013, 1, 3)
65
+ ),
66
+ resources: [
67
+ Resync::Resource.new(
68
+ uri: 'http://example.com/res4',
69
+ modified_time: Time.utc(2013, 1, 3, 17),
70
+ metadata: Resync::Metadata.new(
71
+ change: Resync::Types::Change::UPDATED,
72
+ hashes: { 'sha-256' => 'f4OxZX_x_DFGFDgghgdfb6rtSx-iosjf6735432nklj' },
73
+ length: 56_778,
74
+ mime_type: 'application/json'
75
+ ),
76
+ links: [
77
+ Resync::Link.new(
78
+ rel: 'http://www.openarchives.org/rs/terms/patch',
79
+ uri: 'http://example.com/res4-json-patch',
80
+ modified_time: Time.utc(2013, 1, 3, 17),
81
+ hashes: { 'sha-256' => 'f4OxZX_x_DFGFDgghgdfb6rtSx-iosjf6735432nklj' },
82
+ length: 73,
83
+ mime_type: 'application/json-patch'
84
+ )
85
+ ]
86
+ ),
87
+ Resync::Resource.new(
88
+ uri: 'http://example.com/res5-full.tiff',
89
+ modified_time: Time.utc(2013, 1, 3, 18),
90
+ metadata: Resync::Metadata.new(
91
+ change: Resync::Types::Change::DELETED
92
+ )
93
+ )
94
+ ]
95
+ )
96
+
97
+ xml = change_list.save_to_xml
98
+ formatter = REXML::Formatters::Pretty.new
99
+ formatter.write(xml, $stdout)
100
+ puts
@@ -0,0 +1,85 @@
1
+ require_relative 'shared/base_resource_list'
2
+ require_relative 'xml'
3
+
4
+ module Resync
5
+ # A capability list. See section 9,
6
+ # "{http://www.openarchives.org/rs/1.0/resourcesync#CapabilityList Advertising Capabilities}",
7
+ # in the ResourceSync specification.
8
+ class CapabilityList < BaseResourceList
9
+ include ::XML::Mapping
10
+
11
+ # The capability provided by this type.
12
+ CAPABILITY = 'capabilitylist'
13
+
14
+ # ------------------------------------------------------------
15
+ # Initializer
16
+
17
+ # Creates a new +BaseResourceList+.
18
+ #
19
+ # @param resources [Array<Resource>] The +<url>+ or +<sitemap>+ elements contained in this list.
20
+ # All resources must have a capability, and there can be no more than one resource for each
21
+ # specified capability.
22
+ # @param links [Array<Link>] Related links (+<rs:ln>+).
23
+ # @param metadata [Metadata] Metadata about this list. The +capability+ of the metadata must
24
+ # be +'capabilitylist'+.
25
+ # @raise [ArgumentError] if a provided resource does not have a +capability+ attribute.
26
+ # @raise [ArgumentError] if more than one provided resource has the same +capability+ attribute.
27
+ # @raise [ArgumentError] if the specified metadata does not have the correct +capability+ attribute.
28
+ def initialize(resources: [], links: [], metadata: nil)
29
+ @source_descripton = source_description_from(links)
30
+ super(resources: resources, links: links, metadata: metadata)
31
+ end
32
+
33
+ # ------------------------------------------------------------
34
+ # Custom accessors
35
+
36
+ # Sets the +resources+ list. +nil+ is treated as an empty list.
37
+ # @raise [ArgumentError] if a provided resource does not have a +capability+ attribute.
38
+ # @raise [ArgumentError] if more than one provided resource has the same +capability+ attribute.
39
+ def resources=(value)
40
+ resources = value || []
41
+ @capabilities = to_capability_map(resources)
42
+ @resources = @capabilities.values
43
+ end
44
+
45
+ # Gets the resource for the specified capability.
46
+ #
47
+ # @param capability [String] The capability.
48
+ # @return [Resource] the resource providing the capability, or +nil+ if
49
+ # there is no resource with that capability in this list.
50
+ def resource_for(capability:)
51
+ @capabilities[capability]
52
+ end
53
+
54
+ # Gets the URI of the description of the source whose capabilities are identified by this list.
55
+ #
56
+ # @return [URI] the URI of the description of the source whose capabilities are identified
57
+ # by this list. See section 8,
58
+ # "{http://www.openarchives.org/rs/1.0/resourcesync#SourceDesc Describing the Source}",
59
+ # in the ResourceSync specification.
60
+ def source_description
61
+ @source_description ||= source_description_from(links)
62
+ end
63
+
64
+ # ------------------------------
65
+ # Conversions
66
+
67
+ def to_capability_map(resources)
68
+ capabilities = {}
69
+ (resources || []).each do |resource|
70
+ capability = resource.capability
71
+ fail ArgumentError, "No capability found for resource with URI #{resource.uri}" unless capability
72
+ fail ArgumentError, "Duplicate resource for capability #{capability}" if capabilities.key?(capability)
73
+ capabilities[capability] = resource
74
+ end
75
+ capabilities
76
+ end
77
+
78
+ def source_description_from(links)
79
+ return nil unless links
80
+ desc = links.map { |link| link.uri if link.rel == 'up' }.compact.first
81
+ fail ArgumentError, "No source descrption (<link rel='up'/>) provided" unless desc
82
+ desc
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'shared/sorted_resource_list'
2
+ require_relative 'xml'
3
+
4
+ module Resync
5
+ # A change dump. See section 13.1,
6
+ # "{http://www.openarchives.org/rs/1.0/resourcesync#ChangeDump Change Dump}",
7
+ # in the ResourceSync specification.
8
+ class ChangeDump < SortedResourceList
9
+ include ::XML::Mapping
10
+
11
+ # The capability provided by this type.
12
+ CAPABILITY = 'changedump'
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'shared/sorted_resource_list'
2
+ require_relative 'xml'
3
+
4
+ module Resync
5
+ # A change dump manifest. See section 13.2,
6
+ # "{http://www.openarchives.org/rs/1.0/resourcesync#ChangeDumpManifest Change Dump Manifest}",
7
+ # in the ResourceSync specification.
8
+ class ChangeDumpManifest < SortedResourceList
9
+ include ::XML::Mapping
10
+
11
+ # The capability provided by this type.
12
+ CAPABILITY = 'changedump-manifest'
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'shared/sorted_resource_list'
2
+ require_relative 'xml'
3
+
4
+ module Resync
5
+ # A change list. See section 12.1,
6
+ # "{http://www.openarchives.org/rs/1.0/resourcesync#ChangeList Change List}",
7
+ # in the ResourceSync specification.
8
+ class ChangeList < SortedResourceList
9
+ include ::XML::Mapping
10
+
11
+ # The capability provided by this type.
12
+ CAPABILITY = 'changelist'
13
+
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'shared/sorted_resource_list'
2
+ require_relative 'shared/sitemap_index'
3
+
4
+ module Resync
5
+ # A change list index. See section 12.2,
6
+ # "{http://www.openarchives.org/rs/1.0/resourcesync#ChangeListIndex Change List Index}",
7
+ # in the ResourceSync specification.
8
+ class ChangeListIndex < SortedResourceList
9
+ include ::XML::Mapping
10
+ include SitemapIndex
11
+
12
+ # The capability provided by this type.
13
+ CAPABILITY = 'changelist'
14
+
15
+ # use_mapping :sitemapindex
16
+ # root_element_name 'sitemapindex'
17
+ # array_node :resources, 'sitemap', class: Resource, default_value: [], sub_mapping: :_default
18
+ #
19
+ # # Ensures that an index is always written as a +<sitemapindex>+.
20
+ # # Overrides +::XML::Mapping.save_to_xml+.
21
+ # def save_to_xml(options = { mapping: :_default })
22
+ # options = options.merge(mapping: :sitemapindex)
23
+ # super(options)
24
+ # end
25
+ end
26
+ end
@@ -0,0 +1,87 @@
1
+ require_relative 'shared/descriptor'
2
+ require_relative 'xml'
3
+
4
+ module Resync
5
+
6
+ # A link to a related resource. See "Linking to Related Resources"
7
+ # under section 5.1,
8
+ # {http://www.openarchives.org/rs/1.0/resourcesync#SourcePers Synchronization Processes: Source Perspective}
9
+ # in the ResourceSync specification.
10
+ #
11
+ # @!attribute [rw] rel
12
+ # @return [String] the relationship of the linked resource to the original
13
+ # resource. See {http://tools.ietf.org/html/rfc5988 RFC 5988}, "Web Linking".
14
+ # for information on link relation types.
15
+ # @!attribute [rw] uri
16
+ # @return [URI] the URI of the linked resource.
17
+ # @!attribute [rw] priority
18
+ # @return [Integer] the priority of the linked resource among links with the
19
+ # same relation type. Values should be in the range 1-999,999 (inclusive).
20
+ # Lower values indicate higher priorities.
21
+ class Link < Descriptor
22
+ include ::XML::Mapping
23
+
24
+ # ------------------------------------------------------------
25
+ # Attributes
26
+
27
+ root_element_name 'ln'
28
+
29
+ text_node :rel, '@rel', default_value: nil
30
+ uri_node :uri, '@href', default_value: nil
31
+ numeric_node :priority, '@pri', default_value: nil
32
+
33
+ # ------------------------------------------------------------
34
+ # Initializer
35
+
36
+ # @param rel [String] the relationship of the linked resource to the
37
+ # original resource. See {http://tools.ietf.org/html/rfc5988 RFC 5988},
38
+ # "Web Linking". for information on link relation types.
39
+ # @param uri [URI] the URI of the linked resource.
40
+ # @param priority [Integer] the priority of the linked resource among links
41
+ # with the same relation type. Values should be in the range
42
+ # 1-999,999 (inclusive). Lower values indicate higher priorities.
43
+ # @param modified_time [Time] The date and time when the referenced resource was last modified.
44
+ # @param length [Integer] The content length of the referenced resource.
45
+ # @param mime_type [MIME::Type] The media type of the referenced resource.
46
+ # @param encoding [String] Any content encoding (if any) applied to the data in the
47
+ # referenced resource (e.g. for compression)
48
+ # @param hashes [Hash<String, String>] Fixity information for the referenced
49
+ # resource, as a map from hash algorithm tokens (e.g. +md5+, +sha-256+)
50
+ # to hex-encoded digest values.
51
+ # @param path [String] For +ResourceDumpManifests+ and +ChangeDumpManifests+,
52
+ # the path to the referenced resource within the dump ZIP file.
53
+ # @raise [URI::InvalidURIError] if +uri+ cannot be converted to a URI.
54
+ def initialize( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
55
+ rel:,
56
+ uri:,
57
+
58
+ priority: nil,
59
+
60
+ modified_time: nil,
61
+
62
+ length: nil,
63
+ mime_type: nil,
64
+ encoding: nil,
65
+ hashes: {},
66
+
67
+ path: nil
68
+ )
69
+ super(modified_time: modified_time, length: length, mime_type: mime_type, encoding: encoding, hashes: hashes, path: path)
70
+
71
+ self.rel = rel
72
+ self.uri = uri
73
+ self.priority = priority
74
+ end
75
+
76
+ # ------------------------------------------------------------
77
+ # Custom setters
78
+
79
+ # Sets the URI of the linked resource. Strings will be converted to +URI+ objects.
80
+ # @param value [URI, String] the URI.
81
+ # @raise [URI::InvalidURIError] if +value+ cannot be converted to a URI.
82
+ def uri=(value)
83
+ @uri = XML.to_uri(value)
84
+ end
85
+
86
+ end
87
+ end