fdoc 0.3.0 → 0.3.1
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 +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?
|