Skip to content

Commit 835de73

Browse files
committed
Merge pull request #282 from marnen/275-slate-writer
Add a writer for Slate
2 parents ad4ef4a + b8250a2 commit 835de73

File tree

7 files changed

+529
-0
lines changed

7 files changed

+529
-0
lines changed

features/slate_documentation.feature

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
Feature: Generate Slate documentation from test examples
2+
3+
Background:
4+
Given a file named "app.rb" with:
5+
"""
6+
require 'sinatra'
7+
8+
class App < Sinatra::Base
9+
get '/orders' do
10+
content_type :json
11+
12+
[200, {
13+
:page => 1,
14+
:orders => [
15+
{ name: 'Order 1', amount: 9.99, description: nil },
16+
{ name: 'Order 2', amount: 100.0, description: 'A great order' }
17+
]
18+
}.to_json]
19+
end
20+
21+
get '/orders/:id' do
22+
content_type :json
23+
24+
[200, { order: { name: 'Order 1', amount: 100.0, description: 'A great order' } }.to_json]
25+
end
26+
27+
post '/orders' do
28+
201
29+
end
30+
31+
put '/orders/:id' do
32+
200
33+
end
34+
35+
delete '/orders/:id' do
36+
200
37+
end
38+
39+
get '/help' do
40+
[200, 'Welcome Henry !']
41+
end
42+
end
43+
"""
44+
And a file named "app_spec.rb" with:
45+
"""
46+
require "rspec_api_documentation"
47+
require "rspec_api_documentation/dsl"
48+
49+
RspecApiDocumentation.configure do |config|
50+
config.app = App
51+
config.api_name = "Example API"
52+
config.format = :slate
53+
config.curl_host = 'http://localhost:3000'
54+
config.request_headers_to_include = %w[Content-Type Host]
55+
config.response_headers_to_include = %w[Content-Type Content-Length]
56+
end
57+
58+
resource 'Orders' do
59+
get '/orders' do
60+
response_field :page, "Current page"
61+
62+
example_request 'Getting a list of orders' do
63+
status.should eq(200)
64+
response_body.should eq('{"page":1,"orders":[{"name":"Order 1","amount":9.99,"description":null},{"name":"Order 2","amount":100.0,"description":"A great order"}]}')
65+
end
66+
end
67+
68+
get '/orders/:id' do
69+
let(:id) { 1 }
70+
71+
example_request 'Getting a specific order' do
72+
status.should eq(200)
73+
response_body.should == '{"order":{"name":"Order 1","amount":100.0,"description":"A great order"}}'
74+
end
75+
end
76+
77+
post '/orders' do
78+
parameter :name, 'Name of order', :required => true
79+
parameter :amount, 'Amount paid', :required => true
80+
parameter :description, 'Some comments on the order'
81+
82+
let(:name) { "Order 3" }
83+
let(:amount) { 33.0 }
84+
85+
example_request 'Creating an order' do
86+
status.should == 201
87+
end
88+
end
89+
90+
put '/orders/:id' do
91+
parameter :name, 'Name of order', :required => true
92+
parameter :amount, 'Amount paid', :required => true
93+
parameter :description, 'Some comments on the order'
94+
95+
let(:id) { 2 }
96+
let(:name) { "Updated name" }
97+
98+
example_request 'Updating an order' do
99+
status.should == 200
100+
end
101+
end
102+
103+
delete "/orders/:id" do
104+
let(:id) { 1 }
105+
106+
example_request "Deleting an order" do
107+
status.should == 200
108+
end
109+
end
110+
end
111+
112+
resource 'Help' do
113+
get '/help' do
114+
example_request 'Getting welcome message' do
115+
status.should eq(200)
116+
response_body.should == 'Welcome Henry !'
117+
end
118+
end
119+
120+
end
121+
"""
122+
When I run `rspec app_spec.rb --require ./app.rb --format RspecApiDocumentation::ApiFormatter`
123+
124+
Scenario: Output helpful progress to the console
125+
Then the output should contain:
126+
"""
127+
Generating API Docs
128+
Orders
129+
GET /orders
130+
* Getting a list of orders
131+
GET /orders/:id
132+
* Getting a specific order
133+
POST /orders
134+
* Creating an order
135+
PUT /orders/:id
136+
* Updating an order
137+
DELETE /orders/:id
138+
* Deleting an order
139+
Help
140+
GET /help
141+
* Getting welcome message
142+
"""
143+
And the output should contain "6 examples, 0 failures"
144+
And the exit status should be 0
145+
146+
Scenario: Example 'Getting a list of orders' docs should look like we expect
147+
Then the file "doc/api/_generated_examples.markdown" should contain:
148+
"""
149+
## Getting a list of orders
150+
151+
### Request
152+
153+
#### Endpoint
154+
155+
```
156+
GET /orders
157+
Host: example.org
158+
```
159+
160+
`GET /orders`
161+
162+
163+
#### Parameters
164+
165+
166+
None known.
167+
168+
169+
### Response
170+
171+
```
172+
Content-Type: application/json
173+
Content-Length: 137
174+
200 OK
175+
```
176+
177+
178+
```json
179+
{
180+
"page": 1,
181+
"orders": [
182+
{
183+
"name": "Order 1",
184+
"amount": 9.99,
185+
"description": null
186+
},
187+
{
188+
"name": "Order 2",
189+
"amount": 100.0,
190+
"description": "A great order"
191+
}
192+
]
193+
}
194+
```
195+
196+
197+
198+
#### Fields
199+
200+
| Name | Description |
201+
|:-----------|:--------------------|
202+
| page | Current page |
203+
204+
205+
206+
### cURL
207+
208+
<code>curl&nbsp;"http://localhost:3000/orders"&nbsp;-X&nbsp;GET&nbsp;&#92;<br>&nbsp;&nbsp;-H&nbsp;"Host:&nbsp;example.org"&nbsp;&#92;<br>&nbsp;&nbsp;-H&nbsp;"Cookie:&nbsp;"</code>
209+
"""
210+
211+
Scenario: Example 'Creating an order' docs should look like we expect
212+
Then the file "doc/api/_generated_examples.markdown" should contain:
213+
"""
214+
## Creating an order
215+
216+
### Request
217+
218+
#### Endpoint
219+
220+
```
221+
POST /orders
222+
Host: example.org
223+
Content-Type: application/x-www-form-urlencoded
224+
```
225+
226+
`POST /orders`
227+
228+
229+
#### Parameters
230+
231+
232+
```json
233+
name=Order+3&amount=33.0
234+
```
235+
236+
237+
| Name | Description |
238+
|:-----|:------------|
239+
| name *required* | Name of order |
240+
| amount *required* | Amount paid |
241+
| description | Some comments on the order |
242+
243+
244+
245+
### Response
246+
247+
```
248+
Content-Type: text/html;charset=utf-8
249+
Content-Length: 0
250+
201 Created
251+
```
252+
253+
254+
255+
256+
257+
### cURL
258+
259+
<code>curl&nbsp;"http://localhost:3000/orders"&nbsp;-d&nbsp;'name=Order+3&amount=33.0'&nbsp;-X&nbsp;POST&nbsp;&#92;<br>&nbsp;&nbsp;-H&nbsp;"Host:&nbsp;example.org"&nbsp;&#92;<br>&nbsp;&nbsp;-H&nbsp;"Content-Type:&nbsp;application/x-www-form-urlencoded"&nbsp;&#92;<br>&nbsp;&nbsp;-H&nbsp;"Cookie:&nbsp;"</code>
260+
"""
261+
262+
Scenario: Example 'Deleting an order' docs should be created
263+
Then the file "doc/api/_generated_examples.markdown" should contain:
264+
"""
265+
## Deleting an order
266+
"""
267+
268+
Scenario: Example 'Getting a list of orders' docs should be created
269+
Then the file "doc/api/_generated_examples.markdown" should contain:
270+
"""
271+
## Getting a list of orders
272+
"""
273+
274+
Scenario: Example 'Getting a specific order' docs should be created
275+
Then the file "doc/api/_generated_examples.markdown" should contain:
276+
"""
277+
## Getting a specific order
278+
"""
279+
280+
Scenario: Example 'Updating an order' docs should be created
281+
Then the file "doc/api/_generated_examples.markdown" should contain:
282+
"""
283+
## Updating an order
284+
"""
285+
286+
Scenario: Example 'Getting welcome message' docs should be created
287+
Then the file "doc/api/_generated_examples.markdown" should contain:
288+
"""
289+
## Getting welcome message
290+
"""

