issue#566: removes markdown (#567)

- updates README
- updates UPGRADING
- adds changelog entry
- cleans up gemfile
This commit is contained in:
peter scholz 2017-01-13 18:26:48 +01:00 committed by LeFnord
parent a6cb8babd4
commit 51d4ff6622
14 changed files with 32 additions and 408 deletions

View File

@ -2,6 +2,8 @@
#### Features #### Features
* [#567](https://github.com/ruby-grape/grape-swagger/pull/567): Issue#566: removes markdown - [@LeFnord](https://github.com/LeFnord).
* Your contribution here. * Your contribution here.
#### Fixes #### Fixes

View File

@ -15,7 +15,6 @@ gem ENV['MODEL_PARSER'] if ENV.key?('MODEL_PARSER')
group :development, :test do group :development, :test do
gem 'bundler' gem 'bundler'
gem 'kramdown'
gem 'pry', platforms: [:mri] gem 'pry', platforms: [:mri]
gem 'pry-byebug', platforms: [:mri] gem 'pry-byebug', platforms: [:mri]
gem 'rack' gem 'rack'
@ -23,11 +22,8 @@ group :development, :test do
gem 'rack-test' gem 'rack-test'
gem 'rake' gem 'rake'
gem 'rdoc' gem 'rdoc'
gem 'redcarpet', '< 3.4', platforms: [:mri]
gem 'rouge', platforms: [:mri]
gem 'rspec', '~> 3.0' gem 'rspec', '~> 3.0'
gem 'rubocop', '~> 0.40' gem 'rubocop', '~> 0.40'
gem 'shoulda'
end end
group :test do group :test do
gem 'grape-entity' gem 'grape-entity'

View File

@ -16,7 +16,7 @@
* [Routes Configuration](#routes) * [Routes Configuration](#routes)
* [Using Grape Entities](#grape-entity) * [Using Grape Entities](#grape-entity)
* [Securing the Swagger UI](#oauth) * [Securing the Swagger UI](#oauth)
* [Markdown](#md_usage) * [Markdown (deprecated)](#md_usage)
* [Example](#example) * [Example](#example)
* [Rake Tasks](#rake) * [Rake Tasks](#rake)
@ -196,7 +196,6 @@ end
* [add_base_path](#add_base_path) * [add_base_path](#add_base_path)
* [add_version](#add_version) * [add_version](#add_version)
* [doc_version](#doc_version) * [doc_version](#doc_version)
* [markdown](#markdown)
* [endpoint_auth_wrapper](#endpoint_auth_wrapper) * [endpoint_auth_wrapper](#endpoint_auth_wrapper)
* [swagger_endpoint_guard](#swagger_endpoint_guard) * [swagger_endpoint_guard](#swagger_endpoint_guard)
* [token_owner](#token_owner) * [token_owner](#token_owner)
@ -268,18 +267,8 @@ add_swagger_documentation \
``` ```
<a name="markdown" /> <a name="markdown" />
#### markdown: #### markdown: (deprecated)
Allow markdown in `detail`, default is `false`. (disabled) See [below](#md_usage) for details. OAPI accepts GFM for descriptions
```ruby
add_swagger_documentation \
markdown: GrapeSwagger::Markdown::KramdownAdapter.new
```
or alternative
```ruby
add_swagger_documentation \
markdown: GrapeSwagger::Markdown::RedcarpetAdapter.new
```
<a name="endpoint_auth_wrapper" /> <a name="endpoint_auth_wrapper" />
#### endpoint_auth_wrapper: #### endpoint_auth_wrapper:
@ -1087,77 +1076,11 @@ The lambda is checking whether the user is authenticated (if not, the token_owne
role - only admins can see this endpoint. role - only admins can see this endpoint.
<a name="md_usage" /> <a name="md_usage" />
## Markdown in Detail ## Markdown in Detail (deprecated)
The grape-swagger gem allows you to add an explanation in markdown in the detail field. Which would result in proper formatted markdown in Swagger UI. Usage of option `markdown` won't no longer be supported,
Grape-swagger uses adapters for several markdown formatters. It includes adapters for [kramdown](http://kramdown.rubyforge.org) (kramdown [syntax](http://kramdown.rubyforge.org/syntax.html)) and [redcarpet](https://github.com/vmg/redcarpet). cause OAPI accepts [GFM](https://help.github.com/articles/github-flavored-markdown) and plain text.
The adapters are packed in the GrapeSwagger::Markdown modules. We do not include the markdown gems in our gemfile, so be sure to include or install the depended gems. (see: [description of `Info`](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/2.0.md#info-object))
To use it, add a new instance of the adapter to the markdown options of `add_swagger_documentation`, such as:
```ruby
add_swagger_documentation \
markdown: GrapeSwagger::Markdown::KramdownAdapter.new(options)
```
and write your route details in GFM, examples could be find in [details spec](blob/master/spec/swagger_v2/api_swagger_v2_detail_spec.rb)
#### Kramdown
If you want to use kramdown as markdown formatter, you need to add kramdown to your gemfile.
```ruby
gem 'kramdown'
```
Configure your api documentation route with:
```ruby
add_swagger_documentation \
markdown: GrapeSwagger::Markdown::KramdownAdapter.new(options)
```
#### Redcarpet
As alternative you can use [redcarpet](https://github.com/vmg/redcarpet) as formatter, you need to include redcarpet in your gemspec. If you also want to use [rouge](https://github.com/jneen/rouge) as syntax highlighter you also need to include it.
```ruby
gem 'redcarpet'
gem 'rouge'
```
Configure your api documentation route with:
```ruby
add_swagger_documentation(
markdown: GrapeSwagger::Markdown::RedcarpetAdapter.new(render_options: { highlighter: :rouge })
)
```
Alternatively you can disable rouge by adding `:none` as highlighter option. You can add redcarpet extensions and render options trough the `extenstions:` and `render_options:` parameters.
#### Custom markdown formatter
You can also add your custom adapter for your favourite markdown formatter, as long it responds to the method `markdown(text)` and it formats the given text.
```ruby
module API
class FancyAdapter
attr_reader :adapter
def initialize(options)
require 'superbmarkdownformatter'
@adapter = SuperbMarkdownFormatter.new options
end
def markdown(text)
@adapter.render_supreme(text)
end
end
add_swagger_documentation markdown: FancyAdapter.new(no_links: true)
end
```
<a="example" /> <a="example" />

View File

@ -1,5 +1,11 @@
## Upgrading Grape-swagger ## Upgrading Grape-swagger
### Upgrading to >= 0.26.0
Usage of option `markdown` won't no longer be supported,
cause OAPI accepts [GFM](https://help.github.com/articles/github-flavored-markdown) and plain text.
(see: [description of `Info`](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/2.0.md#info-object))
### Upgrading to >= 0.25.2 ### Upgrading to >= 0.25.2
Avoids ambiguous documentation of array parameters, Avoids ambiguous documentation of array parameters,

View File

@ -7,9 +7,6 @@ require 'grape-swagger/errors'
require 'grape-swagger/doc_methods' require 'grape-swagger/doc_methods'
require 'grape-swagger/model_parsers' require 'grape-swagger/model_parsers'
require 'grape-swagger/markdown/kramdown_adapter'
require 'grape-swagger/markdown/redcarpet_adapter'
module GrapeSwagger module GrapeSwagger
class << self class << self
def model_parsers def model_parsers

View File

@ -27,6 +27,9 @@ module GrapeSwagger
end end
def setup(options) def setup(options)
# FIXME: move out after next minor is released
GrapeSwagger::Errors::SwaggerSpecDeprecated.tell!(options[:markdown]) if options.key?(:markdown)
options = defaults.merge(options) options = defaults.merge(options)
# options could be set on #add_swagger_documentation call, # options could be set on #add_swagger_documentation call,
@ -93,7 +96,6 @@ module GrapeSwagger
base_path: nil, base_path: nil,
add_base_path: false, add_base_path: false,
add_version: true, add_version: true,
markdown: false,
hide_documentation_path: true, hide_documentation_path: true,
format: :json, format: :json,
authorizations: nil, authorizations: nil,

View File

@ -105,12 +105,12 @@ module Grape
def method_object(route, options, path) def method_object(route, options, path)
method = {} method = {}
method[:summary] = summary_object(route) method[:summary] = summary_object(route)
method[:description] = description_object(route, options[:markdown]) method[:description] = description_object(route)
method[:produces] = produces_object(route, options[:produces] || options[:format]) method[:produces] = produces_object(route, options[:produces] || options[:format])
method[:consumes] = consumes_object(route, options[:format]) method[:consumes] = consumes_object(route, options[:format])
method[:parameters] = params_object(route) method[:parameters] = params_object(route)
method[:security] = security_object(route) method[:security] = security_object(route)
method[:responses] = response_object(route, options[:markdown]) method[:responses] = response_object(route)
method[:tags] = route.options.fetch(:tags, tag_object(route)) method[:tags] = route.options.fetch(:tags, tag_object(route))
method[:operationId] = GrapeSwagger::DocMethods::OperationId.build(route, path) method[:operationId] = GrapeSwagger::DocMethods::OperationId.build(route, path)
method.delete_if { |_, value| value.blank? } method.delete_if { |_, value| value.blank? }
@ -130,10 +130,9 @@ module Grape
summary summary
end end
def description_object(route, markdown) def description_object(route)
description = route.description if route.description.present? description = route.description if route.description.present?
description = route.options[:detail] if route.options.key?(:detail) description = route.options[:detail] if route.options.key?(:detail)
description = markdown.markdown(description.to_s).chomp if markdown
description description
end end
@ -178,7 +177,7 @@ module Grape
parameters parameters
end end
def response_object(route, markdown) def response_object(route)
codes = (route.http_codes || route.options[:failure] || []) codes = (route.http_codes || route.options[:failure] || [])
codes = apply_success_codes(route) + codes codes = apply_success_codes(route) + codes
@ -199,7 +198,7 @@ module Grape
next unless !response_model.start_with?('Swagger_doc') && next unless !response_model.start_with?('Swagger_doc') &&
((@definitions[response_model] && value[:code].to_s.start_with?('2')) || value[:model]) ((@definitions[response_model] && value[:code].to_s.start_with?('2')) || value[:model])
@definitions[response_model][:description] = description_object(route, markdown) @definitions[response_model][:description] = description_object(route)
# TODO: proof that the definition exist, if model isn't specified # TODO: proof that the definition exist, if model isn't specified
reference = { '$ref' => "#/definitions/#{response_model}" } reference = { '$ref' => "#/definitions/#{response_model}" }
memo[value[:code]][:schema] = if route.options[:is_array] memo[value[:code]][:schema] = if route.options[:is_array]

View File

@ -1,12 +1,13 @@
module GrapeSwagger module GrapeSwagger
module Errors module Errors
class MarkdownDependencyMissingError < StandardError
def initialize(missing_gem)
super("Missing required dependency: #{missing_gem}")
end
end
class UnregisteredParser < StandardError; end class UnregisteredParser < StandardError; end
class SwaggerSpec < StandardError; end class SwaggerSpec < StandardError; end
class SwaggerSpecDeprecated < SwaggerSpec
class << self
def tell!(what)
warn "[GrapeSwagger] usage of #{what} is deprecated"
end
end
end
end end
end end

View File

@ -1,37 +0,0 @@
module GrapeSwagger
class Markdown
class KramdownAdapter
attr_reader :options
###
# Initializes the kramdown adapter with options.
# See kramdown documentation what options can be passed.
# Default it uses Github flavoured markup as input and won't use coderay as converter for syntax highlighting.
# config: an hash of configuration options to be passed to the kramdown.
# usage:
# Add the kramdown gem to your gemfile or run:
# $ (sudo) gem install kramdown
#
# Then pass a new instance of GrapeSwagger::Markdown::KramdownAdapter as markdown option.
###
def initialize(config = {})
require 'kramdown'
defaults = {
input: 'GFM',
enable_coderay: false
}
@options = defaults.merge(config)
rescue LoadError
raise GrapeSwagger::Errors::MarkdownDependencyMissingError, 'kramdown'
end
###
# marks down the given text to html format.
# text: The text to be formatted.
###
def markdown(text)
Kramdown::Document.new(text, @options).to_html
end
end
end
end

View File

@ -1,92 +0,0 @@
module GrapeSwagger
class Markdown
class RedcarpetAdapter
module RenderWithoutSyntaxHighlighter
require 'cgi'
def block_code(code, language)
language ||= 'text'
"<div class=\"code_highlight\">
<pre><code class=\"highlight #{language}\">#{CGI.escapeHTML(code)}</code></pre>
</div>"
end
end
attr_reader :extension_options
attr_reader :render_options
###
# Initializes the redcarpet adapter with markup options.
# See redcarpet documentation what options can be passed.
# Default it uses fenced_code_blocks, autolinks and rouge as syntax highlighter.
# To configure an highlighter add {highlighter: :value} to the extentions hash.
# Currently supported highlighters:
# :rouge
#
# extensions: an hash of configuration options to be passed to markdown.
# render_options: an hash of configuration options to be passed to renderer.
#
# usage:
# Add the redcarpet gem to your gemfile or run:
# $ (sudo) gem install redcarpet
# when you want to have rouge as syntax highlighter add rouge to the gemfile or run:
# $ (sudo) gem install rouge
#
# GrapeSwagger::Markdown::RedcarpetAdapter.new({highlighter: :none},{no_links: true})
# will use no syntax highlighter and won't render links.
###
def initialize(options = {})
require 'redcarpet'
extentions_defaults = {
fenced_code_blocks: true,
autolink: true
}
render_defaults = { highlighter: :rouge }
@extension_options = extentions_defaults.merge(options.fetch(:extensions, {}))
@render_options = render_defaults.merge(options.fetch(:render_options, {}))
@renderer = new_redcarpet_renderer(@render_options.delete(:highlighter)).new(@render_options)
@markdown = Redcarpet::Markdown.new(@renderer, @extension_options)
rescue LoadError
raise GrapeSwagger::Errors::MarkdownDependencyMissingError, 'redcarpet'
end
###
# Marks down the given text to html format.
###
def markdown(text)
@markdown.render(text)
end
private
###
# Creates a new redcarpet renderer based on the highlighter given.
#
# render_options: options passed to the renderer.
#
# usage:
# new_redcarpet_renderer(:rouge) # uses rouge as highlighter.
# new_redcarpet_renderer # no highlight plugin
###
def new_redcarpet_renderer(syntax_highlighter = nil)
case syntax_highlighter
when :rouge
begin
Class.new(Redcarpet::Render::HTML) do
require 'rouge'
require 'rouge/plugins/redcarpet'
include Rouge::Plugins::Redcarpet
end
rescue LoadError
raise GrapeSwagger::Errors::MarkdownDependencyMissingError, 'rouge'
end
else
Class.new(Redcarpet::Render::HTML) do
include RenderWithoutSyntaxHighlighter
end
end
end
end
end
end

View File

@ -1,31 +0,0 @@
require 'spec_helper'
describe GrapeSwagger::Markdown::KramdownAdapter do
context 'initialization' do
it 'uses GFM as default input and disable coderay' do
adapter = GrapeSwagger::Markdown::KramdownAdapter.new
expect(adapter.options).to eq(input: 'GFM', enable_coderay: false)
end
it 'overrides default values' do
options = { input: 'kramdown', enable_coderay: true }
adapter = GrapeSwagger::Markdown::KramdownAdapter.new options
expect(adapter.options).to eq(options)
end
end
context 'markdown' do
it 'marks down with the configured options' do
text = '# hello world'
options = { input: 'GFM', enable_coderay: true, auto_ids: false, hard_wrap: true }
expect(GrapeSwagger::Markdown::KramdownAdapter).to receive(:new).with(options).and_call_original
output = GrapeSwagger::Markdown::KramdownAdapter.new(options).markdown(text)
expect(output).to include('<h1>hello world</h1>')
end
end
end

View File

@ -1,66 +0,0 @@
require 'spec_helper'
describe GrapeSwagger::Markdown::RedcarpetAdapter, unless: RUBY_PLATFORM.eql?('java') || RUBY_ENGINE.eql?('rbx') do
context 'initialization' do
context 'initialization' do
it 'uses fenced_code_blocks, auto_links and rouge as default.' do
expect_any_instance_of(GrapeSwagger::Markdown::RedcarpetAdapter).to receive(:new_redcarpet_renderer).with(:rouge).and_call_original
adapter = GrapeSwagger::Markdown::RedcarpetAdapter.new
expect(adapter.extension_options).to eq(fenced_code_blocks: true, autolink: true)
expect(adapter.render_options).to eq({})
end
it 'initializes with no highlighter.' do
expect_any_instance_of(GrapeSwagger::Markdown::RedcarpetAdapter).to receive(:new_redcarpet_renderer).with(:none).and_call_original
adapter = GrapeSwagger::Markdown::RedcarpetAdapter.new render_options: { highlighter: :none }
expect(adapter.extension_options).to eq(fenced_code_blocks: true, autolink: true)
expect(adapter.render_options).to eq({})
end
it 'overrides default values' do
extensions = { fenced_code_blocks: true, autolink: true }
render_options = { highlighter: :none, no_links: true }
adapter = GrapeSwagger::Markdown::RedcarpetAdapter.new extensions: extensions, render_options: render_options
expect(adapter.extension_options).to eq(extensions)
expect(adapter.render_options).to eq(no_links: true)
end
end
context 'markdown' do
it 'marks down with the configured options' do
text = '# hello world #'
extensions = { fenced_code_blocks: true, autolink: true }
render_options = { highlighter: :none, no_links: true }
expect_any_instance_of(Redcarpet::Markdown).to receive(:render).with(text).and_call_original
output = GrapeSwagger::Markdown::RedcarpetAdapter.new(extensions: extensions, render_options: render_options).markdown(text)
expect(output).to include('<h1>hello world</h1>')
end
end
context 'new_redcarpet_renderer' do
it 'returns a rouge syntax highlighter' do
adapter = GrapeSwagger::Markdown::RedcarpetAdapter.new
renderer = adapter.send(:new_redcarpet_renderer, :rouge)
expect(renderer).to include(Rouge::Plugins::Redcarpet)
expect(renderer.superclass).to be(Redcarpet::Render::HTML)
end
it 'returns a default syntax highlighter' do
adapter = GrapeSwagger::Markdown::RedcarpetAdapter.new
renderer = adapter.send(:new_redcarpet_renderer, :none)
expect(renderer).to include(GrapeSwagger::Markdown::RedcarpetAdapter::RenderWithoutSyntaxHighlighter)
expect(renderer.superclass).to be(Redcarpet::Render::HTML)
end
end
end
end

View File

@ -46,7 +46,7 @@ describe 'details' do
{ 'declared_params' => declared(params) } { 'declared_params' => declared(params) }
end end
add_swagger_documentation add_swagger_documentation markdown: 'foo'
end end
end end
end end
@ -74,80 +74,4 @@ describe 'details' do
expect(subject['paths']['/use_detail_block']['get']['description']).to eql 'detailed description of the route inside the `desc` block' expect(subject['paths']['/use_detail_block']['get']['description']).to eql 'detailed description of the route inside the `desc` block'
end end
end end
describe 'details, convert markdown with kramdown' do
include_context "#{MODEL_PARSER} swagger example"
before :all do
module TheApi
class GfmDetailApi < Grape::API
format :json
desc 'This returns something',
detail: details,
entity: Entities::UseResponse,
failure: [{ code: 400, model: Entities::ApiError }]
get '/use_gfm_detail' do
{ 'declared_params' => declared(params) }
end
add_swagger_documentation markdown: GrapeSwagger::Markdown::KramdownAdapter.new
end
end
end
def app
TheApi::GfmDetailApi
end
subject do
get '/swagger_doc'
JSON.parse(last_response.body)
end
specify do
expect(subject['paths']['/use_gfm_detail']['get']).to include('description')
expect(subject['paths']['/use_gfm_detail']['get']['description']).to eql(
"<h1 id=\"burgers-in-heaven\">Burgers in Heaven</h1>\n\n<blockquote>\n <p>A burger doesnt come for free</p>\n</blockquote>\n\n<p>If you want to reserve a burger in heaven, you have to do<br />\nsome crazy stuff on earth.</p>\n\n<pre><code>def do_good\nputs 'help people'\nend\n</code></pre>\n\n<ul>\n <li><em>Will go to Heaven:</em> Probably</li>\n <li><em>Will go to Hell:</em> Probably not</li>\n</ul>"
)
end
end
describe 'details, convert markdown with redcarpet', unless: RUBY_PLATFORM.eql?('java') do
include_context "#{MODEL_PARSER} swagger example"
before :all do
module TheApi
class GfmRcDetailApi < Grape::API
format :json
desc 'This returns something',
detail: details,
entity: Entities::UseResponse,
failure: [{ code: 400, model: Entities::ApiError }]
get '/use_gfm_rc_detail' do
{ 'declared_params' => declared(params) }
end
add_swagger_documentation markdown: GrapeSwagger::Markdown::RedcarpetAdapter.new
end
end
end
def app
TheApi::GfmRcDetailApi
end
subject do
get '/swagger_doc'
JSON.parse(last_response.body)
end
specify do
expect(subject['paths']['/use_gfm_rc_detail']['get']).to include('description')
expect(subject['paths']['/use_gfm_rc_detail']['get']['description']).to eql(
"<h1>Burgers in Heaven</h1>\n\n<blockquote>\n<p>A burger doesn&#39;t come for free</p>\n</blockquote>\n\n<p>If you want to reserve a burger in heaven, you have to do\nsome crazy stuff on earth.</p>\n<pre class=\"highlight plaintext\"><code>def do_good\nputs 'help people'\nend\n</code></pre>\n<ul>\n<li><em>Will go to Heaven:</em> Probably</li>\n<li><em>Will go to Hell:</em> Probably not</li>\n</ul>"
)
end
end
end end

View File

@ -1,7 +1,7 @@
require 'spec_helper' require 'spec_helper'
describe 'details' do describe 'details' do
describe 'details, pass markdown with redcarpet even with nil description and detail', unless: RUBY_PLATFORM.eql?('java') do describe 'has no description, if details or description are nil' do
before :all do before :all do
module TheApi module TheApi
class GfmRcDetailApi < Grape::API class GfmRcDetailApi < Grape::API