wayback 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +10 -0
- data/CHANGELOG.md +3 -0
- data/CONTRIBUTING.md +52 -0
- data/LICENSE.md +20 -0
- data/README.md +81 -0
- data/Rakefile +11 -0
- data/lib/wayback.rb +32 -0
- data/lib/wayback/api/archive.rb +42 -0
- data/lib/wayback/api/utils.rb +27 -0
- data/lib/wayback/archive.rb +15 -0
- data/lib/wayback/base.rb +127 -0
- data/lib/wayback/client.rb +62 -0
- data/lib/wayback/configurable.rb +48 -0
- data/lib/wayback/default.rb +68 -0
- data/lib/wayback/error.rb +31 -0
- data/lib/wayback/error/already_favorited.rb +10 -0
- data/lib/wayback/error/already_retweeted.rb +10 -0
- data/lib/wayback/error/bad_gateway.rb +11 -0
- data/lib/wayback/error/bad_request.rb +10 -0
- data/lib/wayback/error/client_error.rb +28 -0
- data/lib/wayback/error/configuration_error.rb +8 -0
- data/lib/wayback/error/decode_error.rb +9 -0
- data/lib/wayback/error/forbidden.rb +10 -0
- data/lib/wayback/error/gateway_timeout.rb +11 -0
- data/lib/wayback/error/identity_map_key_error.rb +9 -0
- data/lib/wayback/error/internal_server_error.rb +11 -0
- data/lib/wayback/error/not_acceptable.rb +10 -0
- data/lib/wayback/error/not_found.rb +10 -0
- data/lib/wayback/error/server_error.rb +28 -0
- data/lib/wayback/error/service_unavailable.rb +11 -0
- data/lib/wayback/error/too_many_requests.rb +12 -0
- data/lib/wayback/error/unauthorized.rb +10 -0
- data/lib/wayback/error/unprocessable_entity.rb +10 -0
- data/lib/wayback/factory.rb +21 -0
- data/lib/wayback/identity.rb +50 -0
- data/lib/wayback/identity_map.rb +22 -0
- data/lib/wayback/page.rb +18 -0
- data/lib/wayback/response/parse_memento.rb +61 -0
- data/lib/wayback/response/parse_memento_page.rb +23 -0
- data/lib/wayback/response/raise_error.rb +31 -0
- data/lib/wayback/version.rb +18 -0
- data/spec/fixtures/list.timemap +9 -0
- data/spec/fixtures/page.html +225 -0
- data/spec/helper.rb +65 -0
- data/spec/wayback/api/archive_spec.rb +73 -0
- data/spec/wayback/archive_spec.rb +23 -0
- data/spec/wayback/base_spec.rb +117 -0
- data/spec/wayback/client_spec.rb +114 -0
- data/spec/wayback/error/client_error_spec.rb +23 -0
- data/spec/wayback/error/server_error_spec.rb +20 -0
- data/spec/wayback/error_spec.rb +20 -0
- data/spec/wayback/identifiable_spec.rb +50 -0
- data/spec/wayback/page_spec.rb +36 -0
- data/spec/wayback_spec.rb +47 -0
- data/wayback.gemspec +26 -0
- 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 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,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,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,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,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,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
|