fdoc 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +15 -0
- data/lib/fdoc.rb +2 -1
- data/lib/fdoc/cli.rb +53 -15
- data/lib/fdoc/meta_service.rb +1 -1
- data/lib/fdoc/presenters/{html_presenter.rb → base_presenter.rb} +12 -17
- data/lib/fdoc/presenters/endpoint_presenter.rb +22 -24
- data/lib/fdoc/presenters/json_presenter.rb +31 -0
- data/lib/fdoc/presenters/meta_service_presenter.rb +25 -6
- data/lib/fdoc/presenters/response_code_presenter.rb +13 -4
- data/lib/fdoc/presenters/schema_presenter.rb +36 -4
- data/lib/fdoc/presenters/service_presenter.rb +25 -6
- data/lib/fdoc/templates/endpoint.html.erb +5 -5
- data/lib/fdoc/templates/endpoint.md.erb +40 -0
- data/lib/fdoc/templates/meta_service.html.erb +3 -1
- data/lib/fdoc/templates/meta_service.md.erb +20 -0
- data/lib/fdoc/templates/service.html.erb +4 -2
- data/lib/fdoc/templates/service.md.erb +18 -0
- data/lib/fdoc/templates/shared/endpoint_name.html.erb +8 -0
- data/lib/fdoc/templates/shared/endpoint_name.md.erb +1 -0
- data/lib/fdoc/templates/styles.css +1 -1
- data/spec/fdoc/cli_spec.rb +87 -40
- data/spec/fdoc/endpoint_scaffold_spec.rb +5 -5
- data/spec/fdoc/endpoint_spec.rb +18 -18
- data/spec/fdoc/presenters/base_presenter_spec.rb +28 -0
- data/spec/fdoc/presenters/endpoint_presenter_spec.rb +8 -1
- data/spec/fdoc/presenters/meta_service_presenter_spec.rb +38 -0
- data/spec/fdoc/presenters/schema_presenter_spec.rb +34 -0
- data/spec/fdoc/presenters/service_presenter_spec.rb +24 -1
- data/spec/fixtures/members/list/complex-params-GET.fdoc +20 -20
- data/spec/spec_helper.rb +1 -0
- metadata +14 -2
data/README.md
CHANGED
@@ -24,6 +24,12 @@ Tell fdoc where to look for `.fdoc` files. By default, fdoc will look in `docs/f
|
|
24
24
|
require 'fdoc'
|
25
25
|
|
26
26
|
Fdoc.service_path = "path/to/your/fdocs"
|
27
|
+
|
28
|
+
# Configure how Fdoc decides a successful response
|
29
|
+
Fdoc.decide_success_with do |response, status|
|
30
|
+
status.to_i < 400
|
31
|
+
end
|
32
|
+
|
27
33
|
```
|
28
34
|
|
29
35
|
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.
|
@@ -88,6 +94,15 @@ In this repo, try running:
|
|
88
94
|
|
89
95
|
bin/fdoc convert ./spec/fixtures --output=./html
|
90
96
|
|
97
|
+
```
|
98
|
+
Options:
|
99
|
+
-o, [--output=OUTPUT] # Output path
|
100
|
+
-u, [--url-base-path=URL_BASE_PATH] # URL base path
|
101
|
+
-f, [--format=FORMAT] # Format in html or markdown, defaults to html
|
102
|
+
# Default: html
|
103
|
+
-t, [--templates=TEMPLATES] # Directory with override templates
|
104
|
+
```
|
105
|
+
|
91
106
|
## Example
|
92
107
|
|
93
108
|
`.fdoc` files are YAML files based on [JSON schema][json_schema] to describe API endpoints. They derive their endpoint path and verb from their filename.
|
data/lib/fdoc.rb
CHANGED
@@ -38,7 +38,8 @@ require 'fdoc/service'
|
|
38
38
|
require 'fdoc/meta_service'
|
39
39
|
require 'fdoc/endpoint'
|
40
40
|
require 'fdoc/endpoint_scaffold'
|
41
|
-
require 'fdoc/presenters/
|
41
|
+
require 'fdoc/presenters/json_presenter'
|
42
|
+
require 'fdoc/presenters/base_presenter'
|
42
43
|
require 'fdoc/presenters/service_presenter'
|
43
44
|
require 'fdoc/presenters/meta_service_presenter'
|
44
45
|
require 'fdoc/presenters/endpoint_presenter'
|
data/lib/fdoc/cli.rb
CHANGED
@@ -19,11 +19,13 @@ module Fdoc
|
|
19
19
|
File.expand_path("../templates", __FILE__)
|
20
20
|
end
|
21
21
|
|
22
|
-
desc "convert FDOC_PATH", "Convert fdoc to HTML"
|
23
|
-
method_option :output, :aliases => "-o", :desc => "
|
22
|
+
desc "convert FDOC_PATH", "Convert fdoc to HTML or Markdowns"
|
23
|
+
method_option :output, :aliases => "-o", :desc => "Output path"
|
24
24
|
method_option :url_base_path, :aliases => "-u", :desc => "URL base path"
|
25
|
+
method_option :format, :aliases => "-f", :desc => "Format in html or markdown, defaults to html", :default => "html"
|
26
|
+
method_option :templates, :aliases => "-t", :desc => "Template overrides path"
|
25
27
|
def convert(fdoc_path)
|
26
|
-
say_status nil, "Converting fdoc to
|
28
|
+
say_status nil, "Converting fdoc to #{options[:format]}"
|
27
29
|
|
28
30
|
self.origin_path = File.expand_path(fdoc_path)
|
29
31
|
raise Fdoc::NotFound.new(origin_path) unless has_valid_origin?
|
@@ -33,25 +35,51 @@ module Fdoc
|
|
33
35
|
raise Fdoc::NotADirectory.new(output_path) unless has_valid_destination?
|
34
36
|
say_status :inside, output_path
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
if options[:format] == 'markdown'
|
39
|
+
convert_to_markdown
|
40
|
+
else
|
41
|
+
convert_to_html
|
39
42
|
end
|
43
|
+
end
|
40
44
|
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
no_tasks do
|
46
|
+
def convert_to_html
|
47
|
+
in_root do
|
48
|
+
copy_file("styles.css")
|
49
|
+
create_file("index.html", meta_presenter.to_html) if has_meta_service?
|
50
|
+
end
|
44
51
|
|
45
|
-
|
46
|
-
|
47
|
-
|
52
|
+
service_presenters.each do |service_presenter|
|
53
|
+
inside_service_presenter(service_presenter) do
|
54
|
+
create_file("index.html", service_presenter.to_html)
|
55
|
+
|
56
|
+
service_presenter.endpoints.each do |endpoint_prefix_group|
|
57
|
+
endpoint_prefix_group.each do |endpoint|
|
58
|
+
create_file(endpoint.url, endpoint.to_html)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def convert_to_markdown
|
66
|
+
in_root do
|
67
|
+
create_file("index.md", meta_presenter.to_markdown) if has_meta_service?
|
68
|
+
end
|
69
|
+
|
70
|
+
service_presenters.each do |service_presenter|
|
71
|
+
inside_service_presenter(service_presenter) do
|
72
|
+
create_file("index.md", service_presenter.to_markdown)
|
73
|
+
|
74
|
+
service_presenter.endpoints.each do |endpoint_prefix_group|
|
75
|
+
endpoint_prefix_group.each do |endpoint|
|
76
|
+
create_file(endpoint.url('.md'), endpoint.to_markdown)
|
77
|
+
end
|
48
78
|
end
|
49
79
|
end
|
50
80
|
end
|
51
81
|
end
|
52
|
-
end
|
53
82
|
|
54
|
-
no_tasks do
|
55
83
|
def inside_service_presenter(service, &block)
|
56
84
|
if has_meta_service?
|
57
85
|
inside(service.slug_name, {:verbose => true}, &block)
|
@@ -65,7 +93,16 @@ module Fdoc
|
|
65
93
|
if options[:output]
|
66
94
|
File.expand_path(options[:output])
|
67
95
|
else
|
68
|
-
File.expand_path("
|
96
|
+
File.expand_path("../#{options[:format]}", origin_path)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def template_path
|
101
|
+
@template_path ||=
|
102
|
+
if options[:templates]
|
103
|
+
File.expand_path(options[:templates])
|
104
|
+
else
|
105
|
+
File.expand_path("../templates", origin_path)
|
69
106
|
end
|
70
107
|
end
|
71
108
|
|
@@ -91,6 +128,7 @@ module Fdoc
|
|
91
128
|
{
|
92
129
|
:static_html => true,
|
93
130
|
:url_base_path => options[:url_base_path],
|
131
|
+
:template_directory => template_path,
|
94
132
|
:html_directory => destination_root
|
95
133
|
}
|
96
134
|
end
|
data/lib/fdoc/meta_service.rb
CHANGED
@@ -5,7 +5,7 @@ class Fdoc::MetaService
|
|
5
5
|
attr_reader :meta_service_dir
|
6
6
|
|
7
7
|
def initialize(meta_service_dir)
|
8
|
-
@meta_service_dir = meta_service_dir
|
8
|
+
@meta_service_dir = File.expand_path(meta_service_dir)
|
9
9
|
|
10
10
|
service_path = Dir["#{meta_service_dir}/*.fdoc.meta"].first
|
11
11
|
@schema = if service_path
|
@@ -1,21 +1,25 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'kramdown'
|
3
3
|
require 'json'
|
4
|
+
require 'forwardable'
|
4
5
|
|
5
|
-
#
|
6
|
-
#
|
6
|
+
# BasePresenters assist in generating Html for fdoc classes.
|
7
|
+
# BasePresenters is an abstract class with a lot of helper methods
|
7
8
|
# for URLs and common text styling tasks (like #render_markdown
|
8
9
|
# and #render_json)
|
9
|
-
class Fdoc::
|
10
|
+
class Fdoc::BasePresenter
|
10
11
|
attr_reader :options
|
11
12
|
|
12
13
|
def initialize(options = {})
|
13
14
|
@options = options
|
14
15
|
end
|
15
16
|
|
16
|
-
def render_erb(erb_name)
|
17
|
-
template_path = File.join(
|
18
|
-
|
17
|
+
def render_erb(erb_name, binding = get_binding)
|
18
|
+
template_path = File.join(options[:template_directory], erb_name)
|
19
|
+
if !File.exists? template_path
|
20
|
+
template_path = File.join(File.dirname(__FILE__), "../templates", erb_name)
|
21
|
+
end
|
22
|
+
template = ERB.new(File.read(template_path), nil, '-')
|
19
23
|
template.result(binding)
|
20
24
|
end
|
21
25
|
|
@@ -27,17 +31,8 @@ class Fdoc::HtmlPresenter
|
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
|
-
def
|
31
|
-
|
32
|
-
'<tt>"%s"</tt>' % json.gsub(/\"/, 'quot;')
|
33
|
-
elsif json.kind_of?(Numeric) ||
|
34
|
-
json.kind_of?(TrueClass) ||
|
35
|
-
json.kind_of?(FalseClass)
|
36
|
-
'<tt>%s</tt>' % json
|
37
|
-
elsif json.kind_of?(Hash) ||
|
38
|
-
json.kind_of?(Array)
|
39
|
-
'<pre><code>%s</code></pre>' % JSON.pretty_generate(json)
|
40
|
-
end
|
34
|
+
def get_binding
|
35
|
+
binding
|
41
36
|
end
|
42
37
|
|
43
38
|
def html_directory
|
@@ -1,33 +1,19 @@
|
|
1
|
-
#
|
2
|
-
class Fdoc::EndpointPresenter < Fdoc::
|
3
|
-
attr_accessor :service_presenter, :endpoint
|
1
|
+
# BasePresenter for an Endpoint
|
2
|
+
class Fdoc::EndpointPresenter < Fdoc::BasePresenter
|
3
|
+
attr_accessor :service_presenter, :endpoint, :endpoint_presenter
|
4
4
|
|
5
5
|
def initialize(endpoint, options = {})
|
6
6
|
super(options)
|
7
7
|
@endpoint = endpoint
|
8
|
+
@endpoint_presenter = self
|
8
9
|
end
|
9
10
|
|
10
11
|
def to_html
|
11
12
|
render_erb('endpoint.html.erb')
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
-
|
16
|
-
<span class="endpoint-name #{@endpoint.deprecated? ? 'deprecated' : nil}">
|
17
|
-
<span class="verb">#{@endpoint.verb}</span>
|
18
|
-
<span class="root">#{zws_ify(@endpoint.service.base_path)}</span><span
|
19
|
-
class="path">#{zws_ify(@endpoint.path)}</span>
|
20
|
-
#{@endpoint.deprecated? ? '(deprecated)' : nil}
|
21
|
-
</span>
|
22
|
-
EOS
|
23
|
-
end
|
24
|
-
|
25
|
-
def name_as_link
|
26
|
-
<<-EOS
|
27
|
-
<a href="#{url}">
|
28
|
-
#{name}
|
29
|
-
</a>
|
30
|
-
EOS
|
15
|
+
def to_markdown
|
16
|
+
render_erb('endpoint.md.erb')
|
31
17
|
end
|
32
18
|
|
33
19
|
def url(extension = ".html")
|
@@ -62,11 +48,11 @@ class Fdoc::EndpointPresenter < Fdoc::HtmlPresenter
|
|
62
48
|
def request_parameters
|
63
49
|
Fdoc::SchemaPresenter.new(endpoint.request_parameters,
|
64
50
|
options.merge(:request => true)
|
65
|
-
)
|
51
|
+
)
|
66
52
|
end
|
67
53
|
|
68
54
|
def response_parameters
|
69
|
-
Fdoc::SchemaPresenter.new(endpoint.response_parameters, options)
|
55
|
+
Fdoc::SchemaPresenter.new(endpoint.response_parameters, options)
|
70
56
|
end
|
71
57
|
|
72
58
|
def response_codes
|
@@ -84,11 +70,23 @@ class Fdoc::EndpointPresenter < Fdoc::HtmlPresenter
|
|
84
70
|
end
|
85
71
|
|
86
72
|
def example_request
|
87
|
-
|
73
|
+
Fdoc::JsonPresenter.new(example_from_schema(endpoint.request_parameters))
|
88
74
|
end
|
89
75
|
|
90
76
|
def example_response
|
91
|
-
|
77
|
+
Fdoc::JsonPresenter.new(example_from_schema(endpoint.response_parameters))
|
78
|
+
end
|
79
|
+
|
80
|
+
def deprecated?
|
81
|
+
@endpoint.deprecated?
|
82
|
+
end
|
83
|
+
|
84
|
+
def base_path
|
85
|
+
zws_ify(@endpoint.service.base_path)
|
86
|
+
end
|
87
|
+
|
88
|
+
def path
|
89
|
+
zws_ify(@endpoint.path)
|
92
90
|
end
|
93
91
|
|
94
92
|
ATOMIC_TYPES = %w(string integer number boolean null)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'json'
|
3
|
+
class Fdoc::JsonPresenter
|
4
|
+
attr_reader :json
|
5
|
+
|
6
|
+
def initialize(json)
|
7
|
+
@json = json
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_html
|
11
|
+
if json.kind_of? String
|
12
|
+
'<tt>"%s"</tt>' % json.gsub(/\"/, 'quot;')
|
13
|
+
elsif json.kind_of?(Numeric) ||
|
14
|
+
json.kind_of?(TrueClass) ||
|
15
|
+
json.kind_of?(FalseClass)
|
16
|
+
'<tt>%s</tt>' % json
|
17
|
+
elsif json.kind_of?(Hash) ||
|
18
|
+
json.kind_of?(Array)
|
19
|
+
'<pre><code>%s</code></pre>' % JSON.pretty_generate(json)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_markdown
|
24
|
+
if json.kind_of?(Hash) ||
|
25
|
+
json.kind_of?(Array)
|
26
|
+
JSON.pretty_generate(json)
|
27
|
+
else
|
28
|
+
json
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,16 +1,27 @@
|
|
1
|
-
#
|
2
|
-
class Fdoc::MetaServicePresenter < Fdoc::
|
1
|
+
# BasePresenter for Fdoc::MetaService
|
2
|
+
class Fdoc::MetaServicePresenter < Fdoc::BasePresenter
|
3
3
|
attr_reader :meta_service
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegators :meta_service, :name, :meta_service_dir
|
4
7
|
|
5
8
|
def initialize(meta_service, options = {})
|
6
9
|
super(options)
|
7
10
|
@meta_service = meta_service
|
8
11
|
end
|
9
12
|
|
13
|
+
def name
|
14
|
+
meta_service.name
|
15
|
+
end
|
16
|
+
|
10
17
|
def to_html
|
11
18
|
render_erb('meta_service.html.erb')
|
12
19
|
end
|
13
20
|
|
21
|
+
def to_markdown
|
22
|
+
render_erb('meta_service.md.erb')
|
23
|
+
end
|
24
|
+
|
14
25
|
def services
|
15
26
|
@services ||= meta_service.services.
|
16
27
|
sort_by(&:name).
|
@@ -38,12 +49,20 @@ class Fdoc::MetaServicePresenter < Fdoc::HtmlPresenter
|
|
38
49
|
@endpoints
|
39
50
|
end
|
40
51
|
|
41
|
-
def description
|
42
|
-
render_markdown(meta_service.description)
|
52
|
+
def description(options = {:render => true})
|
53
|
+
options[:render] ? render_markdown(meta_service.description) : meta_service.description
|
54
|
+
end
|
55
|
+
|
56
|
+
def discussion(options = {:render => true})
|
57
|
+
options[:render] ? render_markdown(meta_service.discussion) : meta_service.discussion
|
43
58
|
end
|
44
59
|
|
45
|
-
def
|
46
|
-
|
60
|
+
def relative_service_path(service_presenter, file_name = nil)
|
61
|
+
service_path = service_presenter.slug_name
|
62
|
+
if file_name
|
63
|
+
service_path = File.join(service_path, file_name)
|
64
|
+
end
|
65
|
+
service_path
|
47
66
|
end
|
48
67
|
|
49
68
|
private
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# An
|
2
|
-
class Fdoc::ResponseCodePresenter < Fdoc::
|
1
|
+
# An BasePresenter for ResponseCodes
|
2
|
+
class Fdoc::ResponseCodePresenter < Fdoc::BasePresenter
|
3
3
|
attr_reader :response_code
|
4
4
|
|
5
5
|
def initialize(response_code, options)
|
@@ -18,6 +18,10 @@ class Fdoc::ResponseCodePresenter < Fdoc::HtmlPresenter
|
|
18
18
|
EOS
|
19
19
|
end
|
20
20
|
|
21
|
+
def to_markdown
|
22
|
+
"__#{status}__: #{description_raw}"
|
23
|
+
end
|
24
|
+
|
21
25
|
def successful?
|
22
26
|
response_code["successful"]
|
23
27
|
end
|
@@ -27,6 +31,11 @@ class Fdoc::ResponseCodePresenter < Fdoc::HtmlPresenter
|
|
27
31
|
end
|
28
32
|
|
29
33
|
def description
|
30
|
-
render_markdown(
|
34
|
+
render_markdown(description_raw)
|
31
35
|
end
|
32
|
-
|
36
|
+
|
37
|
+
def description_raw
|
38
|
+
response_code["description"]
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
# An
|
1
|
+
# An BasePresenter for a JSON Schema fragment. Like most JSON
|
2
2
|
# schema things, has a tendency to recurse.
|
3
|
-
class Fdoc::SchemaPresenter < Fdoc::
|
3
|
+
class Fdoc::SchemaPresenter < Fdoc::BasePresenter
|
4
4
|
FORMATTED_KEYS = %w(
|
5
5
|
description
|
6
6
|
type
|
@@ -40,7 +40,7 @@ class Fdoc::SchemaPresenter < Fdoc::HtmlPresenter
|
|
40
40
|
html << '<li>Required: %s</li>' % required? if nested?
|
41
41
|
html << '<li>Type: %s</li>' % type if type
|
42
42
|
html << '<li>Format: %s</li>' % format if format
|
43
|
-
html << '<li>Example: %s</li>' % example if example
|
43
|
+
html << '<li>Example: %s</li>' % example.to_html if example
|
44
44
|
html << enum_html
|
45
45
|
|
46
46
|
(@schema.keys - FORMATTED_KEYS).each do |key|
|
@@ -58,6 +58,38 @@ class Fdoc::SchemaPresenter < Fdoc::HtmlPresenter
|
|
58
58
|
html.string
|
59
59
|
end
|
60
60
|
|
61
|
+
def to_markdown(prefix = "")
|
62
|
+
md = StringIO.new
|
63
|
+
md << 'Deprecated' if deprecated?
|
64
|
+
md << "\n#{@schema["description"]}"
|
65
|
+
md << "\n#{prefix}* __Required__: #{required?}" if nested?
|
66
|
+
md << "\n#{prefix}* __Type__: #{type}" if type
|
67
|
+
md << "\n#{prefix}* __Format__: #{format}" if format
|
68
|
+
md << "\n#{prefix}* __Example__: <tt>#{example.to_markdown}</tt>" if example
|
69
|
+
md << "\n#{@schema['enum']}"
|
70
|
+
(@schema.keys - Fdoc::SchemaPresenter::FORMATTED_KEYS).each do |key|
|
71
|
+
md << "\n#{prefix}* %{key} %{@schema[key]}"
|
72
|
+
end
|
73
|
+
if items = @schema["items"]
|
74
|
+
md << "\n#{prefix}* Items"
|
75
|
+
if items.kind_of? Array
|
76
|
+
item.compact.each do |item|
|
77
|
+
md << Fdoc::SchemaPresenter.new(item, options.merge(:nested => true)).to_markdown(prefix + "\t")
|
78
|
+
end
|
79
|
+
else
|
80
|
+
md << Fdoc::SchemaPresenter.new(@schema["items"], options.merge(:nested => true)).to_markdown(prefix + "\t")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
if properties = @schema["properties"]
|
84
|
+
properties.each do |key, property|
|
85
|
+
next if property.nil?
|
86
|
+
md << "\n#{prefix}* __#{key}__:"
|
87
|
+
md << Fdoc::SchemaPresenter.new(property, options.merge(:nested => true)).to_markdown(prefix + "\t")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
md.string
|
91
|
+
end
|
92
|
+
|
61
93
|
def type
|
62
94
|
t = @schema["type"]
|
63
95
|
if t.kind_of? Array
|
@@ -82,7 +114,7 @@ class Fdoc::SchemaPresenter < Fdoc::HtmlPresenter
|
|
82
114
|
def example
|
83
115
|
return unless e = @schema["example"]
|
84
116
|
|
85
|
-
|
117
|
+
Fdoc::JsonPresenter.new(e)
|
86
118
|
end
|
87
119
|
|
88
120
|
def deprecated?
|