doc_repo 0.1.1 → 1.0.0.pre.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30a5493aa98e1c88a25bc6b485716d6e8aecf14b
|
4
|
+
data.tar.gz: 9ffdcb7925e8e3743dca930bfe03a46669305b4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b1b66bdfe4e58c16bd0c173c65795c96ea5aaacb313376a92927a5bc8ca60d0d18032aca18db0259bcdb86400a0bd77d4949f33e5dafbc2e5d1549d9a54bd1b
|
7
|
+
data.tar.gz: 70ab80ee398d1491c22255206f41573a9268fe6adc87113a02269165dcd54973292797e10018c9b0c2ad86a3eb3f256e65f56e7e0a508a566ca6d038527e01c2
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
language: ruby
|
2
2
|
sudo: false
|
3
|
+
distro: trusty
|
3
4
|
bundler_args: --binstubs --standalone --without documentation debug
|
4
5
|
script: bin/rspec
|
5
6
|
rvm:
|
6
|
-
- 2.
|
7
|
-
- 2.
|
7
|
+
- 2.4 # Always try testing against latest patch release
|
8
|
+
- 2.4.0
|
9
|
+
- 2.4.1
|
8
10
|
- ruby-head
|
9
|
-
- rbx-2
|
10
11
|
matrix:
|
11
12
|
allow_failures:
|
12
13
|
- rvm: ruby-head
|
13
|
-
- rvm: rbx-2
|
14
14
|
fast_finish: true
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
## v1.0.0-beta.1 / 2017-07-07
|
2
|
+
|
3
|
+
[Full Changelog](http://github.com/RadiusNetworks/doc_repo/compare/v0.1.1...v1.0.0-beta.1)
|
4
|
+
|
5
|
+
Enhancements:
|
6
|
+
|
7
|
+
- Add support for 2.4 (Aaron Kromer, #5)
|
8
|
+
- Add new configuration settings (Aaron Kromer, #8)
|
9
|
+
- `cache_options`
|
10
|
+
- `cache_store`
|
11
|
+
- `doc_formats`
|
12
|
+
- `doc_root`
|
13
|
+
- `fallback_ext`
|
14
|
+
- Add ability to provide custom error handling through callback to avoid
|
15
|
+
control flow by exception (Aaron Kromer, #8)
|
16
|
+
- Provide ability to specify separate `not_found` and `error` handlers with
|
17
|
+
`not_found` falling back to the `error` handler when not set (Aaron Kromer,
|
18
|
+
#8)
|
19
|
+
- Support local HTTP cache reducing remote origin requests (Aaron Kromer, #8)
|
20
|
+
- Further Reduce remote origin requests through use of `Expires` header with
|
21
|
+
conditional `GET` requests using `If-None-Match` and `If-Modified-Since` when
|
22
|
+
local HTTP cache is configured (Aaron Kromer, #8)
|
23
|
+
- Support Rails view caching of `DocRepo::Doc` instances (Aaron Kromer, #8)
|
24
|
+
- Support Rails 5.2 recyclable view caches for `DocRepo::Doc` instances (Aaron
|
25
|
+
Kromer, #8)
|
26
|
+
- Support Rails conditional `GET` through `fresh_when` and `stale?` for
|
27
|
+
`DocRepo::Doc` instances (Aaron Kromer, #8)
|
28
|
+
|
29
|
+
Bug Fixes:
|
30
|
+
|
31
|
+
- Add back the `DocRepo.configuration=` writer (Aaron Kromer, #5)
|
32
|
+
|
33
|
+
Breaking Changes:
|
34
|
+
|
35
|
+
- Drop support for Ruby 2.0, 2.1, and 2.2 (Aaron Kromer, #5)
|
36
|
+
- Drop support for Ruby 2.3 (Aaron Kromer, #8)
|
37
|
+
- Drop support for inheriting configuration from `ENV` (Aaron Kromer, #8)
|
38
|
+
- Drop the following classes (Aaron Kromer, #8)
|
39
|
+
- `DocRepo::BadPageFormat`
|
40
|
+
- `DocRepo::NotFound`
|
41
|
+
- `DocRepo::GithubFile`
|
42
|
+
- `DocRepo::Page`
|
43
|
+
- `DocRepo::Response`
|
44
|
+
- `DocRepo#respond_with` is now `DocRepo#request` and the object yielded
|
45
|
+
to the block uses handler blocks (Aaron Kromer, #8)
|
46
|
+
- `DocRepo::Repository` requires a `DocRepo::Configuration` on creation (Aaron
|
47
|
+
Kromer, #8)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# DocRepo
|
2
2
|
|
3
|
-
Store your markdown based documentation in a repo but serve it from with in
|
3
|
+
Store your markdown based documentation in a repo but serve it from with in
|
4
|
+
your app.
|
4
5
|
|
5
|
-
This is a little project that will pull raw markdown from the GitHub API and
|
6
|
+
This is a little project that will pull raw markdown from the GitHub API and
|
7
|
+
proxy them through your app. This lets you render things in your app, customize
|
8
|
+
the layout and access control -- but lets you update the docs without
|
9
|
+
re-deploying.
|
6
10
|
|
7
11
|
|
8
12
|
## Installation
|
@@ -15,30 +19,177 @@ gem 'doc_repo'
|
|
15
19
|
|
16
20
|
And then execute:
|
17
21
|
|
18
|
-
|
22
|
+
```console
|
23
|
+
$ bundle
|
24
|
+
```
|
19
25
|
|
20
26
|
Or install it yourself as:
|
21
27
|
|
22
|
-
|
28
|
+
```console
|
29
|
+
$ gem install doc_repo
|
30
|
+
```
|
23
31
|
|
24
32
|
## Usage
|
25
33
|
|
26
|
-
|
34
|
+
Initialize the configuration through `DocRepo.configuration`. It's good to
|
35
|
+
place this somewhere early in the app startup (such as a Rails initializer
|
36
|
+
`config/initializers/doc_repo.rb`):
|
27
37
|
|
28
38
|
```ruby
|
29
39
|
DocRepo.configure do |c|
|
30
|
-
# GitHub
|
40
|
+
# GitHub Organization or User:
|
31
41
|
c.org = "RadiusNetworks"
|
32
42
|
|
33
43
|
# GitHub Repo:
|
34
|
-
c.repo = "
|
44
|
+
c.repo = "doc_repo"
|
35
45
|
|
36
|
-
# Git Branch (Optional):
|
46
|
+
# Git Branch (Optional - default is 'master'):
|
37
47
|
c.branch = "master"
|
38
48
|
end
|
39
49
|
```
|
40
50
|
|
41
|
-
|
51
|
+
Requests for documents can then be made through `DocRepo.request`:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
DocRepo.request(params[:slug]) do |on|
|
55
|
+
on.complete do |doc|
|
56
|
+
# Do something with the document
|
57
|
+
end
|
58
|
+
|
59
|
+
on.redirect do |target|
|
60
|
+
# The asset exists else where and should be requested directly
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
### Advanced Configuration
|
66
|
+
|
67
|
+
Most functionality in Doc Repo can be configured. The full list of available
|
68
|
+
settings is:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
DocRepo.configure do |c|
|
72
|
+
# Repo settings
|
73
|
+
c.org = "YourOrg"
|
74
|
+
c.repo = "your_repo"
|
75
|
+
c.branch = "api-v2" # Default: "master"
|
76
|
+
c.doc_root = "/api-docs/rest" # Default: "docs"
|
77
|
+
|
78
|
+
# Content settings
|
79
|
+
c.doc_formats = %w[ # Default: %w[ .md .markdown .htm .html ]
|
80
|
+
.md
|
81
|
+
.mark
|
82
|
+
.txt
|
83
|
+
]
|
84
|
+
c.fallback_ext = ".mark" # Default: ".md"
|
85
|
+
|
86
|
+
# Cache settings
|
87
|
+
c.cache_store = Rails.cache # Default: DocRepo::NullCache.instance
|
88
|
+
c.cache_options = { # Default: {}
|
89
|
+
namespace: :docs,
|
90
|
+
expires_in: 36.hours,
|
91
|
+
}
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
When a request is made for a URI with an extension not listed in `doc_formats`
|
96
|
+
the `redirect` handler will be called without making a remote request.
|
97
|
+
|
98
|
+
### Error Handling
|
99
|
+
|
100
|
+
When no error handling is configured errors are raised:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
def show
|
104
|
+
DocRepo.request(params[:slug]) do |on|
|
105
|
+
on.complete do |doc|
|
106
|
+
# Do something with the document
|
107
|
+
end
|
108
|
+
|
109
|
+
on.redirect do |target|
|
110
|
+
# The asset exists else where and should be requested directly
|
111
|
+
end
|
112
|
+
end
|
113
|
+
rescue DocRepo::Error => error
|
114
|
+
# Handle the error
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
118
|
+
However, errors such as a missing document may be more common and behavior
|
119
|
+
handling should be treated differently. An example of this is a Rails app which
|
120
|
+
pulls the document name from the URL. When someone mistypes the URL that isn't
|
121
|
+
really an internal error.
|
122
|
+
|
123
|
+
Doc Repo provides an alternative interface to avoid control flow by exception.
|
124
|
+
This interface also allows separating behavior for the common missing document
|
125
|
+
from other error cases:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
DocRepo.request(params[:slug]) do |on|
|
129
|
+
on.complete do |doc|
|
130
|
+
# Do something with the document
|
131
|
+
end
|
132
|
+
|
133
|
+
on.redirect do |target|
|
134
|
+
# The asset exists else where and should be requested directly
|
135
|
+
end
|
136
|
+
|
137
|
+
on.not_found do |error|
|
138
|
+
# Handle the missing document
|
139
|
+
end
|
140
|
+
|
141
|
+
on.error do |error|
|
142
|
+
# Handle the error
|
143
|
+
end
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
When the `not_found` handler is left undefined the defined `error` handler will
|
148
|
+
be called. When this is not defined the default `raise` behavior is used.
|
149
|
+
|
150
|
+
### Caching
|
151
|
+
|
152
|
+
By default no caching is enabled. Specifically the default configuration uses a
|
153
|
+
null cache which results in all requests being sent to the remote origin
|
154
|
+
server. Custom cache stores can configured through the `cache_store`
|
155
|
+
configuration setting. In order for Doc Repo to work with the custom cache it
|
156
|
+
must implement the following APIs (existing Rails cache stores implement
|
157
|
+
these):
|
158
|
+
|
159
|
+
- `fetch(key, options = {}, &block)`
|
160
|
+
- `write(key, value, options = {})`
|
161
|
+
|
162
|
+
Any configured `cache_options` are provided directly to the `cache_store` for
|
163
|
+
all `fetch` and `write` calls.
|
164
|
+
|
165
|
+
When a custom cache store is configured it will be used as an internal local
|
166
|
+
HTTP cache. This HTTP cache will prevent remote origin requests when possible.
|
167
|
+
This is accomplished by serving content from local cache as long as the cache
|
168
|
+
is valid per the cache store. Additionally, Doc Repo supports a basic
|
169
|
+
understanding of general HTTP cache through the
|
170
|
+
[`Expires`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires)([RFC
|
171
|
+
7234](https://tools.ietf.org/html/rfc7234#section-5.3)) header.
|
172
|
+
|
173
|
+
When a local HTTP cache has expired according to the `Expires` header, but is
|
174
|
+
still valid in `cache_store`, a conditional `GET` request will be made to the
|
175
|
+
origin server. Any [`ETag`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag)([RFC
|
176
|
+
7232](https://tools.ietf.org/html/rfc7232#section-2.3)) or
|
177
|
+
[`Last-Modified`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified)([RFC
|
178
|
+
7232](https://tools.ietf.org/html/rfc7232#section-2.2)) headers originally
|
179
|
+
provided by the origin server will be sent in the request through
|
180
|
+
[`If-None-Match`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match)([RFC
|
181
|
+
7232](https://tools.ietf.org/html/rfc7232#section-3.2)) and
|
182
|
+
[`If-Modified-Since`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since)([RFC
|
183
|
+
7232](https://tools.ietf.org/html/rfc7232#section-3.3)) headers respectively.
|
184
|
+
|
185
|
+
Based on the response either the existing cache will be refreshed (i.e. in
|
186
|
+
response to a `304 Not Modified`) or replaced (i.e. in response to a `200 OK`).
|
187
|
+
This will cause the local HTTP cache to be re-written to the `cache_store`.
|
188
|
+
|
189
|
+
### Rails
|
190
|
+
|
191
|
+
We suggest creating a controller to render the documentation pages. A simple
|
192
|
+
implementation may look like the following:
|
42
193
|
|
43
194
|
```ruby
|
44
195
|
class DocsController < ApplicationController
|
@@ -49,20 +200,135 @@ class DocsController < ApplicationController
|
|
49
200
|
end
|
50
201
|
|
51
202
|
def show
|
52
|
-
DocRepo.
|
53
|
-
|
54
|
-
|
203
|
+
DocRepo.request(params[:slug]) do |on|
|
204
|
+
on.complete do |target|
|
205
|
+
@doc = doc
|
206
|
+
fresh_when @doc
|
207
|
+
end
|
55
208
|
|
56
|
-
|
57
|
-
|
209
|
+
on.redirect do |target|
|
210
|
+
redirect_to target.location, status: target.code
|
211
|
+
end
|
212
|
+
|
213
|
+
on.not_found do |error|
|
214
|
+
logger.warn "Not Found (URI=#{error.uri})"
|
215
|
+
render file: "public/404.html", status: :not_found, layout: false
|
216
|
+
end
|
58
217
|
end
|
59
|
-
rescue DocRepo::NotFound
|
60
|
-
raise ActionController::RoutingError.new('Not Found')
|
61
218
|
end
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
222
|
+
#### Rendering and Views
|
223
|
+
|
224
|
+
By default all `DocRepo::Doc` instances will generate safe HTML when provided
|
225
|
+
to `render` as the following types `:html`, `:plain`, and `:body`:
|
62
226
|
|
227
|
+
```ruby
|
228
|
+
DocRepo.request(params[:slug]) do |on|
|
229
|
+
on.complete do |doc|
|
230
|
+
# These two lines are equivalent
|
231
|
+
render html: doc.to_html.html_safe
|
232
|
+
render html: doc
|
233
|
+
|
234
|
+
# As are these
|
235
|
+
render plain: doc.to_html.html_safe
|
236
|
+
render plain: doc
|
237
|
+
end
|
63
238
|
end
|
64
239
|
```
|
65
240
|
|
241
|
+
For those documents which are written in markdown, if you wish to provide a way
|
242
|
+
to display the raw markdown you will need to explicitly provide it through
|
243
|
+
`DocRepo::Doc#content`:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
DocRepo.request(params[:slug]) do |on|
|
247
|
+
on.complete do |doc|
|
248
|
+
respond_to do |format|
|
249
|
+
format.html { render html: doc }
|
250
|
+
|
251
|
+
format.text { render plain: doc.content }
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
```
|
256
|
+
|
257
|
+
Inside of a view you will need to call `to_html`, `content` or `to_text` as
|
258
|
+
appropriate:
|
259
|
+
|
260
|
+
```erb
|
261
|
+
<%== doc.to_html %>
|
262
|
+
```
|
263
|
+
|
264
|
+
```erb
|
265
|
+
<%= doc.to_html.html_safe %>
|
266
|
+
```
|
267
|
+
|
268
|
+
#### View Caches and Conditional `GET` Support
|
269
|
+
|
270
|
+
The above mentioned caching behavior does not hook into the Rails view cache
|
271
|
+
nor the conditional `GET` request/response interfaces. However, `DocRepo::Doc`
|
272
|
+
instances provided to the `complete` handler do implement the necessary
|
273
|
+
interfaces.
|
274
|
+
|
275
|
+
You can explicitly define how to handle conditional `GET` through `stale?` or
|
276
|
+
`fresh_when`:
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
DocRepo.request(params[:slug]) do |on|
|
280
|
+
on.complete do |doc|
|
281
|
+
@doc = doc
|
282
|
+
fresh_when strong_etag: doc.cache_key_with_version, last_modified: doc.last_modified
|
283
|
+
end
|
284
|
+
end
|
285
|
+
```
|
286
|
+
|
287
|
+
Alternatively, you can provide the document instance directly:
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
DocRepo.request(params[:slug]) do |on|
|
291
|
+
on.complete do |doc|
|
292
|
+
@doc = doc
|
293
|
+
fresh_when @doc
|
294
|
+
end
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
298
|
+
This also applies to view caches:
|
299
|
+
|
300
|
+
```erb
|
301
|
+
<% cache @doc do %>
|
302
|
+
<%== @doc.to_html %>
|
303
|
+
<% end %>
|
304
|
+
```
|
305
|
+
|
306
|
+
#### Rails 5.1 and Earlier Cache Keys
|
307
|
+
|
308
|
+
The gem will attempt to check the Rails version when it is loaded and the
|
309
|
+
`Rails` module is defined. When it detects a version prior to 5.2 it will load
|
310
|
+
a patch which retains the legacy behavior of `DocRepo::Doc#cache_key`
|
311
|
+
containing version information. On theses versions of Rails
|
312
|
+
`DocRepo::Doc#cache_key_with_version` will simply be an alias for `cache_key`.
|
313
|
+
|
314
|
+
#### Rails 5.2 Recyclable View Caches
|
315
|
+
|
316
|
+
Support for this feature is built-in. The default implementation for
|
317
|
+
`DocRepo::Doc#cache_key` does not include the version. Additionally,
|
318
|
+
`DocRepo::Doc#cache_key_with_version` is already available to provide a
|
319
|
+
versioned implementation. This means Rails view caches can be recycled while
|
320
|
+
conditional `GET` calls through `fresh_when` and `stale?` continue to behave as
|
321
|
+
expected.
|
322
|
+
|
323
|
+
While we do not suggest it, if you wish to explicitly retain the legacy
|
324
|
+
`cache_key` behavior then you will need to load it through an initializer:
|
325
|
+
|
326
|
+
```ruby
|
327
|
+
# config/initializers/doc_repo.rb
|
328
|
+
require 'doc_repo/rails/legacy_versioned_cache'
|
329
|
+
```
|
330
|
+
|
331
|
+
|
66
332
|
## Contributing
|
67
333
|
|
68
334
|
1. Fork it
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Run from the command line: bundle exec ruby benchmarks/reject_extract.rb
|
2
|
+
require 'kalibera'
|
3
|
+
require 'benchmark/ips'
|
4
|
+
require 'digest'
|
5
|
+
|
6
|
+
MESSAGE = <<~MESSAGE
|
7
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pulvinar
|
8
|
+
magna metus, vel facilisis sapien mollis sit amet. Pellentesque bibendum mi a
|
9
|
+
mi malesuada dignissim. Donec pharetra purus at finibus pretium. Sed vitae
|
10
|
+
odio lobortis, bibendum mi id, eleifend mauris. Integer varius molestie
|
11
|
+
bibendum.
|
12
|
+
MESSAGE
|
13
|
+
|
14
|
+
Benchmark.ips do |x|
|
15
|
+
x.config stats: :bootstrap, confidence: 95
|
16
|
+
|
17
|
+
x.report("MD5") do
|
18
|
+
Digest::MD5.hexdigest MESSAGE
|
19
|
+
end
|
20
|
+
|
21
|
+
x.report("RMD160") do
|
22
|
+
Digest::RMD160.hexdigest MESSAGE
|
23
|
+
end
|
24
|
+
|
25
|
+
x.report("SHA1") do
|
26
|
+
Digest::SHA1.hexdigest MESSAGE
|
27
|
+
end
|
28
|
+
|
29
|
+
x.report("SHA2") do
|
30
|
+
Digest::SHA2.hexdigest MESSAGE
|
31
|
+
end
|
32
|
+
|
33
|
+
x.compare!
|
34
|
+
end
|
35
|
+
|
36
|
+
__END__
|
37
|
+
|
38
|
+
Warming up --------------------------------------
|
39
|
+
MD5 32.125k i/100ms
|
40
|
+
RMD160 21.157k i/100ms
|
41
|
+
SHA1 30.969k i/100ms
|
42
|
+
SHA2 17.655k i/100ms
|
43
|
+
Calculating -------------------------------------
|
44
|
+
MD5 402.545k (± 0.9%) i/s - 2.024M in 5.035449s
|
45
|
+
RMD160 239.013k (± 0.7%) i/s - 1.206M in 5.049813s
|
46
|
+
SHA1 402.324k (± 0.8%) i/s - 2.013M in 5.008906s
|
47
|
+
SHA2 199.182k (± 0.8%) i/s - 1.006M in 5.058252s
|
48
|
+
with 95.0% confidence
|
49
|
+
|
50
|
+
Comparison:
|
51
|
+
MD5: 402545.1 i/s
|
52
|
+
SHA1: 402323.6 i/s - same-ish: difference falls within error
|
53
|
+
RMD160: 239013.3 i/s - 1.68x (± 0.02) slower
|
54
|
+
SHA2: 199182.2 i/s - 2.02x (± 0.03) slower
|
55
|
+
with 95.0% confidence
|
data/doc_repo.gemspec
CHANGED
@@ -18,13 +18,13 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.required_ruby_version = '~> 2.
|
21
|
+
spec.required_ruby_version = '~> 2.3'
|
22
22
|
|
23
|
-
spec.add_dependency "rouge", "~> 1
|
23
|
+
spec.add_dependency "rouge", "~> 2.1"
|
24
24
|
spec.add_dependency "redcarpet", "~> 3.2"
|
25
25
|
|
26
26
|
spec.add_development_dependency "bundler", "~> 1.6"
|
27
|
-
spec.add_development_dependency "rake", "~>
|
28
|
-
spec.add_development_dependency "rspec", "~> 3.
|
29
|
-
spec.add_development_dependency "webmock", "~>
|
27
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.6"
|
29
|
+
spec.add_development_dependency "webmock", "~> 3.0"
|
30
30
|
end
|
@@ -1,11 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'singleton'
|
3
|
+
|
1
4
|
module DocRepo
|
5
|
+
class NullCache
|
6
|
+
include Singleton
|
7
|
+
instance.freeze
|
8
|
+
|
9
|
+
def fetch(name, options = nil)
|
10
|
+
yield
|
11
|
+
end
|
12
|
+
|
13
|
+
def write(name, value, options = nil)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
2
17
|
class Configuration
|
3
|
-
|
18
|
+
Settings = Module.new
|
19
|
+
include Settings
|
20
|
+
|
21
|
+
def self.setting(name, default: nil)
|
22
|
+
attr_writer name
|
23
|
+
Settings.module_exec do
|
24
|
+
attr_reader name
|
25
|
+
end
|
26
|
+
if default
|
27
|
+
define_method(name) do
|
28
|
+
super() || default
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
private_class_method :setting
|
33
|
+
|
34
|
+
setting :branch, default: "master"
|
35
|
+
|
36
|
+
setting :cache_options, default: {}
|
37
|
+
|
38
|
+
setting :cache_store, default: NullCache.instance
|
39
|
+
|
40
|
+
setting :doc_formats, default: %w[
|
41
|
+
.md
|
42
|
+
.markdown
|
43
|
+
.htm
|
44
|
+
.html
|
45
|
+
].freeze
|
46
|
+
|
47
|
+
setting :doc_root, default: "docs"
|
48
|
+
|
49
|
+
setting :fallback_ext, default: ".md"
|
50
|
+
|
51
|
+
setting :org
|
52
|
+
|
53
|
+
setting :repo
|
4
54
|
|
5
55
|
def initialize
|
6
|
-
|
7
|
-
|
8
|
-
|
56
|
+
yield self if block_given?
|
57
|
+
end
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
settings = to_h.map { |setting, value| "#{setting}=#{value.inspect}" }
|
61
|
+
.join(", ")
|
62
|
+
"#<#{self.class}:0x%014x #{settings}>" % (object_id << 1)
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_h
|
66
|
+
Settings.instance_methods(_include_super = false)
|
67
|
+
.each_with_object({}) { |setting, hash|
|
68
|
+
hash[setting] = public_send(setting)
|
69
|
+
}
|
9
70
|
end
|
10
71
|
end
|
11
72
|
end
|
data/lib/doc_repo/doc.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'converters/markdown_parser'
|
3
|
+
require 'digest'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
module DocRepo
|
7
|
+
class Doc
|
8
|
+
include HttpResult
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
module Cache
|
12
|
+
attr_reader :cache_key, :cache_version
|
13
|
+
|
14
|
+
def cache_control
|
15
|
+
http['Cache-Control']
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache_key_with_version
|
19
|
+
"#{cache_key}-#{cache_version}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
include Cache
|
23
|
+
|
24
|
+
def initialize(uri, http_response)
|
25
|
+
@http = http_response
|
26
|
+
init_result_readers(uri, @http.code)
|
27
|
+
@cache_key = uri.dup.freeze
|
28
|
+
|
29
|
+
# The Github raw server currently provides ETags for content. It's
|
30
|
+
# possible a change in servers/APIs may cause some content to no longer
|
31
|
+
# produce ETags. Additionally, for app cache versioning we have a default
|
32
|
+
# opinion that only the raw content important.
|
33
|
+
#
|
34
|
+
# For these reasons we calculate the cache version based solely on the
|
35
|
+
# raw content, ignoring any provided ETags or lack of one. As this cache
|
36
|
+
# version is meant to be used for general change comparisons we are not
|
37
|
+
# overly concerned with cryptographic level collision prevention or
|
38
|
+
# security. We'd rather have something reasonably fast to keep overhead
|
39
|
+
# down.
|
40
|
+
#
|
41
|
+
# As MD5 and SHA1 are roughly equivalent in speed (see
|
42
|
+
# benchmarks/digests.rb) we choose the latter as it provides a reduction
|
43
|
+
# in collisions at almost no additional cost.
|
44
|
+
@cache_version = Digest::SHA1.hexdigest(@http.body.to_s).freeze
|
45
|
+
|
46
|
+
# NOTE: Not set by Github raw site - we include it for future proofing
|
47
|
+
@last_modified = Time.httpdate(@http['Last-Modified']).freeze rescue nil
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :last_modified
|
51
|
+
|
52
|
+
attr_reader :http
|
53
|
+
private :http
|
54
|
+
|
55
|
+
def content
|
56
|
+
http.body
|
57
|
+
end
|
58
|
+
|
59
|
+
def content_type
|
60
|
+
# NOTE: The Github raw site does not respond with anything other than
|
61
|
+
# `text/plain` for general HTTP errors.
|
62
|
+
http['Content-Type']
|
63
|
+
end
|
64
|
+
|
65
|
+
def success?
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_html(_options = {})
|
70
|
+
Converters::MarkdownParser.new(
|
71
|
+
extensions: %i[
|
72
|
+
no_intra_emphasis
|
73
|
+
tables
|
74
|
+
fenced_code_blocks
|
75
|
+
autolink
|
76
|
+
strikethrough
|
77
|
+
lax_spacing
|
78
|
+
superscript
|
79
|
+
with_toc_data
|
80
|
+
]
|
81
|
+
).convert(content.to_s)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|