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