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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CHANGES.md +10 -0
  4. data/lib/resync/client.rb +63 -0
  5. data/lib/resync/client/http_helper.rb +90 -90
  6. data/lib/resync/client/mixins.rb +1 -0
  7. data/lib/resync/client/mixins/bitstream_resource.rb +26 -0
  8. data/lib/resync/client/mixins/bitstream_resource_list.rb +29 -0
  9. data/lib/resync/client/mixins/client_delegator.rb +38 -0
  10. data/lib/resync/client/mixins/downloadable.rb +36 -0
  11. data/lib/resync/client/mixins/link_client_delegate.rb +19 -0
  12. data/lib/resync/client/mixins/resource_client_delegate.rb +19 -0
  13. data/lib/resync/client/mixins/zipped_resource.rb +20 -0
  14. data/lib/resync/client/mixins/zipped_resource_list.rb +26 -0
  15. data/lib/resync/client/version.rb +1 -1
  16. data/lib/resync/client/zip.rb +1 -0
  17. data/lib/resync/client/zip/bitstream.rb +85 -0
  18. data/lib/resync/client/zip/zip_package.rb +78 -0
  19. data/lib/resync/client/zip/zip_packages.rb +59 -0
  20. data/lib/resync/extensions.rb +36 -0
  21. data/resync-client.gemspec +1 -1
  22. data/spec/acceptance/example_spec.rb +46 -0
  23. data/spec/data/resourcedump/changedump.xml +16 -0
  24. data/spec/data/simulator/capability-list.xml +2 -0
  25. data/spec/data/simulator/change-list.xml +2 -0
  26. data/spec/data/simulator/source-description.xml +2 -0
  27. data/spec/data/simulator/update.txt +1 -0
  28. data/spec/unit/resync/client/bitstream_spec.rb +84 -80
  29. data/spec/unit/resync/client/client_spec.rb +64 -3
  30. data/spec/unit/resync/client/{resync_extensions_spec.rb → extensions_spec.rb} +1 -6
  31. data/spec/unit/resync/client/http_helper_spec.rb +187 -185
  32. data/spec/unit/resync/client/zip_package_spec.rb +47 -32
  33. data/spec/unit/resync/client/zip_packages_spec.rb +42 -38
  34. data/spec/unit/resync/client/zipped_resource_list_spec.rb +61 -0
  35. metadata +35 -16
  36. data/lib/resync/client/bitstream.rb +0 -79
  37. data/lib/resync/client/client.rb +0 -58
  38. data/lib/resync/client/downloadable.rb +0 -35
  39. data/lib/resync/client/dump.rb +0 -26
  40. data/lib/resync/client/resync_extensions.rb +0 -85
  41. data/lib/resync/client/zip_package.rb +0 -66
  42. data/lib/resync/client/zip_packages.rb +0 -51
  43. 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: 541947c7770153caf12143dc26d885166877c4bd
4
- data.tar.gz: 5b7aa7977c089a5d09c7997d92088a695cece901
3
+ metadata.gz: 53bce63e13094ab4ffeec5ff5b3d4365c96eaf9f
4
+ data.tar.gz: 72becf942fb027a0d02d948e3a98fb28d8a626fc
5
5
  SHA512:
6
- metadata.gz: 2d4c7fa6f2ea310e34ff78c2fe956c99e1967ec4c4bba23d88a42cf5e0cc11ff5e4d84e8f88d2b79cf3f446132dbf97ab8e20ae903d90d58fa6306361c2e7c45
7
- data.tar.gz: b23323bc0c7ca29050e4379d8596fb7d4b51f16f9f14fee80e62be12f0b4d2fd0fd34b541825d77b8d8f825b883cdf94bc569e007b02d8c8994dc9d341b5d34f
6
+ metadata.gz: 97e28c200dbd29c6896c9c4d3e351f89ba0f3836f8cdf0ccc5e87517cb97fefe869e313311f87f635007a06e3373b9c499668b277d089b5b68c227fe4126559d
7
+ data.tar.gz: c5b24e9fbb0db69734b5e805077b9da163b86ac84a1c4bf296868979812a49e1efa8b863ca1a7a6a386b148ed8c82f0a68c584f51281437851ff8e4a9af11bf8
data/.gitignore CHANGED
@@ -36,6 +36,8 @@ spec/dummy/.sass-cache
36
36
  # Emacs
37
37
 
38
38
  *~
39
+ .#*
40
+ \#*
39
41
 
40
42
  # Mac OS
41
43
 
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
- # Utility class simplifying GET requests for HTTP/HTTPS resources.
9
- #
10
- class HTTPHelper
11
-
12
- # ------------------------------------------------------------
13
- # Constants
14
-
15
- # The default number of redirects to follow before erroring out.
16
- DEFAULT_MAX_REDIRECTS = 5
17
-
18
- # ------------------------------------------------------------
19
- # Accessors
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
- # Gets the content of the specified URI and saves it to a file. If no
58
- # file path is provided, saves it to a temporary file.
59
- # @param uri [URI] the URI to download
60
- # @param path [String] the path to save the download to (optional)
61
- # @return [String] the path to the downloaded file
62
- def fetch_to_file(uri:, path: nil, limit: redirect_limit)
63
- make_request(uri, limit) do |success|
64
- file = path ? File.new(path, 'w+') : Tempfile.new(['resync-client', ".#{extension_for(success)}"])
65
- open file, 'w' do |out|
66
- success.read_body { |chunk| out.write(chunk) }
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
- # Private methods
74
-
75
- private
76
-
77
- def make_request(uri, limit, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
78
- fail "Redirect limit (#{redirect_limit}) exceeded retrieving URI #{uri}" if limit <= 0
79
- req = Net::HTTP::Get.new(uri, 'User-Agent' => user_agent)
80
- Net::HTTP.start(uri.hostname, uri.port, use_ssl: (uri.scheme == 'https')) do |http|
81
- http.request(req) do |response|
82
- case response
83
- when Net::HTTPSuccess
84
- block.call(response)
85
- when Net::HTTPInformation, Net::HTTPRedirection
86
- make_request(redirect_uri_for(response, uri), limit - 1, &block)
87
- else
88
- fail "Error #{response.code}: #{response.message} retrieving URI #{uri}"
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
- def extension_for(response)
95
- content_type = response['Content-Type']
96
- mime_type = MIME::Types[content_type].first || MIME::Types['application/octet-stream'].first
97
- mime_type.preferred_extension || 'bin'
98
- end
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
- def redirect_uri_for(response, original_uri)
101
- if response.is_a?(Net::HTTPInformation)
102
- original_uri
103
- else
104
- location = response['location']
105
- new_uri = URI(location)
106
- new_uri.relative? ? original_uri + location : new_uri
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