This is a major version change. Consult the README.md for the full list of breaking changes.
Configuration
With this new version the following minimum configuration is still required:
DocRepo.configure do |c|
c.org = "YourOrg"
c.repo = "your_repo"
end
This expands the prior configuration adding the ability to control more of the response formats, where within the repo the docs are located, and add caching:
The main usage is still through the DocRepo module and follows a block style. Things have been renamed in an attempt to improve the API intent and not hide as much of the underlying abstraction. The following is an example of a Rails controller using this new API:
class DocsController < ApplicationController
def show
DocRepo.request(params[:slug]) do |on|
on.complete do |doc|
@doc = doc
end
on.redirect do |target|
redirect_to target.location, status: target.code
end
end
end
end
At a minimum complete and redirect handlers should be specified. Additionally, specific handlers for missing documents (via not_found) and general remote origin related errors (via error) may be specified:
class DocsController < ApplicationController
def show
DocRepo.request(params[:slug]) do |on|
on.complete do |doc|
@doc = doc
end
on.redirect do |target|
redirect_to target.location, status: target.code
end
on.not_found do |error|
logger.warn "Not Found (URI=#{error.uri})"
render file: "public/404.html", status: :not_found, layout: false
end
on.error do |error|
logger.error "#{error} (URI=#{error.uri})"
Bugsnag.notify error
@error = error
render :error, status: error.code
end
end
end
end
When the error handler is not specified the default behavior will be to raise the error which would have been provided to the block. Similarly, when the not_found handler is not specified it will fallback to the specified error or raising when that is not set.
Caching and Conditional GET Support
By default a null cache is configured. This will result in all requests being sent to the remote origin server. Custom cache stores can configured through cache_store. In order to work the custom cache must implement the following APIs (existing Rails cache stores implement these):
fetch(key, options = {}, &block)
write(key, value, options = {})
Any custom cache will be used as an internal HTTP cache. This HTTP cache will prevent remote origin requests when possible. Any configured cache_options are provided directly to the cache_store for all fetch and write calls.
To reduce the number of requests to the remote origin server HTTP responses will be served from cache for as long as the cache is valid per the cache store. Additionally, this supports a basic understanding of HTTP cache through the Expires(RFC 7234) header. When a local HTTP cache has expired, but is still valid in cache_store, a conditional GET request will be made to the origin server. Any ETag(RFC 7232) or Last-Modified(RFC 7232) headers originally provided by the origin server will be sent in the request through If-None-Match(RFC 7232) and If-Modified-Since(RFC 7232) headers respectively.
Based on the response either the existing cache will be refreshed (i.e. in response to a 304 Not Modified) or replaced (i.e. in response to a 200 OK). This will cause the local HTTP cache to be re-written to the cache_store.
Rails Views, Cache, and Conditional GET Support
By default all DocRepo::Doc instances will generate HTML when provided to render for the following types :html, :plain, and :body:
DocRepo.request(params[:slug]) do |on|
on.complete do |doc|
# These two lines are equivalent
render html: doc.to_html.html_safe
render html: doc
# As are these
render plain: doc.to_html.html_safe
render plain: doc
end
end
For those documents which are written in markdown, if you wish to provide a way to display the raw markdown you will need to explicitly provide it through DocRepo::Doc#content:
DocRepo.request(params[:slug]) do |on|
on.complete do |doc|
respond_to do |format|
format.html { render html: doc }
format.text { render plain: doc.content }
end
end
end
Inside of a view you will need to call to_html, content or to_text as appropriate:
<%== doc.to_html %>
<%= doc.to_html.html_safe %>
The above mentioned caching behavior does not hook into the Rails view cache nor request/response conditional GET interfaces. However, DocRepo::Doc instances provided to the complete handler do implement the necessary interfaces.
You can explicitly define how to handle conditional GET through stale? or fresh_when:
DocRepo.request(params[:slug]) do |on|
on.complete do |doc|
@doc = doc
fresh_when strong_etag: doc.cache_key_with_version, last_modified: doc.last_modified
end
end
Alternatively, you can provide the document instance. When Rails is available the necessary interfaces are defined so that this just works:
DocRepo.request(params[:slug]) do |on|
on.complete do |doc|
fresh_when @doc = doc
end
end
This also applies to view caches as well:
<% cache @doc do %>
<%== @doc.to_html %>
<% end %>
Rails 5.1 and Earlier Cache Keys
The gem will attempt to check the Rails version when it is loaded and the Rails module is defined. When it detects a version prior to 5.2 it will load a patch which retains the legacy behavior of DocRepo::Doc#cache_key containing version information. On theses versions of Rails DocRepo::Doc#cache_key_with_version will simply be an alias for cache_key.
Rails 5.2 Recyclable View Caches
Support for this feature is built-in. The default implementation for DocRepo::Doc#cache_key does not include the version. Additionally, DocRepo::Doc#cache_key_with_version is already available to provide a versioned implementation. This means Rails view caches can be recycled while conditional GET calls through fresh_when and stale? continue to behave as expected.
While we do not suggest it, if you wish to explicitly retain the legacy cache_key behavior then you will need to load it through an initializer:
Resolve #6
This is a major version change. Consult the
README.md
for the full list of breaking changes.Configuration
With this new version the following minimum configuration is still required:
This expands the prior configuration adding the ability to control more of the response formats, where within the repo the docs are located, and add caching:
Basic Usage
The main usage is still through the
DocRepo
module and follows a block style. Things have been renamed in an attempt to improve the API intent and not hide as much of the underlying abstraction. The following is an example of a Rails controller using this new API:At a minimum
complete
andredirect
handlers should be specified. Additionally, specific handlers for missing documents (vianot_found
) and general remote origin related errors (viaerror
) may be specified:When the
error
handler is not specified the default behavior will be to raise the error which would have been provided to the block. Similarly, when thenot_found
handler is not specified it will fallback to the specifiederror
or raising when that is not set.Caching and Conditional
GET
SupportBy default a null cache is configured. This will result in all requests being sent to the remote origin server. Custom cache stores can configured through
cache_store
. In order to work the custom cache must implement the following APIs (existing Rails cache stores implement these):fetch(key, options = {}, &block)
write(key, value, options = {})
Any custom cache will be used as an internal HTTP cache. This HTTP cache will prevent remote origin requests when possible. Any configured
cache_options
are provided directly to thecache_store
for allfetch
andwrite
calls.To reduce the number of requests to the remote origin server HTTP responses will be served from cache for as long as the cache is valid per the cache store. Additionally, this supports a basic understanding of HTTP cache through the
Expires
(RFC 7234) header. When a local HTTP cache has expired, but is still valid incache_store
, a conditionalGET
request will be made to the origin server. AnyETag
(RFC 7232) orLast-Modified
(RFC 7232) headers originally provided by the origin server will be sent in the request throughIf-None-Match
(RFC 7232) andIf-Modified-Since
(RFC 7232) headers respectively.Based on the response either the existing cache will be refreshed (i.e. in response to a
304 Not Modified
) or replaced (i.e. in response to a200 OK
). This will cause the local HTTP cache to be re-written to thecache_store
.Rails Views, Cache, and Conditional
GET
SupportBy default all
DocRepo::Doc
instances will generate HTML when provided torender
for the following types:html
,:plain
, and:body
:For those documents which are written in markdown, if you wish to provide a way to display the raw markdown you will need to explicitly provide it through
DocRepo::Doc#content
:Inside of a view you will need to call
to_html
,content
orto_text
as appropriate:The above mentioned caching behavior does not hook into the Rails view cache nor request/response conditional
GET
interfaces. However,DocRepo::Doc
instances provided to thecomplete
handler do implement the necessary interfaces.You can explicitly define how to handle conditional
GET
throughstale?
orfresh_when
:Alternatively, you can provide the document instance. When Rails is available the necessary interfaces are defined so that this just works:
This also applies to view caches as well:
Rails 5.1 and Earlier Cache Keys
The gem will attempt to check the Rails version when it is loaded and the
Rails
module is defined. When it detects a version prior to 5.2 it will load a patch which retains the legacy behavior ofDocRepo::Doc#cache_key
containing version information. On theses versions of RailsDocRepo::Doc#cache_key_with_version
will simply be an alias forcache_key
.Rails 5.2 Recyclable View Caches
Support for this feature is built-in. The default implementation for
DocRepo::Doc#cache_key
does not include the version. Additionally,DocRepo::Doc#cache_key_with_version
is already available to provide a versioned implementation. This means Rails view caches can be recycled while conditionalGET
calls throughfresh_when
andstale?
continue to behave as expected.While we do not suggest it, if you wish to explicitly retain the legacy
cache_key
behavior then you will need to load it through an initializer: