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

Textile documentation generator #83

Merged
merged 26 commits into from
Aug 30, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fecf23b
Ignore swp files.
Jul 4, 2013
842a975
Generate api docs in json, combined_text and html.
Jul 5, 2013
397bd37
Added a GeneralMarkupWriter class as parent of HtmlWriter
Jul 5, 2013
c789f44
Moved HtmlIndex and HtmlExample to a dedicated module
Jul 5, 2013
6174372
Added MarkupIndex and MarkupExample for views as parent
Jul 5, 2013
58b26a3
Merge branch 'feature/writers_minor_refactoring' into develop
Jul 5, 2013
1a74b2a
Added textile views and writer.
jonathanpa Jul 7, 2013
a3780d1
Textile Index and Example mustache templates with
jonathanpa Jul 28, 2013
c03d6c8
Added additional test cases in textile_documentation.feature.
Jul 29, 2013
3a717fc
Remove links in textile index towards textile examples
Aug 2, 2013
c16e673
First version of the textile_example mustache template.
Aug 2, 2013
1800a64
Cucumber steps definitions written for basic textile
jonathanpa Aug 3, 2013
47502e4
Use Sinatra dummy app for textile_documentation.feature.
jonathanpa Aug 15, 2013
e5defdb
Rewrote the spec dummy api and its related api spec.
jonathanpa Aug 18, 2013
1e9ed3f
In textile_documentation.feature, contents retested
jonathanpa Aug 18, 2013
3904c77
Check contents of 'Creating an order' example file in
Aug 19, 2013
ae1eeb9
Added scenarios to test presence of generated examples
jonathanpa Aug 19, 2013
1a99d05
Corrections on scenarios in textile_documentation.feature
jonathanpa Aug 20, 2013
6553294
Added TextileWriter test cases in api_documentation_spec.rb.
jonathanpa Aug 20, 2013
1f05639
Merge branch 'feature/textile_writter' into develop
jonathanpa Aug 20, 2013
a205f57
Merge remote-tracking branch 'zipmark/master' into feature/merge_chan…
jonathanpa Aug 30, 2013
4869063
Updated textile_documentation.feature with the latest
jonathanpa Aug 30, 2013
7306126
Merge branch 'feature/merge_changes_from_zipmark' into develop
jonathanpa Aug 30, 2013
5cd4d3a
Fix in MarkupIndex that should not use HtmlExample.
jonathanpa Aug 30, 2013
c8819f6
Added links to textile examples in textile index.
jonathanpa Aug 30, 2013
637e143
Merge branch 'feature/links_into_textile_index_to_examples' into develop
jonathanpa Aug 30, 2013
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ tmp
example/docs
example/public/docs
*.gem
*.swp
2 changes: 1 addition & 1 deletion example/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
end

RspecApiDocumentation.configure do |config|
config.format = [:json, :combined_text]
config.format = [:json, :combined_text, :html]
config.curl_host = 'http://localhost:3000'
config.api_name = "Example App API"
end
229 changes: 229 additions & 0 deletions features/textile_documentation.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
Feature: Generate Textile 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 = :textile
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.textile" should contain exactly:
"""
h1. Example API

h2. Help

* "Getting welcome message":help/getting_welcome_message.textile

h2. Orders

* "Creating an order":orders/creating_an_order.textile
* "Deleting an order":orders/deleting_an_order.textile
* "Getting a list of orders":orders/getting_a_list_of_orders.textile
* "Getting a specific order":orders/getting_a_specific_order.textile
* "Updating an order":orders/updating_an_order.textile


"""

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

h2. Creating an order

h3. POST /orders


h3. Parameters

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

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

Name : description
Description : Some comments on the order

h3. Request

h4. Headers

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

h4. Route

<pre>POST /orders</pre>


h4. Body

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


h3. Response

h4. Headers

<pre>X-Frame-Options: sameorigin
X-XSS-Protection: 1; mode=block
Content-Type: text/html;charset=utf-8
Content-Length: 0</pre>

h4. Status

<pre>201 Created</pre>




"""

Scenario: Example 'Deleting an order' file should be created
Then a file named "doc/api/orders/deleting_an_order.textile" 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.textile" should exist

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

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

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


13 changes: 13 additions & 0 deletions lib/rspec_api_documentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,27 @@ module RspecApiDocumentation
module Writers
extend ActiveSupport::Autoload

autoload :GeneralMarkupWriter
autoload :HtmlWriter
autoload :TextileWriter
autoload :JsonWriter
autoload :JsonIodocsWriter
autoload :IndexWriter
autoload :CombinedTextWriter
autoload :CombinedJsonWriter
end

module Views
extend ActiveSupport::Autoload

autoload :MarkupIndex
autoload :MarkupExample
autoload :HtmlIndex
autoload :HtmlExample
autoload :TextileIndex
autoload :TextileExample
end

def self.configuration
@configuration ||= Configuration.new
end
Expand Down
16 changes: 16 additions & 0 deletions lib/rspec_api_documentation/views/html_example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module RspecApiDocumentation
module Views
class HtmlExample < MarkupExample
EXTENSION = 'html'

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

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

def examples
@index.examples.map { |example| HtmlExample.new(example, @configuration) }
end
end
end
end
58 changes: 58 additions & 0 deletions lib/rspec_api_documentation/views/markup_example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'mustache'

module RspecApiDocumentation
module Views
class MarkupExample < Mustache
def initialize(example, configuration)
@example = example
@host = configuration.curl_host
self.template_path = configuration.template_path
end

def method_missing(method, *args, &block)
@example.send(method, *args, &block)
end

def respond_to?(method, include_private = false)
super || @example.respond_to?(method, include_private)
end

def dirname
resource_name.downcase.gsub(/\s+/, '_')
end

def filename
basename = description.downcase.gsub(/\s+/, '_').gsub(/[^a-z_]/, '')
basename = Digest::MD5.new.update(description).to_s if basename.blank?
"#{basename}.#{extension}"
end

def requests
super.map do |hash|
hash[:request_headers_text] = format_hash(hash[:request_headers])
hash[:request_query_parameters_text] = format_hash(hash[:request_query_parameters])
hash[:response_headers_text] = format_hash(hash[:response_headers])
if @host
hash[:curl] = hash[:curl].output(@host) if hash[:curl].is_a? RspecApiDocumentation::Curl
else
hash[:curl] = nil
end
hash
end
end

def extension
raise 'Parent class. This method should not be called.'
end

private

def format_hash(hash = {})
return nil unless hash.present?
hash.collect do |k, v|
"#{k}: #{v}"
end.join("\n")
end
end
end
end
21 changes: 21 additions & 0 deletions lib/rspec_api_documentation/views/markup_index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'mustache'

module RspecApiDocumentation
module Views
class MarkupIndex < Mustache
def initialize(index, configuration)
@index = index
@configuration = configuration
self.template_path = configuration.template_path
end

def api_name
@configuration.api_name
end

def sections
RspecApiDocumentation::Writers::IndexWriter.sections(examples, @configuration)
end
end
end
end
Loading