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.
Files changed (31) hide show
  1. data/README.md +15 -0
  2. data/lib/fdoc.rb +2 -1
  3. data/lib/fdoc/cli.rb +53 -15
  4. data/lib/fdoc/meta_service.rb +1 -1
  5. data/lib/fdoc/presenters/{html_presenter.rb → base_presenter.rb} +12 -17
  6. data/lib/fdoc/presenters/endpoint_presenter.rb +22 -24
  7. data/lib/fdoc/presenters/json_presenter.rb +31 -0
  8. data/lib/fdoc/presenters/meta_service_presenter.rb +25 -6
  9. data/lib/fdoc/presenters/response_code_presenter.rb +13 -4
  10. data/lib/fdoc/presenters/schema_presenter.rb +36 -4
  11. data/lib/fdoc/presenters/service_presenter.rb +25 -6
  12. data/lib/fdoc/templates/endpoint.html.erb +5 -5
  13. data/lib/fdoc/templates/endpoint.md.erb +40 -0
  14. data/lib/fdoc/templates/meta_service.html.erb +3 -1
  15. data/lib/fdoc/templates/meta_service.md.erb +20 -0
  16. data/lib/fdoc/templates/service.html.erb +4 -2
  17. data/lib/fdoc/templates/service.md.erb +18 -0
  18. data/lib/fdoc/templates/shared/endpoint_name.html.erb +8 -0
  19. data/lib/fdoc/templates/shared/endpoint_name.md.erb +1 -0
  20. data/lib/fdoc/templates/styles.css +1 -1
  21. data/spec/fdoc/cli_spec.rb +87 -40
  22. data/spec/fdoc/endpoint_scaffold_spec.rb +5 -5
  23. data/spec/fdoc/endpoint_spec.rb +18 -18
  24. data/spec/fdoc/presenters/base_presenter_spec.rb +28 -0
  25. data/spec/fdoc/presenters/endpoint_presenter_spec.rb +8 -1
  26. data/spec/fdoc/presenters/meta_service_presenter_spec.rb +38 -0
  27. data/spec/fdoc/presenters/schema_presenter_spec.rb +34 -0
  28. data/spec/fdoc/presenters/service_presenter_spec.rb +24 -1
  29. data/spec/fixtures/members/list/complex-params-GET.fdoc +20 -20
  30. data/spec/spec_helper.rb +1 -0
  31. 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.
@@ -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/html_presenter'
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'
@@ -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 => "HTML output path"
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 HTML"
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
- in_root do
37
- copy_file("styles.css")
38
- create_file("index.html", meta_presenter.to_html) if has_meta_service?
38
+ if options[:format] == 'markdown'
39
+ convert_to_markdown
40
+ else
41
+ convert_to_html
39
42
  end
43
+ end
40
44
 
41
- service_presenters.each do |service_presenter|
42
- inside_service_presenter(service_presenter) do
43
- create_file("index.html", service_presenter.to_html)
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
- service_presenter.endpoints.each do |endpoint_prefix_group|
46
- endpoint_prefix_group.each do |endpoint|
47
- create_file(endpoint.url, endpoint.to_html)
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("../html", origin_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
@@ -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
- # HtmlPresenters assist in generating Html for fdoc classes.
6
- # HtmlPresenters is an abstract class with a lot of helper methods
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::HtmlPresenter
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(File.dirname(__FILE__), "../templates", erb_name)
18
- template = ERB.new(File.read(template_path))
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 render_json(json)
31
- if json.kind_of? String
32
- '<tt>&quot;%s&quot;</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
- # HtmlPresenter for an Endpoint
2
- class Fdoc::EndpointPresenter < Fdoc::HtmlPresenter
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 name
15
- <<-EOS
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
- ).to_html
51
+ )
66
52
  end
67
53
 
68
54
  def response_parameters
69
- Fdoc::SchemaPresenter.new(endpoint.response_parameters, options).to_html
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
- render_json(example_from_schema(endpoint.request_parameters))
73
+ Fdoc::JsonPresenter.new(example_from_schema(endpoint.request_parameters))
88
74
  end
89
75
 
90
76
  def example_response
91
- render_json(example_from_schema(endpoint.response_parameters))
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>&quot;%s&quot;</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
- # HtmlPresenter for Fdoc::MetaService
2
- class Fdoc::MetaServicePresenter < Fdoc::HtmlPresenter
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 discussion
46
- render_markdown(meta_service.discussion)
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 HtmlPresenter for ResponseCodes
2
- class Fdoc::ResponseCodePresenter < Fdoc::HtmlPresenter
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(response_code["description"])
34
+ render_markdown(description_raw)
31
35
  end
32
- end
36
+
37
+ def description_raw
38
+ response_code["description"]
39
+ end
40
+
41
+ end
@@ -1,6 +1,6 @@
1
- # An HtmlPresenter for a JSON Schema fragment. Like most JSON
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::HtmlPresenter
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
- render_json(e)
117
+ Fdoc::JsonPresenter.new(e)
86
118
  end
87
119
 
88
120
  def deprecated?