fdoc 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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?