fdoc 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +102 -43
- data/lib/fdoc/presenters/html_presenter.rb +11 -0
- data/lib/fdoc/presenters/meta_service_presenter.rb +1 -0
- data/lib/fdoc/presenters/schema_presenter.rb +9 -1
- data/lib/fdoc/templates/endpoint.html.erb +9 -9
- data/lib/fdoc/templates/meta_service.html.erb +4 -12
- data/lib/fdoc/templates/service.html.erb +3 -9
- data/lib/fdoc/templates/styles.css +12 -0
- metadata +16 -15
data/README.md
CHANGED
@@ -18,26 +18,32 @@ Add fdoc to your Gemfile.
|
|
18
18
|
|
19
19
|
gem 'fdoc'
|
20
20
|
|
21
|
-
Tell fdoc where to look for
|
21
|
+
Tell fdoc where to look for `.fdoc` files. By default, fdoc will look in `docs/fdoc`, but you can change this behavior to look anywhere. This fits best in something like a spec\_helper file.
|
22
22
|
|
23
|
-
|
23
|
+
```ruby
|
24
|
+
require 'fdoc'
|
24
25
|
|
25
|
-
|
26
|
+
Fdoc.service_path = "path/to/your/fdocs"
|
27
|
+
```
|
26
28
|
|
27
29
|
fdoc is built to work around controller specs in rspec, and provides `Fdoc::SpecWatcher` as a mixin. Make sure to include it *inside* your top level describe.
|
28
30
|
|
29
|
-
|
31
|
+
```ruby
|
32
|
+
require 'fdoc/spec_watcher'
|
30
33
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
describe MembersController do
|
35
|
+
include Fdoc::SpecWatcher
|
36
|
+
# ...
|
37
|
+
end
|
38
|
+
```
|
35
39
|
|
36
40
|
To enable fdoc for an endpoint, add the `fdoc` option with the path to the endpoint. fdoc will intercept all calls to `get`, `post`, `put`, and `delete` and verify those parameters accordingly.
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
```ruby
|
43
|
+
context "#show", :fdoc => 'members/list' do
|
44
|
+
# ...
|
45
|
+
end
|
46
|
+
```
|
41
47
|
|
42
48
|
fdoc also has a scaffolding mode, where it attemps to infer the schema of a request based on sample responses. The interface is exactly the same as verifying, just set the environment variable `FDOC_SCAFFOLD=true`.
|
43
49
|
|
@@ -51,7 +57,7 @@ fdoc provides the `fdoc_to_html` script to transform a directory of `.fdoc` file
|
|
51
57
|
|
52
58
|
In this repo, try running:
|
53
59
|
|
54
|
-
bin/fdoc_to_html spec/fixtures html
|
60
|
+
bin/fdoc_to_html ./spec/fixtures ./html
|
55
61
|
|
56
62
|
## Example
|
57
63
|
|
@@ -60,37 +66,90 @@ In this repo, try running:
|
|
60
66
|
- For more information on fdoc file naming conventions, please see the [fdoc file conventions guide][github_files].
|
61
67
|
- For more information on how fdoc uses JSON schema, please see the [json schema usage document][github_json].
|
62
68
|
|
63
|
-
Here is `members/list-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
69
|
+
Here is `docs/fdoc/members/list-GET.fdoc`:
|
70
|
+
|
71
|
+
```yaml
|
72
|
+
description: The list of members.
|
73
|
+
requestParameters:
|
74
|
+
properties:
|
75
|
+
limit:
|
76
|
+
type: integer
|
77
|
+
required: no
|
78
|
+
default: 50
|
79
|
+
description: Limits the number of results returned, used for paging.
|
80
|
+
responseParameters:
|
81
|
+
properties:
|
82
|
+
members:
|
83
|
+
type: array
|
84
|
+
items:
|
85
|
+
title: member
|
86
|
+
description: Representation of a member
|
87
|
+
type: object
|
88
|
+
properties:
|
89
|
+
name:
|
90
|
+
description: Member's name
|
91
|
+
type: string
|
92
|
+
required: yes
|
93
|
+
example: Captain Smellypants
|
94
|
+
responseCodes:
|
95
|
+
- status: 200 OK
|
96
|
+
successful: yes
|
97
|
+
description: A list of current members
|
98
|
+
- status: 400 Bad Request
|
99
|
+
successful: no
|
100
|
+
description: Indicates malformed parameters
|
101
|
+
```
|
102
|
+
|
103
|
+
If we run a test against our members controller with an undocumented parameter, `offset`, we'll get an error.
|
104
|
+
|
105
|
+
Our spec file, `spec/controllers/members_controller_spec.rb` looks like:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
require 'fdoc/spec_watcher'
|
109
|
+
|
110
|
+
describe MembersController do
|
111
|
+
content "#show", :fdoc => "members/list" do
|
112
|
+
it "can take an offset" do
|
113
|
+
get :show, {
|
114
|
+
:offset => 5
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
We run:
|
122
|
+
|
123
|
+
bundle exec rspec spec/controllers/members_controller_spec.rb
|
124
|
+
|
125
|
+
And since `offset` is undocumented, fdoc will fail the test:
|
126
|
+
|
127
|
+
Failures:
|
128
|
+
|
129
|
+
1) MembersController#show can take an offset
|
130
|
+
Failure/Error: get :show, { :offset => 5 }
|
131
|
+
JSON::Schema::ValidationError:
|
132
|
+
The property '#/' contains additional properties ["offset"] outside of the schema when none are allowed in schema 8fcac6c4-294b-56a2-a3de-9342e2e729da#
|
133
|
+
# ./spec/controllers/members_controller_spec.rb:5:in `block (3 levels) in <top (required)>'
|
134
|
+
|
135
|
+
If we run the same spec in scaffold mode, it passes and fdoc will write changes to the correspoding `.fdoc` file:
|
136
|
+
|
137
|
+
FDOC_SCAFFOLD=true bundle exec spec/controllers/members_controller_spec.rb
|
138
|
+
|
139
|
+
The diff looks like:
|
140
|
+
|
141
|
+
```diff
|
142
|
+
diff --git a/docs/fdoc/members/list-GET.fdoc b/docs/fdoc/members/list-GET.fdoc b2e3656..dfa363a 100644
|
143
|
+
--- a/docs/fdoc/members/list-GET.fdoc
|
144
|
+
+++ b/docs/fdoc/members/list-GET.fdoc
|
145
|
+
+ offset:
|
146
|
+
+ description: ???
|
147
|
+
+ required: ???
|
148
|
+
+ type: integer
|
149
|
+
+ example: 5
|
150
|
+
```
|
151
|
+
|
152
|
+
Notice how it infers a type, and copies an example, but leaves description and required blank. These fields are best left to humans to decide.
|
94
153
|
|
95
154
|
|
96
155
|
## Goals
|
@@ -55,4 +55,15 @@ class Fdoc::HtmlPresenter
|
|
55
55
|
html_path
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
59
|
+
def tag_with_anchor(tag, content, anchor_slug = nil)
|
60
|
+
anchor_slug ||= content.downcase.gsub(' ', '_')
|
61
|
+
<<-EOS
|
62
|
+
<#{tag} id="#{anchor_slug}">
|
63
|
+
<a href="##{anchor_slug}" class="anchor">
|
64
|
+
#{content}
|
65
|
+
</a>
|
66
|
+
</#{tag}>
|
67
|
+
EOS
|
68
|
+
end
|
58
69
|
end
|
@@ -128,11 +128,19 @@ class Fdoc::SchemaPresenter < Fdoc::HtmlPresenter
|
|
128
128
|
properties.each do |key, property|
|
129
129
|
next if property.nil?
|
130
130
|
html << '<li>'
|
131
|
-
html <<
|
131
|
+
html << tag_with_anchor(
|
132
|
+
'span',
|
133
|
+
'<tt>%s</tt>' % key,
|
134
|
+
schema_slug(key, property)
|
135
|
+
)
|
132
136
|
html << self.class.new(property, options.merge(:nested => true)).to_html
|
133
137
|
html << '</li>'
|
134
138
|
end
|
135
139
|
|
136
140
|
html
|
137
141
|
end
|
142
|
+
|
143
|
+
def schema_slug(key, property)
|
144
|
+
"#{key}-#{property.hash}"
|
145
|
+
end
|
138
146
|
end
|
@@ -28,27 +28,27 @@
|
|
28
28
|
<%= description %>
|
29
29
|
|
30
30
|
<% if show_request? %>
|
31
|
-
|
31
|
+
<%= tag_with_anchor('h2', 'Request') %>
|
32
32
|
|
33
|
-
|
33
|
+
<%= tag_with_anchor('h3', 'Example Request') %>
|
34
34
|
<%= example_request %>
|
35
35
|
|
36
|
-
|
36
|
+
<%= tag_with_anchor('h3', 'Request Parameters') %>
|
37
37
|
<%= request_parameters %>
|
38
38
|
<% end %>
|
39
39
|
|
40
|
-
|
40
|
+
<%= tag_with_anchor('h2', 'Response') %>
|
41
41
|
<% if show_response? %>
|
42
|
-
|
42
|
+
<%= tag_with_anchor('h3', 'Example Response') %>
|
43
43
|
<%= example_response %>
|
44
44
|
|
45
|
-
|
45
|
+
<%= tag_with_anchor('h3', 'Response Parameters') %>
|
46
46
|
<%= response_parameters %>
|
47
47
|
<% end %>
|
48
48
|
|
49
|
-
|
49
|
+
<%= tag_with_anchor('h3', 'Response Codes') %>
|
50
50
|
<% if !successful_response_codes.empty? %>
|
51
|
-
|
51
|
+
<%= tag_with_anchor('h4', 'Successful Response Codes') %>
|
52
52
|
<ul>
|
53
53
|
<% successful_response_codes.each do |response_code| %>
|
54
54
|
<li>
|
@@ -59,7 +59,7 @@
|
|
59
59
|
<% end %>
|
60
60
|
|
61
61
|
<% if !successful_response_codes.empty? %>
|
62
|
-
|
62
|
+
<%= tag_with_anchor('h4', 'Failure Response Codes') %>
|
63
63
|
<ul>
|
64
64
|
<% failure_response_codes.each do |response_code| %>
|
65
65
|
<li>
|
@@ -16,9 +16,7 @@
|
|
16
16
|
|
17
17
|
<%= description %>
|
18
18
|
|
19
|
-
|
20
|
-
Services
|
21
|
-
</h2>
|
19
|
+
<%= tag_with_anchor('h2', 'Services') %>
|
22
20
|
|
23
21
|
<ul>
|
24
22
|
<% services.each do |serv| %>
|
@@ -30,14 +28,10 @@
|
|
30
28
|
<% end %>
|
31
29
|
</ul>
|
32
30
|
|
33
|
-
|
34
|
-
Endpoints
|
35
|
-
</h2>
|
31
|
+
<%= tag_with_anchor('h2', 'Endpoints') %>
|
36
32
|
|
37
33
|
<% endpoints.each do |endpoint_ary| %>
|
38
|
-
|
39
|
-
<%= endpoint_ary.first.prefix %>
|
40
|
-
</h3>
|
34
|
+
<%= tag_with_anchor('h3', endpoint_ary.first.prefix) %>
|
41
35
|
<ul>
|
42
36
|
<% endpoint_ary.each do |endpoint| %>
|
43
37
|
<li>
|
@@ -48,9 +42,7 @@
|
|
48
42
|
<% end %>
|
49
43
|
|
50
44
|
<% if discussion %>
|
51
|
-
|
52
|
-
Discussion
|
53
|
-
</h2>
|
45
|
+
<%= tag_with_anchor('h2', 'Discussion') %>
|
54
46
|
<%= discussion %>
|
55
47
|
<% end %>
|
56
48
|
</div>
|
@@ -24,14 +24,10 @@
|
|
24
24
|
|
25
25
|
<%= description %>
|
26
26
|
|
27
|
-
|
28
|
-
Endpoints
|
29
|
-
</h2>
|
27
|
+
<%= tag_with_anchor('h2', 'Endpoints') %>
|
30
28
|
|
31
29
|
<% endpoints.each do |endpoint_ary| %>
|
32
|
-
|
33
|
-
<%= endpoint_ary.first.prefix %>
|
34
|
-
</h3>
|
30
|
+
<%= tag_with_anchor('h3', endpoint_ary.first.prefix) %>
|
35
31
|
<ul>
|
36
32
|
<% endpoint_ary.each do |endpoint| %>
|
37
33
|
<li>
|
@@ -42,9 +38,7 @@
|
|
42
38
|
<% end %>
|
43
39
|
|
44
40
|
<% if discussion %>
|
45
|
-
|
46
|
-
Discussion
|
47
|
-
</h2>
|
41
|
+
<%= tag_with_anchor('h2', 'Discussion') %>
|
48
42
|
<%= discussion %>
|
49
43
|
<% end %>
|
50
44
|
</div>
|
@@ -29,6 +29,18 @@ h1, h2, h3 { font-weight: 200; }
|
|
29
29
|
a { text-decoration: none; color: #66f; }
|
30
30
|
a:hover, a:active { text-decoration: underline; }
|
31
31
|
|
32
|
+
a.anchor { color: inherit; }
|
33
|
+
a.anchor:hover, a.anchor:active { text-decoration: none;}
|
34
|
+
a.anchor:hover::before, a.anchor.active::before {
|
35
|
+
background: white;
|
36
|
+
display: inline-block;
|
37
|
+
position: absolute;
|
38
|
+
content: '\0261E';
|
39
|
+
font-size: 120%;
|
40
|
+
font-weight: bold;
|
41
|
+
margin-left: -1.2em;
|
42
|
+
}
|
43
|
+
|
32
44
|
ul {
|
33
45
|
list-style: square;
|
34
46
|
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fdoc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -15,7 +15,7 @@ date: 2011-11-07 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: json
|
18
|
-
requirement: &
|
18
|
+
requirement: &70290789895620 !ruby/object:Gem::Requirement
|
19
19
|
none: false
|
20
20
|
requirements:
|
21
21
|
- - ! '>='
|
@@ -23,10 +23,10 @@ dependencies:
|
|
23
23
|
version: '0'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
|
-
version_requirements: *
|
26
|
+
version_requirements: *70290789895620
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: json-schema
|
29
|
-
requirement: &
|
29
|
+
requirement: &70290789911300 !ruby/object:Gem::Requirement
|
30
30
|
none: false
|
31
31
|
requirements:
|
32
32
|
- - ! '>='
|
@@ -34,10 +34,10 @@ dependencies:
|
|
34
34
|
version: 1.0.1
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
|
-
version_requirements: *
|
37
|
+
version_requirements: *70290789911300
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: kramdown
|
40
|
-
requirement: &
|
40
|
+
requirement: &70290789910780 !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
43
|
- - ! '>='
|
@@ -45,10 +45,10 @@ dependencies:
|
|
45
45
|
version: '0'
|
46
46
|
type: :runtime
|
47
47
|
prerelease: false
|
48
|
-
version_requirements: *
|
48
|
+
version_requirements: *70290789910780
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: rake
|
51
|
-
requirement: &
|
51
|
+
requirement: &70290789910100 !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
53
53
|
requirements:
|
54
54
|
- - ! '>='
|
@@ -56,10 +56,10 @@ dependencies:
|
|
56
56
|
version: '0'
|
57
57
|
type: :development
|
58
58
|
prerelease: false
|
59
|
-
version_requirements: *
|
59
|
+
version_requirements: *70290789910100
|
60
60
|
- !ruby/object:Gem::Dependency
|
61
61
|
name: rspec
|
62
|
-
requirement: &
|
62
|
+
requirement: &70290789909420 !ruby/object:Gem::Requirement
|
63
63
|
none: false
|
64
64
|
requirements:
|
65
65
|
- - ~>
|
@@ -67,10 +67,10 @@ dependencies:
|
|
67
67
|
version: '2.5'
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
|
-
version_requirements: *
|
70
|
+
version_requirements: *70290789909420
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: nokogiri
|
73
|
-
requirement: &
|
73
|
+
requirement: &70290789908980 !ruby/object:Gem::Requirement
|
74
74
|
none: false
|
75
75
|
requirements:
|
76
76
|
- - ! '>='
|
@@ -78,10 +78,10 @@ dependencies:
|
|
78
78
|
version: '0'
|
79
79
|
type: :development
|
80
80
|
prerelease: false
|
81
|
-
version_requirements: *
|
81
|
+
version_requirements: *70290789908980
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
83
|
name: cane
|
84
|
-
requirement: &
|
84
|
+
requirement: &70290789908380 !ruby/object:Gem::Requirement
|
85
85
|
none: false
|
86
86
|
requirements:
|
87
87
|
- - ! '>='
|
@@ -89,7 +89,7 @@ dependencies:
|
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
|
-
version_requirements: *
|
92
|
+
version_requirements: *70290789908380
|
93
93
|
description: A tool for documenting API endpoints.
|
94
94
|
email: support@squareup.com
|
95
95
|
executables:
|
@@ -172,3 +172,4 @@ test_files:
|
|
172
172
|
- spec/fixtures/members/members.fdoc.service
|
173
173
|
- spec/fixtures/sample_group.fdoc.meta
|
174
174
|
- spec/spec_helper.rb
|
175
|
+
has_rdoc:
|