doc_repo 0.1.1 → 1.0.0.pre.beta.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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +4 -4
- data/CHANGELOG.md +47 -0
- data/Gemfile +5 -0
- data/README.md +282 -16
- data/benchmarks/digests.rb +55 -0
- data/doc_repo.gemspec +5 -5
- data/lib/doc_repo/configuration.rb +65 -4
- data/lib/doc_repo/doc.rb +84 -0
- data/lib/doc_repo/error.rb +14 -0
- data/lib/doc_repo/gateway_error.rb +37 -0
- data/lib/doc_repo/http_error.rb +32 -0
- data/lib/doc_repo/http_result.rb +29 -0
- data/lib/doc_repo/net_http_adapter.rb +203 -0
- data/lib/doc_repo/rails/legacy_versioned_cache.rb +19 -0
- data/lib/doc_repo/rails.rb +37 -0
- data/lib/doc_repo/redirect.rb +19 -0
- data/lib/doc_repo/repository.rb +72 -20
- data/lib/doc_repo/result_handler.rb +30 -0
- data/lib/doc_repo/version.rb +1 -1
- data/lib/doc_repo.rb +20 -17
- data/spec/doc_repo/configuration_spec.rb +86 -31
- data/spec/doc_repo/doc_spec.rb +442 -0
- data/spec/doc_repo/net_http_adapter_spec.rb +435 -0
- data/spec/doc_repo/repository_spec.rb +325 -13
- data/spec/doc_repo/result_handler_spec.rb +43 -0
- data/spec/doc_repo_spec.rb +25 -3
- data/spec/spec_helper.rb +88 -3
- data/spec/support/in_memory_cache.rb +33 -0
- metadata +33 -20
- data/lib/doc_repo/github_file.rb +0 -45
- data/lib/doc_repo/page.rb +0 -35
- data/lib/doc_repo/response.rb +0 -25
- data/spec/doc_repo/page_spec.rb +0 -44
- data/spec/doc_repo/response_spec.rb +0 -19
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DocRepo
|
4
|
+
class GatewayError < Error
|
5
|
+
include HttpResult
|
6
|
+
|
7
|
+
def initialize(uri, code:, cause:)
|
8
|
+
init_result_readers(uri, code)
|
9
|
+
@cause = cause
|
10
|
+
message = case code
|
11
|
+
when NetHttpAdapter::BAD_GATEWAY
|
12
|
+
'502 "Bad Gateway"'
|
13
|
+
when NetHttpAdapter::GATEWAY_TIMEOUT
|
14
|
+
'504 "Gateway Timeout"'
|
15
|
+
else
|
16
|
+
name = if defined?(::Rack::Utils::HTTP_STATUS_CODES)
|
17
|
+
::Rack::Utils::HTTP_STATUS_CODES[code.to_i]
|
18
|
+
else
|
19
|
+
"Unknown Error"
|
20
|
+
end
|
21
|
+
"#{code} #{name.dump}"
|
22
|
+
end
|
23
|
+
super(message)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Wrap exception as normal
|
27
|
+
attr_reader :cause
|
28
|
+
|
29
|
+
def details
|
30
|
+
cause.message
|
31
|
+
end
|
32
|
+
|
33
|
+
def error?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DocRepo
|
4
|
+
class HttpError < Error
|
5
|
+
include HttpResult
|
6
|
+
|
7
|
+
def initialize(uri, http_response)
|
8
|
+
@http = http_response
|
9
|
+
init_result_readers(uri, @http.code)
|
10
|
+
message = @http.code
|
11
|
+
message += ' ' + @http.message.dump if @http.message
|
12
|
+
super(message)
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :http
|
16
|
+
private :http
|
17
|
+
|
18
|
+
def details
|
19
|
+
# NOTE: The Github raw site does not respond with anything other than
|
20
|
+
# `text/plain` for general HTTP errors.
|
21
|
+
http.body
|
22
|
+
end
|
23
|
+
|
24
|
+
def not_found?
|
25
|
+
404 == code
|
26
|
+
end
|
27
|
+
|
28
|
+
def error?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DocRepo
|
4
|
+
module HttpResult
|
5
|
+
def init_result_readers(uri, code)
|
6
|
+
@uri = uri.to_s.freeze
|
7
|
+
@code = code.to_i
|
8
|
+
end
|
9
|
+
protected :init_result_readers
|
10
|
+
|
11
|
+
attr_reader :code, :uri
|
12
|
+
|
13
|
+
def error?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def not_found?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def redirect?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def success?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'net/http'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module DocRepo
|
6
|
+
# @api private
|
7
|
+
class NetHttpAdapter
|
8
|
+
# Net::HTTP default timeouts of 60 seconds are too long for our purposes
|
9
|
+
DEFAULT_OPTS = {
|
10
|
+
open_timeout: 10,
|
11
|
+
read_timeout: 10,
|
12
|
+
ssl_timeout: 10,
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
# HTTP Status Codes
|
16
|
+
BAD_GATEWAY = 502
|
17
|
+
GATEWAY_TIMEOUT = 504
|
18
|
+
UNKNOWN_ERROR = 520
|
19
|
+
|
20
|
+
def initialize(host, cache: NullCache.instance, cache_options: {}, **opts)
|
21
|
+
@host = host.dup.freeze
|
22
|
+
@opts = DEFAULT_OPTS.dup.merge!(opts)
|
23
|
+
# Always force SSL
|
24
|
+
@opts[:use_ssl] = true
|
25
|
+
@opts.freeze
|
26
|
+
@cache = cache
|
27
|
+
@cache_options = cache_options.dup.freeze
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :host, :opts
|
31
|
+
|
32
|
+
attr_reader :cache, :cache_options
|
33
|
+
private :cache
|
34
|
+
|
35
|
+
def retrieve(uri)
|
36
|
+
resp = http_cache(uri)
|
37
|
+
case resp
|
38
|
+
when Net::HTTPRedirection
|
39
|
+
Redirect.new(
|
40
|
+
resp['Location'],
|
41
|
+
code: resp.code,
|
42
|
+
headers: resp.to_hash,
|
43
|
+
)
|
44
|
+
when Net::HTTPSuccess
|
45
|
+
Doc.new(uri, resp)
|
46
|
+
else
|
47
|
+
HttpError.new(uri, resp)
|
48
|
+
end
|
49
|
+
rescue Timeout::Error => timeout
|
50
|
+
# Covers Net::OpenTimeout, Net::ReadTimeout, etc.
|
51
|
+
GatewayError.new(uri, code: GATEWAY_TIMEOUT, cause: timeout)
|
52
|
+
rescue Net::HTTPBadResponse,
|
53
|
+
Net::ProtocolError,
|
54
|
+
OpenSSL::SSL::SSLError => protocol_error
|
55
|
+
# Docs state `Net::HTTPBadResponse` is raised when there is a protocol
|
56
|
+
# error. It's unclear whether all protocol errors are wrapped so we
|
57
|
+
# handle both here.
|
58
|
+
GatewayError.new(uri, code: BAD_GATEWAY, cause: protocol_error)
|
59
|
+
rescue => e
|
60
|
+
# Covers IOError, Errno::*, and SocketError
|
61
|
+
GatewayError.new(uri, code: UNKNOWN_ERROR, cause: e)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def cache_key(uri)
|
67
|
+
"#{host}:#{uri}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def conditional_headers(expired)
|
71
|
+
# Origin servers are supposed to treat `If-None-Match` with higher
|
72
|
+
# precedences than `If-Modified-Since` according to the RFC:
|
73
|
+
#
|
74
|
+
# > A recipient cache or origin server MUST evaluate the request
|
75
|
+
# > preconditions defined by this specification in the following order:
|
76
|
+
# >
|
77
|
+
# > 1. When recipient is the origin server and If-Match is present,
|
78
|
+
# > evaluate the If-Match precondition:
|
79
|
+
# >
|
80
|
+
# > * if true, continue to step 3
|
81
|
+
# >
|
82
|
+
# > * if false, respond 412 (Precondition Failed) unless it can be
|
83
|
+
# > determined that the state-changing request has already
|
84
|
+
# > succeeded (see Section 3.1)
|
85
|
+
# >
|
86
|
+
# > 2. When recipient is the origin server, If-Match is not present, and
|
87
|
+
# > If-Unmodified-Since is present, evaluate the If-Unmodified-Since
|
88
|
+
# > precondition:
|
89
|
+
# >
|
90
|
+
# > * if true, continue to step 3
|
91
|
+
# >
|
92
|
+
# > * if false, respond 412 (Precondition Failed) unless it can be
|
93
|
+
# > determined that the state-changing request has already
|
94
|
+
# > succeeded (see Section 3.4)
|
95
|
+
# >
|
96
|
+
# > 3. When If-None-Match is present, evaluate the If-None-Match
|
97
|
+
# > precondition:
|
98
|
+
# >
|
99
|
+
# > * if true, continue to step 5
|
100
|
+
# >
|
101
|
+
# > * if false for GET/HEAD, respond 304 (Not Modified)
|
102
|
+
# >
|
103
|
+
# > * if false for other methods, respond 412 (Precondition Failed)
|
104
|
+
# >
|
105
|
+
# > 4. When the method is GET or HEAD, If-None-Match is not present, and
|
106
|
+
# > If-Modified-Since is present, evaluate the If-Modified-Since
|
107
|
+
# > precondition:
|
108
|
+
# >
|
109
|
+
# > * if true, continue to step 5
|
110
|
+
# >
|
111
|
+
# > * if false, respond 304 (Not Modified)
|
112
|
+
# >
|
113
|
+
# > -- https://tools.ietf.org/html/rfc7232#section-6
|
114
|
+
#
|
115
|
+
# This allows clients, and caches, some flexibility in how they generate
|
116
|
+
# the `If-Modified-Since` header:
|
117
|
+
#
|
118
|
+
# > When used for cache updates, a cache will typically use the value of
|
119
|
+
# > the cached message's Last-Modified field to generate the field value
|
120
|
+
# > of If-Modified-Since. This behavior is most interoperable for cases
|
121
|
+
# > where clocks are poorly synchronized or when the server has chosen to
|
122
|
+
# > only honor exact timestamp matches (due to a problem with
|
123
|
+
# > Last-Modified dates that appear to go "back in time" when the origin
|
124
|
+
# > server's clock is corrected or a representation is restored from an
|
125
|
+
# > archived backup). However, caches occasionally generate the field
|
126
|
+
# > value based on other data, such as the Date header field of the
|
127
|
+
# > cached message or the local clock time that the message was received,
|
128
|
+
# > particularly when the cached message does not contain a Last-Modified
|
129
|
+
# > field.
|
130
|
+
# >
|
131
|
+
# > -- https://tools.ietf.org/html/rfc7232#section-3.3
|
132
|
+
#
|
133
|
+
# However, the Github raw content server (GRC) does not respect this.
|
134
|
+
# This may be due to the fact that the GRC does not send a
|
135
|
+
# `Last-Modified` header in replies. If we take that into account this
|
136
|
+
# behavior _may_ make sense if we assume the GRC is following the now
|
137
|
+
# obsolete HTTP/1.1 RFC 2616:
|
138
|
+
#
|
139
|
+
# > An HTTP/1.1 origin server, upon receiving a conditional request that
|
140
|
+
# > includes both a Last-Modified date (e.g., in an If-Modified-Since or
|
141
|
+
# > If-Unmodified-Since header field) and one or more entity tags (e.g.,
|
142
|
+
# > in an If-Match, If-None-Match, or If-Range header field) as cache
|
143
|
+
# > validators, MUST NOT return a response status of 304 (Not Modified)
|
144
|
+
# > unless doing so is consistent with all of the conditional header
|
145
|
+
# > fields in the request.
|
146
|
+
# >
|
147
|
+
# > -- https://tools.ietf.org/html/rfc2616#section-13.3.4
|
148
|
+
#
|
149
|
+
# So to actually receive `304 Not Modified` replies from GRC, but also
|
150
|
+
# try to be compatible with more current servers, this only sets
|
151
|
+
# `If-Modified-Since` based on the `Last-Modified` value (i.e. we no
|
152
|
+
# longer fall back to the `Date` value).
|
153
|
+
preconditions = {
|
154
|
+
"If-None-Match" => expired["ETag"],
|
155
|
+
"If-Modified-Since" => expired["Last-Modified"],
|
156
|
+
}
|
157
|
+
preconditions.compact!
|
158
|
+
preconditions
|
159
|
+
end
|
160
|
+
|
161
|
+
def expired?(resp)
|
162
|
+
# TODO: Use `Cache-Control` header when available
|
163
|
+
expires_at = resp['Expires']
|
164
|
+
expires_at && Time.httpdate(expires_at) < Time.now
|
165
|
+
rescue ArgumentError => _e
|
166
|
+
# Raised when `Time.parse` cannot parse the value
|
167
|
+
#
|
168
|
+
# Per the HTTP 1.1 RFC regarding the `Expires` header:
|
169
|
+
#
|
170
|
+
# > A cache recipient MUST interpret invalid date formats, especially the
|
171
|
+
# > value "0", as representing a time in the past (i.e., "already
|
172
|
+
# > expired").
|
173
|
+
# >
|
174
|
+
# > -- https://tools.ietf.org/html/rfc7234#section-5.3
|
175
|
+
true
|
176
|
+
end
|
177
|
+
|
178
|
+
def http_cache(uri)
|
179
|
+
uri_key = cache_key(uri)
|
180
|
+
resp = cache.fetch(uri_key, cache_options) {
|
181
|
+
Net::HTTP.start(host, opts) { |http| http.get(uri) }
|
182
|
+
}
|
183
|
+
if expired?(resp)
|
184
|
+
resp = refresh(uri, resp)
|
185
|
+
cache.write uri_key, resp, cache_options
|
186
|
+
end
|
187
|
+
resp
|
188
|
+
end
|
189
|
+
|
190
|
+
def refresh(uri, expired)
|
191
|
+
fresh = Net::HTTP.start(host, opts) { |http|
|
192
|
+
http.get(uri, conditional_headers(expired))
|
193
|
+
}
|
194
|
+
if Net::HTTPNotModified === fresh
|
195
|
+
fresh.each_header do |k, v|
|
196
|
+
expired[k] = v
|
197
|
+
end
|
198
|
+
fresh = expired
|
199
|
+
end
|
200
|
+
fresh
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patches to work with-in Rails conventions
|
4
|
+
module DocRepo
|
5
|
+
module Rails
|
6
|
+
# Prior to ActiveSupport 5.2 it is assumed the `cache_key` value contains
|
7
|
+
# version information.
|
8
|
+
module VersionedCacheKey
|
9
|
+
def cache_key
|
10
|
+
"#{super}-#{cache_version}"
|
11
|
+
end
|
12
|
+
alias_method :cache_key_with_version, :cache_key
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
DocRepo::Doc.class_exec do
|
18
|
+
include DocRepo::Rails::VersionedCacheKey
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patches to work with-in Rails conventions
|
4
|
+
module DocRepo
|
5
|
+
module Rails
|
6
|
+
module Modelish
|
7
|
+
# For some reason Rails _only_ calls `to_text` for the following:
|
8
|
+
#
|
9
|
+
# render html: doc
|
10
|
+
# render body: doc
|
11
|
+
# render plain: doc
|
12
|
+
#
|
13
|
+
# There's no way for us to know which of these is being called so we
|
14
|
+
# can't conditionally provide the raw markdown for `plain`. And without
|
15
|
+
# this `to_s` will be called then HTML escaped:
|
16
|
+
#
|
17
|
+
# "#<DocRepo::Doc:0x007fabefe8c360>"
|
18
|
+
def to_text
|
19
|
+
to_html.html_safe
|
20
|
+
end
|
21
|
+
|
22
|
+
def updated_at
|
23
|
+
last_modified
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Prior to ActiveSupport 5.2 it is assumed the `cache_key` value contains
|
30
|
+
# version information.
|
31
|
+
if Rails.gem_version < Gem::Version.new("5.2.0")
|
32
|
+
require_relative 'rails/legacy_versioned_cache'
|
33
|
+
end
|
34
|
+
|
35
|
+
DocRepo::Doc.class_exec do
|
36
|
+
include DocRepo::Rails::Modelish
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DocRepo
|
4
|
+
class Redirect
|
5
|
+
include HttpResult
|
6
|
+
|
7
|
+
def initialize(url, code: 302, headers: {})
|
8
|
+
init_result_readers(url, code)
|
9
|
+
@headers = headers.freeze
|
10
|
+
end
|
11
|
+
|
12
|
+
alias_method :url, :uri
|
13
|
+
alias_method :location, :url
|
14
|
+
|
15
|
+
def redirect?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/doc_repo/repository.rb
CHANGED
@@ -1,31 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'forwardable'
|
3
|
+
|
1
4
|
module DocRepo
|
2
5
|
class Repository
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
.
|
7
|
-
|
8
|
-
.
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def initialize(config, http_adapter: nil)
|
9
|
+
@config = config.dup.freeze
|
10
|
+
@http = http_adapter
|
11
|
+
@http ||= NetHttpAdapter.new(
|
12
|
+
GITHUB_HOST,
|
13
|
+
cache: cache_store,
|
14
|
+
cache_options: cache_options,
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :config
|
19
|
+
def_delegators :config, :branch, :doc_root, :fallback_ext, :org, :repo
|
20
|
+
|
21
|
+
def request(slug, result_handler: ResultHandler.new)
|
22
|
+
yield result_handler
|
23
|
+
result = detect(uri_for(slug))
|
24
|
+
action = handler_for(result, result_handler)
|
25
|
+
action.call result
|
26
|
+
end
|
27
|
+
|
28
|
+
def uri_for(slug)
|
29
|
+
"/#{org}/#{repo}/#{branch}/#{doc_root}/#{ensure_ext(slug)}".squeeze("/")
|
18
30
|
end
|
19
31
|
|
20
32
|
private
|
21
33
|
|
22
|
-
|
23
|
-
|
34
|
+
GITHUB_HOST = "raw.githubusercontent.com"
|
35
|
+
|
36
|
+
attr_reader :http
|
37
|
+
def_delegators :config, :doc_formats, :cache_store, :cache_options
|
38
|
+
|
39
|
+
def redirect_type?(ext)
|
40
|
+
!doc_formats.include?(ext)
|
24
41
|
end
|
25
42
|
|
26
|
-
def
|
27
|
-
|
43
|
+
def detect(uri)
|
44
|
+
if redirect_type?(File.extname(uri))
|
45
|
+
Redirect.new("https://#{GITHUB_HOST}#{uri}")
|
46
|
+
else
|
47
|
+
http.retrieve(uri)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def ensure_ext(slug)
|
52
|
+
if File.extname(slug).empty?
|
53
|
+
"#{slug}#{fallback_ext}"
|
54
|
+
else
|
55
|
+
slug
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def handler_for(result, result_handler)
|
60
|
+
case
|
61
|
+
when result.redirect?
|
62
|
+
result_handler.fetch(:redirect) {
|
63
|
+
raise UnhandledAction.new(:redirect, <<~MSG.chomp)
|
64
|
+
no result redirect handler defined for #{result_handler.inspect}
|
65
|
+
MSG
|
66
|
+
}
|
67
|
+
when result.success?
|
68
|
+
result_handler.fetch(:complete) {
|
69
|
+
raise UnhandledAction.new(:complete, <<~MSG.chomp)
|
70
|
+
no result completion handler defined for #{result_handler.inspect}
|
71
|
+
MSG
|
72
|
+
}
|
73
|
+
when result.not_found?
|
74
|
+
result_handler.fetch(:not_found) {
|
75
|
+
result_handler.fetch(:error) { raise result }
|
76
|
+
}
|
77
|
+
else
|
78
|
+
# TODO: Are we missing other cases?
|
79
|
+
result_handler.fetch(:error) { raise result }
|
80
|
+
end
|
28
81
|
end
|
29
82
|
end
|
30
83
|
end
|
31
|
-
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module DocRepo
|
5
|
+
class ResultHandler
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def self.handler(*types)
|
9
|
+
types.each do |type|
|
10
|
+
define_method(type) do |&block|
|
11
|
+
raise ArgumentError, "Result handler block required" unless block
|
12
|
+
@actions[type] = block
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
private_class_method :handler
|
17
|
+
|
18
|
+
handler :complete, :error, :not_found, :redirect
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@actions = {}
|
22
|
+
yield self if block_given?
|
23
|
+
end
|
24
|
+
|
25
|
+
def_delegators :actions, :[], :each, :fetch
|
26
|
+
|
27
|
+
attr_reader :actions
|
28
|
+
private :actions
|
29
|
+
end
|
30
|
+
end
|
data/lib/doc_repo/version.rb
CHANGED
data/lib/doc_repo.rb
CHANGED
@@ -1,23 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require "doc_repo/version"
|
2
3
|
|
3
4
|
module DocRepo
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
require_relative "doc_repo/configuration"
|
6
|
+
require_relative "doc_repo/error"
|
7
|
+
|
8
|
+
# HTTP Adapter and Results
|
9
|
+
autoload :NetHttpAdapter, "doc_repo/net_http_adapter"
|
10
|
+
autoload :HttpResult, "doc_repo/http_result"
|
11
|
+
autoload :Doc, "doc_repo/doc"
|
12
|
+
autoload :Redirect, "doc_repo/redirect"
|
13
|
+
autoload :HttpError, "doc_repo/http_error"
|
14
|
+
autoload :GatewayError, "doc_repo/gateway_error"
|
15
|
+
|
7
16
|
autoload :Repository, "doc_repo/repository"
|
8
|
-
autoload :
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
attr_reader :base
|
13
|
-
def initialize(*args, base: $!)
|
14
|
-
@base = base
|
15
|
-
super(*args)
|
16
|
-
end
|
17
|
+
autoload :ResultHandler, "doc_repo/result_handler"
|
18
|
+
|
19
|
+
if defined?(Rails)
|
20
|
+
require_relative "doc_repo/rails"
|
17
21
|
end
|
18
22
|
|
19
23
|
class << self
|
20
|
-
|
24
|
+
attr_writer :configuration
|
21
25
|
|
22
26
|
def configuration
|
23
27
|
@configuration ||= Configuration.new
|
@@ -25,11 +29,10 @@ module DocRepo
|
|
25
29
|
end
|
26
30
|
|
27
31
|
def self.configure
|
28
|
-
yield(configuration)
|
32
|
+
yield(configuration)
|
29
33
|
end
|
30
34
|
|
31
|
-
def self.
|
32
|
-
Repository.new.
|
35
|
+
def self.request(slug, &block)
|
36
|
+
Repository.new(configuration).request(slug, &block)
|
33
37
|
end
|
34
38
|
end
|
35
|
-
|