wayback 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.
Files changed (56) hide show
  1. data/.yardopts +10 -0
  2. data/CHANGELOG.md +3 -0
  3. data/CONTRIBUTING.md +52 -0
  4. data/LICENSE.md +20 -0
  5. data/README.md +81 -0
  6. data/Rakefile +11 -0
  7. data/lib/wayback.rb +32 -0
  8. data/lib/wayback/api/archive.rb +42 -0
  9. data/lib/wayback/api/utils.rb +27 -0
  10. data/lib/wayback/archive.rb +15 -0
  11. data/lib/wayback/base.rb +127 -0
  12. data/lib/wayback/client.rb +62 -0
  13. data/lib/wayback/configurable.rb +48 -0
  14. data/lib/wayback/default.rb +68 -0
  15. data/lib/wayback/error.rb +31 -0
  16. data/lib/wayback/error/already_favorited.rb +10 -0
  17. data/lib/wayback/error/already_retweeted.rb +10 -0
  18. data/lib/wayback/error/bad_gateway.rb +11 -0
  19. data/lib/wayback/error/bad_request.rb +10 -0
  20. data/lib/wayback/error/client_error.rb +28 -0
  21. data/lib/wayback/error/configuration_error.rb +8 -0
  22. data/lib/wayback/error/decode_error.rb +9 -0
  23. data/lib/wayback/error/forbidden.rb +10 -0
  24. data/lib/wayback/error/gateway_timeout.rb +11 -0
  25. data/lib/wayback/error/identity_map_key_error.rb +9 -0
  26. data/lib/wayback/error/internal_server_error.rb +11 -0
  27. data/lib/wayback/error/not_acceptable.rb +10 -0
  28. data/lib/wayback/error/not_found.rb +10 -0
  29. data/lib/wayback/error/server_error.rb +28 -0
  30. data/lib/wayback/error/service_unavailable.rb +11 -0
  31. data/lib/wayback/error/too_many_requests.rb +12 -0
  32. data/lib/wayback/error/unauthorized.rb +10 -0
  33. data/lib/wayback/error/unprocessable_entity.rb +10 -0
  34. data/lib/wayback/factory.rb +21 -0
  35. data/lib/wayback/identity.rb +50 -0
  36. data/lib/wayback/identity_map.rb +22 -0
  37. data/lib/wayback/page.rb +18 -0
  38. data/lib/wayback/response/parse_memento.rb +61 -0
  39. data/lib/wayback/response/parse_memento_page.rb +23 -0
  40. data/lib/wayback/response/raise_error.rb +31 -0
  41. data/lib/wayback/version.rb +18 -0
  42. data/spec/fixtures/list.timemap +9 -0
  43. data/spec/fixtures/page.html +225 -0
  44. data/spec/helper.rb +65 -0
  45. data/spec/wayback/api/archive_spec.rb +73 -0
  46. data/spec/wayback/archive_spec.rb +23 -0
  47. data/spec/wayback/base_spec.rb +117 -0
  48. data/spec/wayback/client_spec.rb +114 -0
  49. data/spec/wayback/error/client_error_spec.rb +23 -0
  50. data/spec/wayback/error/server_error_spec.rb +20 -0
  51. data/spec/wayback/error_spec.rb +20 -0
  52. data/spec/wayback/identifiable_spec.rb +50 -0
  53. data/spec/wayback/page_spec.rb +36 -0
  54. data/spec/wayback_spec.rb +47 -0
  55. data/wayback.gemspec +26 -0
  56. metadata +175 -0
