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