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.
- checksums.yaml +7 -0
- data/.gitignore +42 -0
- data/.rubocop.yml +23 -0
- data/.ruby-version +1 -0
- data/.travis.yml +2 -0
- data/Gemfile +3 -0
- data/LICENSE.md +22 -0
- data/README.md +92 -0
- data/Rakefile +56 -0
- data/example.rb +100 -0
- data/lib/resync/capability_list.rb +85 -0
- data/lib/resync/change_dump.rb +15 -0
- data/lib/resync/change_dump_manifest.rb +15 -0
- data/lib/resync/change_list.rb +15 -0
- data/lib/resync/change_list_index.rb +26 -0
- data/lib/resync/link.rb +87 -0
- data/lib/resync/metadata.rb +112 -0
- data/lib/resync/resource.rb +72 -0
- data/lib/resync/resource_dump.rb +15 -0
- data/lib/resync/resource_dump_manifest.rb +15 -0
- data/lib/resync/resource_list.rb +15 -0
- data/lib/resync/resource_list_index.rb +15 -0
- data/lib/resync/shared/augmented.rb +76 -0
- data/lib/resync/shared/base_resource_list.rb +117 -0
- data/lib/resync/shared/descriptor.rb +135 -0
- data/lib/resync/shared/sitemap_index.rb +32 -0
- data/lib/resync/shared/sorted_resource_list.rb +60 -0
- data/lib/resync/source_description.rb +14 -0
- data/lib/resync/types/change.rb +14 -0
- data/lib/resync/types/change_frequency.rb +18 -0
- data/lib/resync/types.rb +6 -0
- data/lib/resync/version.rb +4 -0
- data/lib/resync/xml.rb +216 -0
- data/lib/resync/xml_parser.rb +65 -0
- data/lib/resync.rb +4 -0
- data/resync.gemspec +36 -0
- data/spec/acceptance/xml_parser_spec.rb +1049 -0
- data/spec/data/examples/README.md +1 -0
- data/spec/data/examples/example-1.xml +12 -0
- data/spec/data/examples/example-12.xml +25 -0
- data/spec/data/examples/example-13.xml +25 -0
- data/spec/data/examples/example-14.xml +23 -0
- data/spec/data/examples/example-15.xml +21 -0
- data/spec/data/examples/example-16.xml +24 -0
- data/spec/data/examples/example-17.xml +39 -0
- data/spec/data/examples/example-18.xml +25 -0
- data/spec/data/examples/example-19.xml +28 -0
- data/spec/data/examples/example-2.xml +18 -0
- data/spec/data/examples/example-20.xml +22 -0
- data/spec/data/examples/example-21.xml +31 -0
- data/spec/data/examples/example-22.xml +41 -0
- data/spec/data/examples/example-23.xml +41 -0
- data/spec/data/examples/example-24.xml +28 -0
- data/spec/data/examples/example-25.xml +21 -0
- data/spec/data/examples/example-26.xml +18 -0
- data/spec/data/examples/example-27.xml +36 -0
- data/spec/data/examples/example-28.xml +34 -0
- data/spec/data/examples/example-29.xml +27 -0
- data/spec/data/examples/example-3.xml +17 -0
- data/spec/data/examples/example-30.xml +18 -0
- data/spec/data/examples/example-31.xml +16 -0
- data/spec/data/examples/example-32.xml +22 -0
- data/spec/data/examples/example-33.xml +22 -0
- data/spec/data/examples/example-4.xml +10 -0
- data/spec/data/examples/example-5.xml +18 -0
- data/spec/data/examples/example-6.xml +21 -0
- data/spec/data/examples/example-7.xml +13 -0
- data/spec/data/examples/example-8.xml +12 -0
- data/spec/data/resourcesync.xsd +148 -0
- data/spec/data/siteindex.xsd +75 -0
- data/spec/data/sitemap.xsd +116 -0
- data/spec/rspec_custom_matchers.rb +89 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/todo.rb +11 -0
- data/spec/unit/resync/capability_list_spec.rb +138 -0
- data/spec/unit/resync/change_dump_manifest_spec.rb +75 -0
- data/spec/unit/resync/change_dump_spec.rb +61 -0
- data/spec/unit/resync/change_list_index_spec.rb +49 -0
- data/spec/unit/resync/change_list_spec.rb +75 -0
- data/spec/unit/resync/link_spec.rb +93 -0
- data/spec/unit/resync/metadata_spec.rb +169 -0
- data/spec/unit/resync/resource_dump_manifest_spec.rb +59 -0
- data/spec/unit/resync/resource_dump_spec.rb +62 -0
- data/spec/unit/resync/resource_list_index_spec.rb +53 -0
- data/spec/unit/resync/resource_list_spec.rb +60 -0
- data/spec/unit/resync/resource_spec.rb +176 -0
- data/spec/unit/resync/shared/augmented_examples.rb +58 -0
- data/spec/unit/resync/shared/base_resource_list_examples.rb +103 -0
- data/spec/unit/resync/shared/descriptor_examples.rb +122 -0
- data/spec/unit/resync/shared/descriptor_spec.rb +33 -0
- data/spec/unit/resync/shared/sorted_list_examples.rb +134 -0
- data/spec/unit/resync/shared/uri_field_examples.rb +36 -0
- data/spec/unit/resync/source_description_spec.rb +55 -0
- data/spec/unit/resync/xml/timenode_spec.rb +48 -0
- data/spec/unit/resync/xml/xml_spec.rb +40 -0
- data/spec/unit/resync/xml_parser_spec.rb +82 -0
- 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
data/Gemfile
ADDED
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
|
data/lib/resync/link.rb
ADDED
@@ -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
|