resync-client 0.1.2 → 0.2.1

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