Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Markdown format #120

Merged
merged 1 commit into from
Apr 15, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ RspecApiDocumentation.configure do |config|

# An array of output format(s).
# Possible values are :json, :html, :combined_text, :combined_json,
# :json_iodocs, :textile, :append_json
# :json_iodocs, :textile, :markdown, :append_json
config.format = [:html]

# Location of templates
Expand Down Expand Up @@ -144,6 +144,7 @@ end
* **combined_json**: Generates a single file for all examples.
* **json_iodocs**: Generates [I/O Docs](http://www.mashery.com/product/io-docs) style documentation.
* **textile**: Generates an index file and example files in Textile.
* **markdown**: Generates an index file and example files in Markdown.
* **append_json**: Lets you selectively run specs without destroying current documentation. See section below.

### append_json
Expand Down
228 changes: 228 additions & 0 deletions features/markdown_documentation.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
Feature: Generate Markdown documentation from test examples

Background:
Given a file named "app.rb" with:
"""
require 'sinatra'

class App < Sinatra::Base
get '/orders' do
content_type :json

[200, [{ name: 'Order 1', amount: 9.99, description: nil },
{ name: 'Order 2', amount: 100.0, description: 'A great order' }].to_json]
end

get '/orders/:id' do
content_type :json

[200, { order: { name: 'Order 1', amount: 100.0, description: 'A great order' } }.to_json]
end

post '/orders' do
201
end

put '/orders/:id' do
200
end

delete '/orders/:id' do
200
end

get '/help' do
[200, 'Welcome Henry !']
end
end
"""
And a file named "app_spec.rb" with:
"""
require "rspec_api_documentation"
require "rspec_api_documentation/dsl"

RspecApiDocumentation.configure do |config|
config.app = App
config.api_name = "Example API"
config.format = :markdown
config.request_headers_to_include = %w[Content-Type Host]
config.response_headers_to_include = %w[Content-Type Content-Length]
end

resource 'Orders' do
get '/orders' do

example_request 'Getting a list of orders' do
status.should eq(200)
response_body.should eq('[{"name":"Order 1","amount":9.99,"description":null},{"name":"Order 2","amount":100.0,"description":"A great order"}]')
end
end

get '/orders/:id' do
let(:id) { 1 }

example_request 'Getting a specific order' do
status.should eq(200)
response_body.should == '{"order":{"name":"Order 1","amount":100.0,"description":"A great order"}}'
end
end

post '/orders' do
parameter :name, 'Name of order', :required => true
parameter :amount, 'Amount paid', :required => true
parameter :description, 'Some comments on the order'

let(:name) { "Order 3" }
let(:amount) { 33.0 }

example_request 'Creating an order' do
status.should == 201
end
end

put '/orders/:id' do
parameter :name, 'Name of order', :required => true
parameter :amount, 'Amount paid', :required => true
parameter :description, 'Some comments on the order'

let(:id) { 2 }
let(:name) { "Updated name" }

example_request 'Updating an order' do
status.should == 200
end
end

delete "/orders/:id" do
let(:id) { 1 }

example_request "Deleting an order" do
status.should == 200
end
end
end

resource 'Help' do
get '/help' do
example_request 'Getting welcome message' do
status.should eq(200)
response_body.should == 'Welcome Henry !'
end
end

end
"""
When I run `rspec app_spec.rb --require ./app.rb --format RspecApiDocumentation::ApiFormatter`

Scenario: Output helpful progress to the console
Then the output should contain:
"""
Generating API Docs
Orders
GET /orders
* Getting a list of orders
GET /orders/:id
* Getting a specific order
POST /orders
* Creating an order
PUT /orders/:id
* Updating an order
DELETE /orders/:id
* Deleting an order
Help
GET /help
* Getting welcome message
"""
And the output should contain "6 examples, 0 failures"
And the exit status should be 0

Scenario: Index file should look like we expect
Then the file "doc/api/index.markdown" should contain exactly:
"""
# Example API

## Help

* [Getting welcome message](help/getting_welcome_message.markdown)

## Orders

* [Creating an order](orders/creating_an_order.markdown)
* [Deleting an order](orders/deleting_an_order.markdown)
* [Getting a list of orders](orders/getting_a_list_of_orders.markdown)
* [Getting a specific order](orders/getting_a_specific_order.markdown)
* [Updating an order](orders/updating_an_order.markdown)


"""

Scenario: Example 'Creating an order' file should look like we expect
Then the file "doc/api/orders/creating_an_order.markdown" should contain exactly:
"""
# Orders API

## Creating an order

### POST /orders


### Parameters

Name : name *- required -*
Description : Name of order

Name : amount *- required -*
Description : Amount paid

Name : description
Description : Some comments on the order

### Request

#### Headers

<pre>Host: example.org
Content-Type: application/x-www-form-urlencoded</pre>

#### Route

<pre>POST /orders</pre>


#### Body

<pre>name=Order+3&amount=33.0</pre>


### Response

#### Headers

<pre>Content-Type: text/html;charset=utf-8
Content-Length: 0</pre>

#### Status

<pre>201 Created</pre>




"""

Scenario: Example 'Deleting an order' file should be created
Then a file named "doc/api/orders/deleting_an_order.markdown" should exist

Scenario: Example 'Getting a list of orders' file should be created
Then a file named "doc/api/orders/getting_a_list_of_orders.markdown" should exist

Scenario: Example 'Getting a specific order' file should be created
Then a file named "doc/api/orders/getting_a_specific_order.markdown" should exist

Scenario: Example 'Updating an order' file should be created
Then a file named "doc/api/orders/updating_an_order.markdown" should exist

Scenario: Example 'Getting welcome message' file should be created
Then a file named "doc/api/help/getting_welcome_message.markdown" should exist


3 changes: 3 additions & 0 deletions lib/rspec_api_documentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module Writers
autoload :GeneralMarkupWriter
autoload :HtmlWriter
autoload :TextileWriter
autoload :MarkdownWriter
autoload :JsonWriter
autoload :AppendJsonWriter
autoload :JsonIodocsWriter
Expand All @@ -49,6 +50,8 @@ module Views
autoload :HtmlExample
autoload :TextileIndex
autoload :TextileExample
autoload :MarkdownIndex
autoload :MarkdownExample
end

def self.configuration
Expand Down
16 changes: 16 additions & 0 deletions lib/rspec_api_documentation/views/markdown_example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module RspecApiDocumentation
module Views
class MarkdownExample < MarkupExample
EXTENSION = 'markdown'

def initialize(example, configuration)
super
self.template_name = "rspec_api_documentation/markdown_example"
end

def extension
EXTENSION
end
end
end
end
14 changes: 14 additions & 0 deletions lib/rspec_api_documentation/views/markdown_index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module RspecApiDocumentation
module Views
class MarkdownIndex < MarkupIndex
def initialize(index, configuration)
super
self.template_name = "rspec_api_documentation/markdown_index"
end

def examples
@index.examples.map { |example| MarkdownExample.new(example, @configuration) }
end
end
end
end
19 changes: 19 additions & 0 deletions lib/rspec_api_documentation/writers/markdown_writer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module RspecApiDocumentation
module Writers
class MarkdownWriter < GeneralMarkupWriter
EXTENSION = 'markdown'

def markup_index_class
RspecApiDocumentation::Views::MarkdownIndex
end

def markup_example_class
RspecApiDocumentation::Views::MarkdownExample
end

def extension
EXTENSION
end
end
end
end
35 changes: 35 additions & 0 deletions spec/writers/markdown_writer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
require 'spec_helper'

describe RspecApiDocumentation::Writers::MarkdownWriter do
let(:index) { RspecApiDocumentation::Index.new }
let(:configuration) { RspecApiDocumentation::Configuration.new }

describe ".write" do
let(:writer) { double(:writer) }

it "should build a new writer and write the docs" do
described_class.stub(:new).with(index, configuration).and_return(writer)
writer.should_receive(:write)
described_class.write(index, configuration)
end
end

describe "#write" do
let(:writer) { described_class.new(index, configuration) }

before do
template_dir = File.join(configuration.template_path, "rspec_api_documentation")
FileUtils.mkdir_p(template_dir)
File.open(File.join(template_dir, "markdown_index.mustache"), "w+") { |f| f << "{{ mustache }}" }
FileUtils.mkdir_p(configuration.docs_dir)
end

it "should write the index" do
writer.write
index_file = File.join(configuration.docs_dir, "index.markdown")
File.exists?(index_file).should be_true
end
end

end
Loading