lib/rspec_api_documentation.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ module Writers
4343
autoload :IndexHelper
4444
autoload :CombinedTextWriter
4545
autoload :CombinedJsonWriter
46+
autoload :SlateWriter
4647
end
4748

4849
module Views
@@ -56,6 +57,7 @@ module Views
5657
autoload :TextileExample
5758
autoload :MarkdownIndex
5859
autoload :MarkdownExample
60+
autoload :SlateExample
5961
end
6062

6163
def self.configuration
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module RspecApiDocumentation
2+
module Views
3+
class SlateExample < MarkdownExample
4+
def initialize(example, configuration)
5+
super
6+
self.template_name = "rspec_api_documentation/slate_example"
7+
end
8+
9+
def curl_with_linebreaks
10+
requests.map {|request| request[:curl].lines }.flatten.map do |line|
11+
line.rstrip.gsub("\t", ' ').gsub(' ', '&nbsp;').gsub('\\', '&#92;')
12+
end.join "<br>"
13+
end
14+
15+
def explanation_with_linebreaks
16+
explanation.gsub "\n", "<br>\n"
17+
end
18+
19+
def write
20+
File.open(configuration.docs_dir.join("#{FILENAME}.#{extension}"), 'w+') do |file|
21+
file.write "# #{configuration.api_name}\n\n"
22+
index.examples.sort_by!(&:description) unless configuration.keep_source_order
23+
24+
index.examples.each do |example|
25+
markup_example = markup_example_class.new(example, configuration)
26+
file.write markup_example.render
27+
end
28+
end
29+
end
30+
end
31+
end
32+
end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module RspecApiDocumentation
2+
module Writers
3+
4+
class SlateWriter < MarkdownWriter
5+
FILENAME = '_generated_examples'
6+
7+
def self.clear_docs(docs_dir)
8+
FileUtils.mkdir_p(docs_dir)
9+
FileUtils.rm Dir[File.join docs_dir, "#{FILENAME}.*"]
10+
end
11+
12+
def markup_example_class
13+
RspecApiDocumentation::Views::SlateExample
14+
end
15+
16+
def write
17+
File.open(configuration.docs_dir.join("#{FILENAME}.#{extension}"), 'w+') do |file|
18+
file.write "# #{configuration.api_name}\n\n"
19+
index.examples.sort_by!(&:description) unless configuration.keep_source_order
20+
21+
index.examples.each do |example|
22+
markup_example = markup_example_class.new(example, configuration)
23+
file.write markup_example.render
24+
end
25+
end
26+
end
27+
end
28+
end
29+
end

0 commit comments

Comments
 (0)