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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 719ad86ec23cfdb739553ff34cea62ea834a529b
4
- data.tar.gz: 6ac8ba2b6b0a5b32045f77a5622d6b552a2935ee
3
+ metadata.gz: 30a5493aa98e1c88a25bc6b485716d6e8aecf14b
4
+ data.tar.gz: 9ffdcb7925e8e3743dca930bfe03a46669305b4d
5
5
  SHA512:
6
- metadata.gz: f069ab4f8c94d1cbb1bed98d7025d0d6f80b3eb7748f9f9086ab80eef0907a14540c6c300f68c88624239d14506ab0782494f402b61372e9f7aff52eb619e92e
7
- data.tar.gz: fdbb092b9c9cd12fec33ac10a5e8e0c303e3e458df0c16df56784fd78d0d6d6967aae8e7fda69d3c05ac6b5284498c7b1a8ad2f038f8c32e0c57622879b4d211
6
+ metadata.gz: 0b1b66bdfe4e58c16bd0c173c65795c96ea5aaacb313376a92927a5bc8ca60d0d18032aca18db0259bcdb86400a0bd77d4949f33e5dafbc2e5d1549d9a54bd1b
7
+ data.tar.gz: 70ab80ee398d1491c22255206f41573a9268fe6adc87113a02269165dcd54973292797e10018c9b0c2ad86a3eb3f256e65f56e7e0a508a566ca6d038527e01c2
data/.gitignore CHANGED
@@ -18,3 +18,6 @@ mkmf.log
18
18
  # Ignore bundler related files
19
19
  /bundle/
20
20
  *.bundle
21
+
22
+ # Ignore RSpec runner related files
23
+ /spec/examples.txt
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.0
7
- - 2.1
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
@@ -7,3 +7,8 @@ group :debug do
7
7
  gem "pry-nav"
8
8
  gem "travis-lint", require: false
9
9
  end
10
+
11
+ group :benchmarks do
12
+ gem "kalibera", require: false
13
+ gem "benchmark-ips", require: false
14
+ end
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 your app.
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 proxy them through your app. This lets you render things in your app, customize the layout and access control -- but lets you update the docs without re-deploying.
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
- $ bundle
22
+ ```console
23
+ $ bundle
24
+ ```
19
25
 
20
26
  Or install it yourself as:
21
27
 
22
- $ gem install doc_repo
28
+ ```console
29
+ $ gem install doc_repo
30
+ ```
23
31
 
24
32
  ## Usage
25
33
 
26
- Create an initializer to configure Doc Repo, in rails this would live in `config/initializers/doc_repo.rb`:
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 Orgnization or User:
40
+ # GitHub Organization or User:
31
41
  c.org = "RadiusNetworks"
32
42
 
33
43
  # GitHub Repo:
34
- c.repo = "proximitykit-documentation"
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
- Create a controller to render the documentation pages. In Rails you might use something like this:
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.respond_with(params[:slug]) do |f|
53
- # Render the body:
54
- f.html {|body| render text: body, layout: "docs" }
203
+ DocRepo.request(params[:slug]) do |on|
204
+ on.complete do |target|
205
+ @doc = doc
206
+ fresh_when @doc
207
+ end
55
208
 
56
- # Redirect to images and assets:
57
- f.redirect {|url| redirect_to url }
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.0'
21
+ spec.required_ruby_version = '~> 2.3'
22
22
 
23
- spec.add_dependency "rouge", "~> 1.8"
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", "~> 10.0"
28
- spec.add_development_dependency "rspec", "~> 3.0"
29
- spec.add_development_dependency "webmock", "~> 1.0"
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
- attr_accessor :org, :repo, :branch
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
- @org = ENV['DOC_REPO_ORG']
7
- @repo = ENV['DOC_REPO_REPONAME']
8
- @branch = ENV['DOC_REPO_BRANCH'] || "master"
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
@@ -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
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DocRepo
4
+ Error = Class.new(StandardError)
5
+
6
+ class UnhandledAction < Error
7
+ def initialize(action, msg = nil)
8
+ super(msg)
9
+ @action = action.to_s
10
+ end
11
+
12
+ attr_reader :action
13
+ end
14
+ end