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.
- 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
|