@@ -0,0 +1,48 @@
1
+ require 'forwardable'
2
+ require 'wayback/error/configuration_error'
3
+
4
+ module Wayback
5
+ module Configurable
6
+ extend Forwardable
7
+ attr_accessor :endpoint, :connection_options, :identity_map, :middleware
8
+ def_delegator :options, :hash
9
+
10
+ class << self
11
+
12
+ def keys
13
+ @keys ||= [
14
+ :endpoint,
15
+ :connection_options,
16
+ :identity_map,
17
+ :middleware
18
+ ]
19
+ end
20
+
21
+ end
22
+
23
+ # Convenience method to allow configuration options to be set in a block
24
+ #
25
+ # @raise [Wayback::Error::ConfigurationError] Error is raised when supplied
26
+ # wayback credentials are not a String or Symbol.
27
+ def configure
28
+ yield self
29
+ self
30
+ end
31
+
32
+ def reset!
33
+ Wayback::Configurable.keys.each do |key|
34
+ instance_variable_set(:"@#{key}", Wayback::Default.options[key])
35
+ end
36
+ self
37
+ end
38
+ alias setup reset!
39
+
40
+ private
41
+
42
+ # @return [Hash]
43
+ def options
44
+ Hash[Wayback::Configurable.keys.map{|key| [key, instance_variable_get(:"@#{key}")]}]
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,68 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'wayback/configurable'
4
+ require 'wayback/error/client_error'
5
+ require 'wayback/error/server_error'
6
+ require 'wayback/response/raise_error'
7
+ require 'wayback/response/parse_memento'
8
+ require 'wayback/response/parse_memento_page'
9
+ require 'wayback/version'
10
+
11
+ module Wayback
12
+ module Default
13
+ ENDPOINT = 'http://api.wayback.archive.org' unless defined? Wayback::Default::ENDPOINT
14
+ CONNECTION_OPTIONS = {
15
+ :headers => {:user_agent => "Wayback Ruby Gem #{Wayback::Version}"},
16
+ :request => {:open_timeout => 5, :timeout => 10},
17
+ :ssl => {:verify => false},
18
+ } unless defined? Wayback::Default::CONNECTION_OPTIONS
19
+ IDENTITY_MAP = false unless defined? Wayback::Default::IDENTITY_MAP
20
+ MIDDLEWARE = Faraday::Builder.new do |builder|
21
+ # Convert request params to "www-form-urlencoded"
22
+ builder.use Faraday::Request::UrlEncoded
23
+ # Follow redirects
24
+ builder.use FaradayMiddleware::FollowRedirects
25
+ # Handle 4xx server responses
26
+ builder.use Wayback::Response::RaiseError, Wayback::Error::ClientError
27
+ # Handle 5xx server responses
28
+ builder.use Wayback::Response::RaiseError, Wayback::Error::ServerError
29
+ # Parse memento page
30
+ builder.use Wayback::Response::ParseMementoPage
31
+ # Parse link-format with custom memento parser
32
+ builder.use Wayback::Response::ParseMemento
33
+ # Set Faraday's HTTP adapter
34
+ builder.adapter Faraday.default_adapter
35
+ end unless defined? Wayback::Default::MIDDLEWARE
36
+
37
+ class << self
38
+
39
+ # @return [Hash]
40
+ def options
41
+ Hash[Wayback::Configurable.keys.map{|key| [key, send(key)]}]
42
+ end
43
+
44
+ # @note This is configurable in case you want to use a Wayback Machine-compatible endpoint.
45
+ # @return [String]
46
+ def endpoint
47
+ ENDPOINT
48
+ end
49
+
50
+ def connection_options
51
+ CONNECTION_OPTIONS
52
+ end
53
+
54
+ def identity_map
55
+ IDENTITY_MAP
56
+ end
57
+
58
+ # @note Faraday's middleware stack implementation is comparable to that of Rack middleware. The order of middleware is important: the first middleware on the list wraps all others, while the last middleware is the innermost one.
59
+ # @see https://github.com/technoweenie/faraday#advanced-middleware-usage
60
+ # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
61
+ # @return [Faraday::Builder]
62
+ def middleware
63
+ MIDDLEWARE
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,31 @@
1
+ module Wayback
2
+ # Custom error class for rescuing from all Wayback errors
3
+ class Error < StandardError
4
+ attr_reader :wrapped_exception
5
+
6
+ # @return [Hash]
7
+ def self.errors
8
+ @errors ||= Hash[descendants.map{|klass| [klass.const_get(:HTTP_STATUS_CODE), klass]}]
9
+ end
10
+
11
+ # @return [Array]
12
+ def self.descendants
13
+ ObjectSpace.each_object(::Class).select{|klass| klass < self}
14
+ end
15
+
16
+ # Initializes a new Error object
17
+ #
18
+ # @param exception [Exception, String]
19
+ # @param response_headers [Hash]
20
+ # @return [Wayback::Error]
21
+ def initialize(exception=$!, response_headers={})
22
+ @wrapped_exception = exception
23
+ exception.respond_to?(:backtrace) ? super(exception.message) : super(exception.to_s)
24
+ end
25
+
26
+ def backtrace
27
+ @wrapped_exception.respond_to?(:backtrace) ? @wrapped_exception.backtrace : super
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,10 @@
1
+ require 'wayback/error/forbidden'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when a Tweet has already been favorited
6
+ class AlreadyFavorited < Wayback::Error
7
+ MESSAGE = "You have already favorited this status"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'wayback/error/forbidden'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when a Tweet has already been retweeted
6
+ class AlreadyRetweeted < Wayback::Error
7
+ MESSAGE = "sharing is not permissible for this status (Share validations failed)"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ require 'wayback/error/server_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 502
6
+ class BadGateway < Wayback::Error::ServerError
7
+ HTTP_STATUS_CODE = 502
8
+ MESSAGE = "Wayback is down or being upgraded."
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ require 'wayback/error/client_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 400
6
+ class BadRequest < Wayback::Error::ClientError
7
+ HTTP_STATUS_CODE = 400
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ require 'wayback/error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns a 4xx HTTP status code or there's an error in Faraday
6
+ class ClientError < Wayback::Error
7
+
8
+ # Create a new error from an HTTP environment
9
+ #
10
+ # @param response [Hash]
11
+ # @return [Wayback::Error]
12
+ def self.from_response(response={})
13
+ new(parse_error(response[:body]), response[:response_headers])
14
+ end
15
+
16
+ private
17
+
18
+ def self.parse_error(body)
19
+ if body.nil? || body.is_a?(String)
20
+ ''
21
+ # elsif body[:error]
22
+ # body[:error]
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ require 'wayback/error'
2
+
3
+ module Wayback
4
+ class Error
5
+ class ConfigurationError < ::ArgumentError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ require 'wayback/error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when JSON parsing fails
6
+ class DecodeError < Wayback::Error
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ require 'wayback/error/client_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 403
6
+ class Forbidden < Wayback::Error::ClientError
7
+ HTTP_STATUS_CODE = 403
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ require 'wayback/error/server_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 504
6
+ class GatewayTimeout < Wayback::Error::ServerError
7
+ HTTP_STATUS_CODE = 504
8
+ MESSAGE = "The Wayback servers are up, but the request couldn't be serviced due to some failure within our stack. Try again later."
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require 'wayback/error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Inherit from KeyError when Ruby 1.8 compatibility is removed
6
+ class IdentityMapKeyError < ::IndexError
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ require 'wayback/error/server_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 500
6
+ class InternalServerError < Wayback::Error::ServerError
7
+ HTTP_STATUS_CODE = 500
8
+ MESSAGE = "Something is technically wrong."
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ require 'wayback/error/client_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 406
6
+ class NotAcceptable < Wayback::Error::ClientError
7
+ HTTP_STATUS_CODE = 406
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'wayback/error/client_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 404
6
+ class NotFound < Wayback::Error::ClientError
7
+ HTTP_STATUS_CODE = 404
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ require 'wayback/error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns a 5xx HTTP status code
6
+ class ServerError < Wayback::Error
7
+ MESSAGE = "Server Error"
8
+
9
+ # Create a new error from an HTTP environment
10
+ #
11
+ # @param response [Hash]
12
+ # @return [Wayback::Error]
13
+ def self.from_response(response={})
14
+ new(nil, response[:response_headers])
15
+ end
16
+
17
+ # Initializes a new ServerError object
18
+ #
19
+ # @param message [String]
20
+ # @param response_headers [Hash]
21
+ # @return [Wayback::Error::ServerError]
22
+ def initialize(message=nil, response_headers={})
23
+ super((message || self.class.const_get(:MESSAGE)), response_headers)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ require 'wayback/error/server_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 503
6
+ class ServiceUnavailable < Wayback::Error::ServerError
7
+ HTTP_STATUS_CODE = 503
8
+ MESSAGE = "(__-){ Wayback is over capacity."
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ require 'wayback/error/client_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 429
6
+ class TooManyRequests < Wayback::Error::ClientError
7
+ HTTP_STATUS_CODE = 429
8
+ end
9
+ EnhanceYourCalm = TooManyRequests
10
+ RateLimited = TooManyRequests
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ require 'wayback/error/client_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 401
6
+ class Unauthorized < Wayback::Error::ClientError
7
+ HTTP_STATUS_CODE = 401
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'wayback/error/client_error'
2
+
3
+ module Wayback
4
+ class Error
5
+ # Raised when Wayback returns the HTTP status code 404
6
+ class UnprocessableEntity < Wayback::Error::ClientError
7
+ HTTP_STATUS_CODE = 422
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module Wayback
2
+ class Factory
3
+
4
+ # Instantiates a new action object
5
+ #
6
+ # @param attrs [Hash]
7
+ # @raise [ArgumentError] Error raised when supplied argument is missing an :action key.
8
+ # @return [Wayback::Action::Favorite, Wayback::Action::Follow, Wayback::Action::ListMemberAdded, Wayback::Action::Mention, Wayback::Action::Reply, Wayback::Action::Retweet]
9
+ def self.fetch_or_new(method, klass, attrs={})
10
+ return unless attrs
11
+ type = attrs.delete(method.to_sym)
12
+ if type
13
+ const_name = type.gsub(/\/(.?)/){"::#{$1.upcase}"}.gsub(/(?:^|_)(.)/){$1.upcase}
14
+ klass.const_get(const_name.to_sym).fetch_or_new(attrs)
15
+ else
16
+ raise ArgumentError, "argument must have :#{method} key"
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,50 @@
1
+ require 'wayback/base'
2
+ require 'wayback/error/identity_map_key_error'
3
+
4
+ module Wayback
5
+ class Identity < Wayback::Base
6
+
7
+ def self.fetch(attrs)
8
+ return unless identity_map
9
+
10
+ id = attrs[:id]
11
+ if id && object = identity_map.fetch(id)
12
+ return object.update(attrs)
13
+ end
14
+
15
+ return yield if block_given?
16
+ raise Wayback::Error::IdentityMapKeyError, "key not found"
17
+ end
18
+
19
+ # Stores an object in the identity map.
20
+ #
21
+ # @param object [Object]
22
+ # @return [Wayback::Identity]
23
+ def self.store(object)
24
+ return object unless identity_map
25
+ identity_map.store(object.id, object)
26
+ end
27
+
28
+ # Initializes a new object
29
+ #
30
+ # @param attrs [Hash]
31
+ # @raise [ArgumentError] Error raised when supplied argument is missing an :id key.
32
+ # @return [Wayback::Identity]
33
+ def initialize(attrs={})
34
+ super
35
+ raise ArgumentError, "argument must have an :id key" unless id
36
+ end
37
+
38
+ # @param other [Wayback::Identity]
39
+ # @return [Boolean]
40
+ def ==(other)
41
+ super || attr_equal(:id, other) || attrs_equal(other)
42
+ end
43
+
44
+ # @return [Integer]
45
+ def id
46
+ @attrs[:id]
47
+ end
48
+
49
+ end
50
+ end