resync-client 0.1.2 → 0.2.1
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 +4 -4
- data/.gitignore +2 -0
- data/CHANGES.md +10 -0
- data/lib/resync/client.rb +63 -0
- data/lib/resync/client/http_helper.rb +90 -90
- data/lib/resync/client/mixins.rb +1 -0
- data/lib/resync/client/mixins/bitstream_resource.rb +26 -0
- data/lib/resync/client/mixins/bitstream_resource_list.rb +29 -0
- data/lib/resync/client/mixins/client_delegator.rb +38 -0
- data/lib/resync/client/mixins/downloadable.rb +36 -0
- data/lib/resync/client/mixins/link_client_delegate.rb +19 -0
- data/lib/resync/client/mixins/resource_client_delegate.rb +19 -0
- data/lib/resync/client/mixins/zipped_resource.rb +20 -0
- data/lib/resync/client/mixins/zipped_resource_list.rb +26 -0
- data/lib/resync/client/version.rb +1 -1
- data/lib/resync/client/zip.rb +1 -0
- data/lib/resync/client/zip/bitstream.rb +85 -0
- data/lib/resync/client/zip/zip_package.rb +78 -0
- data/lib/resync/client/zip/zip_packages.rb +59 -0
- data/lib/resync/extensions.rb +36 -0
- data/resync-client.gemspec +1 -1
- data/spec/acceptance/example_spec.rb +46 -0
- data/spec/data/resourcedump/changedump.xml +16 -0
- data/spec/data/simulator/capability-list.xml +2 -0
- data/spec/data/simulator/change-list.xml +2 -0
- data/spec/data/simulator/source-description.xml +2 -0
- data/spec/data/simulator/update.txt +1 -0
- data/spec/unit/resync/client/bitstream_spec.rb +84 -80
- data/spec/unit/resync/client/client_spec.rb +64 -3
- data/spec/unit/resync/client/{resync_extensions_spec.rb → extensions_spec.rb} +1 -6
- data/spec/unit/resync/client/http_helper_spec.rb +187 -185
- data/spec/unit/resync/client/zip_package_spec.rb +47 -32
- data/spec/unit/resync/client/zip_packages_spec.rb +42 -38
- data/spec/unit/resync/client/zipped_resource_list_spec.rb +61 -0
- metadata +35 -16
- data/lib/resync/client/bitstream.rb +0 -79
- data/lib/resync/client/client.rb +0 -58
- data/lib/resync/client/downloadable.rb +0 -35
- data/lib/resync/client/dump.rb +0 -26
- data/lib/resync/client/resync_extensions.rb +0 -85
- data/lib/resync/client/zip_package.rb +0 -66
- data/lib/resync/client/zip_packages.rb +0 -51
- data/spec/unit/resync/client/dump_spec.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53bce63e13094ab4ffeec5ff5b3d4365c96eaf9f
|
4
|
+
data.tar.gz: 72becf942fb027a0d02d948e3a98fb28d8a626fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97e28c200dbd29c6896c9c4d3e351f89ba0f3836f8cdf0ccc5e87517cb97fefe869e313311f87f635007a06e3373b9c499668b277d089b5b68c227fe4126559d
|
7
|
+
data.tar.gz: c5b24e9fbb0db69734b5e805077b9da163b86ac84a1c4bf296868979812a49e1efa8b863ca1a7a6a386b148ed8c82f0a68c584f51281437851ff8e4a9af11bf8
|
data/.gitignore
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# 0.2.1
|
2
|
+
|
3
|
+
- Update to depend on [resync](https://github.com/dmolesUC3/resync) 0.1.3
|
4
|
+
- Add more tests for client delegation
|
5
|
+
|
6
|
+
# 0.2.0
|
7
|
+
|
8
|
+
- Use named mixins instead of instance monkey-patching for easier documentation and navigation.
|
9
|
+
- Update to depend on [resync](https://github.com/dmolesUC3/resync) 0.1.2
|
10
|
+
|
1
11
|
# 0.1.2
|
2
12
|
|
3
13
|
- Change the `:zip_packages` extension method on `ResourceDump` and `ChangeDump` to return a lazy enumerable instead of preemptively downloading all packages.
|
data/lib/resync/client.rb
CHANGED
@@ -1,3 +1,66 @@
|
|
1
1
|
require 'resync'
|
2
|
+
require_relative 'extensions'
|
2
3
|
|
3
4
|
Dir.glob(File.expand_path('../client/*.rb', __FILE__), &method(:require))
|
5
|
+
|
6
|
+
module Resync
|
7
|
+
|
8
|
+
# Utility class for retrieving HTTP content and parsing it as ResourceSync documents.
|
9
|
+
class Client
|
10
|
+
|
11
|
+
# ------------------------------------------------------------
|
12
|
+
# Initializer
|
13
|
+
|
14
|
+
# Creates a new +Client+
|
15
|
+
# @param helper [HTTPHelper] the HTTP helper. Defaults to a new HTTP helper with
|
16
|
+
# +resync-client VERSION+ as the User-Agent string.
|
17
|
+
def initialize(helper: HTTPHelper.new(user_agent: "resync-client #{VERSION}"))
|
18
|
+
@helper = helper
|
19
|
+
end
|
20
|
+
|
21
|
+
# ------------------------------------------------------------
|
22
|
+
# Public methods
|
23
|
+
|
24
|
+
# Gets the content of the specified URI and parses it as a ResourceSync document.
|
25
|
+
def get_and_parse(uri)
|
26
|
+
uri = Resync::XML.to_uri(uri)
|
27
|
+
raw_contents = get(uri)
|
28
|
+
doc = XMLParser.parse(raw_contents)
|
29
|
+
doc.client_delegate = self
|
30
|
+
doc
|
31
|
+
end
|
32
|
+
|
33
|
+
# Gets the content of the specified URI as a string.
|
34
|
+
# @param uri [URI, String] the URI to download
|
35
|
+
# @return [String] the content of the URI
|
36
|
+
def get(uri)
|
37
|
+
uri = Resync::XML.to_uri(uri)
|
38
|
+
@helper.fetch(uri: uri)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gets the content of the specified URI and saves it to a temporary file.
|
42
|
+
# @param uri [URI, String] the URI to download
|
43
|
+
# @return [String] the path to the downloaded file
|
44
|
+
def download_to_temp_file(uri)
|
45
|
+
uri = Resync::XML.to_uri(uri)
|
46
|
+
@helper.fetch_to_file(uri: uri)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Gets the content of the specified URI and saves it to the specified file,
|
50
|
+
# overwriting it if it exists.
|
51
|
+
# @param uri [URI, String] the URI to download
|
52
|
+
# @param path [String] the path to save the download to
|
53
|
+
# @return [String] the path to the downloaded file
|
54
|
+
def download_to_file(uri:, path:)
|
55
|
+
uri = Resync::XML.to_uri(uri)
|
56
|
+
@helper.fetch_to_file(path: path, uri: uri)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Allows a {Client} to act as a {Mixins::ClientDelegator} delegate.
|
60
|
+
# @return [Client] this client
|
61
|
+
def client
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -4,108 +4,108 @@ require 'uri'
|
|
4
4
|
require 'mime-types'
|
5
5
|
|
6
6
|
module Resync
|
7
|
+
class Client
|
8
|
+
# Utility class simplifying GET requests for HTTP/HTTPS resources.
|
9
|
+
class HTTPHelper
|
10
|
+
|
11
|
+
# ------------------------------------------------------------
|
12
|
+
# Constants
|
13
|
+
|
14
|
+
# The default number of redirects to follow before erroring out.
|
15
|
+
DEFAULT_MAX_REDIRECTS = 5
|
16
|
+
|
17
|
+
# ------------------------------------------------------------
|
18
|
+
# Accessors
|
19
|
+
|
20
|
+
# @!attribute [rw] user_agent
|
21
|
+
# @return [String] the User-Agent string to send when making requests
|
22
|
+
attr_accessor :user_agent
|
23
|
+
|
24
|
+
# @!attribute [rw] redirect_limit
|
25
|
+
# @return [Integer] the number of redirects to follow before erroring out
|
26
|
+
attr_accessor :redirect_limit
|
27
|
+
|
28
|
+
# ------------------------------------------------------------
|
29
|
+
# Initializer
|
30
|
+
|
31
|
+
# Creates a new +HTTPHelper+
|
32
|
+
#
|
33
|
+
# @param user_agent [String] the User-Agent string to send when making requests
|
34
|
+
# @param redirect_limit [Integer] the number of redirects to follow before erroring out
|
35
|
+
# (defaults to {DEFAULT_MAX_REDIRECTS})
|
36
|
+
def initialize(user_agent:, redirect_limit: DEFAULT_MAX_REDIRECTS)
|
37
|
+
@user_agent = user_agent
|
38
|
+
@redirect_limit = redirect_limit
|
39
|
+
end
|
7
40
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# @!attribute [rw] user_agent
|
22
|
-
# @return [String] the User-Agent string to send when making requests
|
23
|
-
attr_accessor :user_agent
|
24
|
-
|
25
|
-
# @!attribute [rw] redirect_limit
|
26
|
-
# @return [Integer] the number of redirects to follow before erroring out
|
27
|
-
attr_accessor :redirect_limit
|
28
|
-
|
29
|
-
# ------------------------------------------------------------
|
30
|
-
# Initializer
|
31
|
-
|
32
|
-
# Creates a new +HTTPHelper+
|
33
|
-
#
|
34
|
-
# @param user_agent [String] the User-Agent string to send when making requests
|
35
|
-
# @param redirect_limit [Integer] the number of redirects to follow before erroring out
|
36
|
-
# (defaults to {DEFAULT_MAX_REDIRECTS})
|
37
|
-
def initialize(user_agent:, redirect_limit: DEFAULT_MAX_REDIRECTS)
|
38
|
-
@user_agent = user_agent
|
39
|
-
@redirect_limit = redirect_limit
|
40
|
-
end
|
41
|
-
|
42
|
-
# ------------------------------------------------------------
|
43
|
-
# Public methods
|
44
|
-
|
45
|
-
# Gets the content of the specified URI as a string.
|
46
|
-
# @param uri [URI] the URI to download
|
47
|
-
# @param limit [Integer] the number of redirects to follow (defaults to {#redirect_limit})
|
48
|
-
# @return [String] the content of the URI
|
49
|
-
def fetch(uri:, limit: redirect_limit)
|
50
|
-
make_request(uri, limit) do |success|
|
51
|
-
# not 100% clear why we need an explicit return here; it
|
52
|
-
# doesn't show up in unit tests but it does in example.rb
|
53
|
-
return success.body
|
41
|
+
# ------------------------------------------------------------
|
42
|
+
# Public methods
|
43
|
+
|
44
|
+
# Gets the content of the specified URI as a string.
|
45
|
+
# @param uri [URI] the URI to download
|
46
|
+
# @param limit [Integer] the number of redirects to follow (defaults to {#redirect_limit})
|
47
|
+
# @return [String] the content of the URI
|
48
|
+
def fetch(uri:, limit: redirect_limit)
|
49
|
+
make_request(uri, limit) do |success|
|
50
|
+
# not 100% clear why we need an explicit return here; it
|
51
|
+
# doesn't show up in unit tests but it does in example.rb
|
52
|
+
return success.body
|
53
|
+
end
|
54
54
|
end
|
55
|
-
end
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
56
|
+
# Gets the content of the specified URI and saves it to a file. If no
|
57
|
+
# file path is provided, saves it to a temporary file.
|
58
|
+
# @param uri [URI] the URI to download
|
59
|
+
# @param path [String] the path to save the download to (optional)
|
60
|
+
# @return [String] the path to the downloaded file
|
61
|
+
def fetch_to_file(uri:, path: nil, limit: redirect_limit)
|
62
|
+
make_request(uri, limit) do |success|
|
63
|
+
file = path ? File.new(path, 'w+') : Tempfile.new(['resync-client', ".#{extension_for(success)}"])
|
64
|
+
open file, 'w' do |out|
|
65
|
+
success.read_body { |chunk| out.write(chunk) }
|
66
|
+
end
|
67
|
+
return file.path
|
67
68
|
end
|
68
|
-
return file.path
|
69
69
|
end
|
70
|
-
end
|
71
70
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
71
|
+
# ------------------------------------------------------------
|
72
|
+
# Private methods
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def make_request(uri, limit, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
77
|
+
fail "Redirect limit (#{redirect_limit}) exceeded retrieving URI #{uri}" if limit <= 0
|
78
|
+
req = Net::HTTP::Get.new(uri, 'User-Agent' => user_agent)
|
79
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: (uri.scheme == 'https')) do |http|
|
80
|
+
http.request(req) do |response|
|
81
|
+
case response
|
82
|
+
when Net::HTTPSuccess
|
83
|
+
block.call(response)
|
84
|
+
when Net::HTTPInformation, Net::HTTPRedirection
|
85
|
+
make_request(redirect_uri_for(response, uri), limit - 1, &block)
|
86
|
+
else
|
87
|
+
fail "Error #{response.code}: #{response.message} retrieving URI #{uri}"
|
88
|
+
end
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
92
|
-
end
|
93
92
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
93
|
+
def extension_for(response)
|
94
|
+
content_type = response['Content-Type']
|
95
|
+
mime_type = MIME::Types[content_type].first || MIME::Types['application/octet-stream'].first
|
96
|
+
mime_type.preferred_extension || 'bin'
|
97
|
+
end
|
99
98
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
99
|
+
def redirect_uri_for(response, original_uri)
|
100
|
+
if response.is_a?(Net::HTTPInformation)
|
101
|
+
original_uri
|
102
|
+
else
|
103
|
+
location = response['location']
|
104
|
+
new_uri = URI(location)
|
105
|
+
new_uri.relative? ? original_uri + location : new_uri
|
106
|
+
end
|
107
107
|
end
|
108
|
-
end
|
109
108
|
|
109
|
+
end
|
110
110
|
end
|
111
111
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir.glob(File.expand_path('../mixins/*.rb', __FILE__), &method(:require))
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../zip'
|
2
|
+
|
3
|
+
# A resource that refers to a bitsream within a zipped bitstream package.
|
4
|
+
#
|
5
|
+
# @!attribute [rw] zip_package_delegate
|
6
|
+
# @return [ZipPackage] the provider of the containing package,
|
7
|
+
# e.g. its manifest
|
8
|
+
module Resync
|
9
|
+
class Client
|
10
|
+
module Mixins
|
11
|
+
module BitstreamResource
|
12
|
+
attr_accessor :zip_package_delegate
|
13
|
+
|
14
|
+
# @return [ZipPackage] the package containing the bitstream for this resource
|
15
|
+
def containing_package
|
16
|
+
@zip_package_delegate.zip_package
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Bitstream] the bitstream for this resource
|
20
|
+
def bitstream
|
21
|
+
containing_package.bitstream_for(self)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative '../zip'
|
2
|
+
require_relative 'bitstream_resource'
|
3
|
+
|
4
|
+
# A list of resources within a single zipped bitstream package, e.g. as provided
|
5
|
+
# by the package manifest.
|
6
|
+
#
|
7
|
+
# @!attribute [rw] zip_package
|
8
|
+
# @return [ZipPackage] the package.
|
9
|
+
module Resync
|
10
|
+
class Client
|
11
|
+
module Mixins
|
12
|
+
module BitstreamResourceList
|
13
|
+
attr_accessor :zip_package
|
14
|
+
|
15
|
+
# Makes each provided resource a {BitstreamResource}
|
16
|
+
# @param value [Array<Resource>] the resources for this list
|
17
|
+
def resources=(value)
|
18
|
+
super
|
19
|
+
resources.each do |r|
|
20
|
+
class << r
|
21
|
+
prepend BitstreamResource
|
22
|
+
end
|
23
|
+
r.zip_package_delegate = self
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative '../../client'
|
2
|
+
|
3
|
+
# An object that delegates to another to provide a {Client} for downloading
|
4
|
+
# resources and links.
|
5
|
+
#
|
6
|
+
# @!attribute [rw] client_delegate
|
7
|
+
# @return [#client] The client provider.
|
8
|
+
module Resync
|
9
|
+
class Client
|
10
|
+
module Mixins
|
11
|
+
module ClientDelegator
|
12
|
+
attr_accessor :client_delegate
|
13
|
+
|
14
|
+
def client
|
15
|
+
client_delegate.client
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates a one-off delegate wrapper around the specified {Client}
|
19
|
+
# @param value [Client] the client
|
20
|
+
def client=(value)
|
21
|
+
@client_delegate = ClientDelegate.new(value)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Minimal 'delegate' wrapper around a specified {Client}
|
25
|
+
class ClientDelegate
|
26
|
+
# @return [#client] the client
|
27
|
+
attr_reader :client
|
28
|
+
|
29
|
+
# Creates a new {ClientDelegate} wrapping the specified {Client}
|
30
|
+
# @param client The client to delegate to
|
31
|
+
def initialize(client)
|
32
|
+
@client = client
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative 'client_delegator'
|
2
|
+
|
3
|
+
# A downloadable resource or link.
|
4
|
+
module Resync
|
5
|
+
class Client
|
6
|
+
module Mixins
|
7
|
+
module Downloadable
|
8
|
+
prepend ClientDelegator
|
9
|
+
|
10
|
+
# Delegates to {Client#get_and_parse} to get the contents of
|
11
|
+
# +:uri+ as a ResourceSync document
|
12
|
+
def get_and_parse # rubocop:disable Style/AccessorMethodName
|
13
|
+
client.get_and_parse(uri)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Delegates to {Client#get} to get the contents of this +:uri+
|
17
|
+
def get # rubocop:disable Style/AccessorMethodName
|
18
|
+
client.get(uri)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Delegates to {Client#download_to_temp_file} to download the
|
22
|
+
# contents of +:uri+ to a file.
|
23
|
+
def download_to_temp_file # rubocop:disable Style/AccessorMethodName
|
24
|
+
client.download_to_temp_file(uri)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Delegates to {Client#download_to_file} to download the
|
28
|
+
# contents of +:uri+ to the specified path.
|
29
|
+
# @param path [String] the path to download to
|
30
|
+
def download_to_file(path)
|
31
|
+
client.download_to_file(uri: uri, path: path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'client_delegator'
|
2
|
+
|
3
|
+
# A link container that is capable of providing those resources with a {Client}
|
4
|
+
module Resync
|
5
|
+
class Client
|
6
|
+
module Mixins
|
7
|
+
module LinkClientDelegate
|
8
|
+
prepend ClientDelegator
|
9
|
+
|
10
|
+
# Sets this object as the client provider delegate for each link.
|
11
|
+
# @param value [Array<Link>] the links for this list
|
12
|
+
def links=(value)
|
13
|
+
super
|
14
|
+
links.each { |l| l.client_delegate = self }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|