fdoc 0.2.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 (36) hide show
  1. data/Gemfile +2 -0
  2. data/README.md +120 -0
  3. data/Rakefile +7 -0
  4. data/bin/fdoc_to_html +77 -0
  5. data/fdoc.gemspec +36 -0
  6. data/lib/endpoint-schema.yaml +30 -0
  7. data/lib/fdoc.rb +46 -0
  8. data/lib/fdoc/endpoint.rb +110 -0
  9. data/lib/fdoc/endpoint_scaffold.rb +132 -0
  10. data/lib/fdoc/meta_service.rb +46 -0
  11. data/lib/fdoc/presenters/endpoint_presenter.rb +152 -0
  12. data/lib/fdoc/presenters/html_presenter.rb +58 -0
  13. data/lib/fdoc/presenters/meta_service_presenter.rb +65 -0
  14. data/lib/fdoc/presenters/response_code_presenter.rb +32 -0
  15. data/lib/fdoc/presenters/schema_presenter.rb +138 -0
  16. data/lib/fdoc/presenters/service_presenter.rb +56 -0
  17. data/lib/fdoc/service.rb +88 -0
  18. data/lib/fdoc/spec_watcher.rb +48 -0
  19. data/lib/fdoc/templates/endpoint.html.erb +75 -0
  20. data/lib/fdoc/templates/meta_service.html.erb +60 -0
  21. data/lib/fdoc/templates/service.html.erb +54 -0
  22. data/lib/fdoc/templates/styles.css +63 -0
  23. data/spec/fdoc/endpoint_scaffold_spec.rb +242 -0
  24. data/spec/fdoc/endpoint_spec.rb +243 -0
  25. data/spec/fdoc/presenters/endpoint_presenter_spec.rb +93 -0
  26. data/spec/fdoc/presenters/service_presenter_spec.rb +18 -0
  27. data/spec/fdoc/service_spec.rb +63 -0
  28. data/spec/fixtures/members/add-PUT.fdoc +20 -0
  29. data/spec/fixtures/members/draft-POST.fdoc +5 -0
  30. data/spec/fixtures/members/list/GET.fdoc +50 -0
  31. data/spec/fixtures/members/list/complex-params-GET.fdoc +94 -0
  32. data/spec/fixtures/members/list/filter-GET.fdoc +60 -0
  33. data/spec/fixtures/members/members.fdoc.service +11 -0
  34. data/spec/fixtures/sample_group.fdoc.meta +9 -0
  35. data/spec/spec_helper.rb +2 -0
  36. metadata +174 -0
@@ -0,0 +1,56 @@
1
+ # An HtmlPresenter for Fdoc::Service
2
+ class Fdoc::ServicePresenter < Fdoc::HtmlPresenter
3
+ attr_reader :service
4
+
5
+ def initialize(service, options = {})
6
+ super(options)
7
+ @service = service
8
+ end
9
+
10
+ def to_html
11
+ render_erb('service.html.erb')
12
+ end
13
+
14
+ def name_as_link(options = {})
15
+ path = service.meta_service ? index_path(slug_name) : index_path
16
+ '<a href="%s">%s %s</a>' % [ path, options[:prefix], service.name ]
17
+ end
18
+
19
+ def slug_name
20
+ service.name.downcase.gsub(/[ \/]/, '_')
21
+ end
22
+
23
+ def url(extension = ".html")
24
+ '%s-%s%s' % [ @endpoint.path, @endpoint.verb, extension ]
25
+ end
26
+
27
+ def endpoints
28
+ if !@endpoints
29
+ @endpoints = []
30
+ prefix = nil
31
+
32
+ service.endpoints.sort_by(&:endpoint_path).each do |endpoint|
33
+ presenter = Fdoc::EndpointPresenter.new(endpoint, options)
34
+ presenter.service_presenter = self
35
+ presenter
36
+
37
+ current_prefix = presenter.prefix
38
+
39
+ @endpoints << [] if prefix != current_prefix
40
+ @endpoints.last << presenter
41
+
42
+ prefix = current_prefix
43
+ end
44
+ end
45
+
46
+ @endpoints
47
+ end
48
+
49
+ def description
50
+ render_markdown(service.description)
51
+ end
52
+
53
+ def discussion
54
+ render_markdown(service.discussion)
55
+ end
56
+ end
@@ -0,0 +1,88 @@
1
+ require 'yaml'
2
+
3
+ # Services represent a group of Fdoc API endpoints in a directory
4
+ class Fdoc::Service
5
+ attr_reader :service_dir
6
+ attr_accessor :meta_service
7
+
8
+ def initialize(service_dir, scaffold_mode = Fdoc.scaffold_mode?)
9
+ @service_dir = File.expand_path(service_dir)
10
+ service_path = Dir["#{@service_dir}/*.fdoc.service"].first
11
+ @schema = if service_path
12
+ YAML.load_file(service_path)
13
+ elsif scaffold_mode
14
+ schema = {
15
+ 'name' => '???',
16
+ 'basePath' => '???',
17
+ 'description' => '???'
18
+ }
19
+
20
+ Dir.mkdir(service_dir) unless Dir.exist?(service_dir)
21
+ service_path = "#{service_dir}/???.fdoc.service"
22
+ File.open(service_path, "w") { |file| YAML.dump(schema, file) }
23
+
24
+ schema
25
+ else
26
+ {}
27
+ end
28
+ end
29
+
30
+ DefaultService = self.new(Fdoc::DEFAULT_SERVICE_PATH)
31
+
32
+ # Returns an Endpoint described by (verb, path)
33
+ # In scaffold_mode, it will return an EndpointScaffold an of existing file
34
+ # or create an empty EndpointScaffold
35
+ def open(verb, path, scaffold_mode = Fdoc.scaffold_mode?)
36
+ endpoint_path = path_for(verb, path)
37
+
38
+ if scaffold_mode
39
+ Fdoc::EndpointScaffold.new(endpoint_path, self)
40
+ else
41
+ Fdoc::Endpoint.new(endpoint_path, self)
42
+ end
43
+ end
44
+
45
+ def endpoint_paths
46
+ Dir["#{service_dir}/**/*.fdoc"]
47
+ end
48
+
49
+ def endpoints
50
+ endpoint_paths.map do |path|
51
+ Fdoc::Endpoint.new(path, self)
52
+ end
53
+ end
54
+
55
+ def path_for(verb, path)
56
+ flat_path = File.join(@service_dir, "#{path}-#{verb.to_s.upcase}.fdoc")
57
+ nested_path = File.join(@service_dir, "#{path}/#{verb.to_s.upcase}.fdoc")
58
+
59
+ if File.exist?(flat_path)
60
+ flat_path
61
+ elsif File.exist?(nested_path)
62
+ nested_path
63
+ else # neither exists, default to flat_path
64
+ flat_path
65
+ end
66
+ end
67
+
68
+ def name
69
+ @schema['name']
70
+ end
71
+
72
+ def base_path
73
+ base_path = @schema['basePath']
74
+ if base_path && !base_path.end_with?('/')
75
+ base_path + '/'
76
+ else
77
+ base_path
78
+ end
79
+ end
80
+
81
+ def description
82
+ @schema['description']
83
+ end
84
+
85
+ def discussion
86
+ @schema['discussion']
87
+ end
88
+ end
@@ -0,0 +1,48 @@
1
+ module Fdoc
2
+ module SpecWatcher
3
+ VERBS = [:get, :post, :put, :delete]
4
+
5
+ VERBS.each do |verb|
6
+ define_method(verb) do |*params|
7
+ action, request_params = params
8
+ request_params ||= {}
9
+ result = super(*params)
10
+
11
+ path = if respond_to?(:example) # Rspec 2
12
+ example.metadata[:fdoc]
13
+ else # Rspec 1.3.2
14
+ opts = {}
15
+ __send__(:example_group_hierarchy).each do |example|
16
+ opts.merge!(example.options)
17
+ end
18
+ opts.merge!(options)
19
+ opts[:fdoc]
20
+ end
21
+
22
+ if path
23
+ response_params = begin
24
+ JSON.parse(response.body)
25
+ rescue
26
+ {}
27
+ end
28
+ successful = Fdoc.decide_success(response_params, response.status)
29
+ verify!(verb, path, request_params, response_params, response.status,
30
+ successful)
31
+ end
32
+
33
+ result
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def verify!(verb, path, request_params, response_params, response_status,
40
+ successful)
41
+ service = Service.new(Fdoc.service_path)
42
+ endpoint = service.open(verb, path)
43
+ endpoint.consume_request(request_params, successful)
44
+ endpoint.consume_response(response_params, response_status, successful)
45
+ endpoint.persist! if endpoint.respond_to?(:persist!)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,75 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>
5
+ <%= title %>
6
+ </title>
7
+
8
+ <link rel="stylesheet" type="text/css" href="<%= css_path %>" />
9
+ </head>
10
+ <body>
11
+ <div id="page">
12
+ <div id="content">
13
+ <h1>
14
+ <%= name %>
15
+ </h1>
16
+
17
+ <div id="nav">
18
+ <% if @endpoint.service.meta_service %>
19
+ <a href="<%= index_path %>">
20
+ &#187; <%= @endpoint.service.meta_service.name %>
21
+ </a>
22
+ <% end %>
23
+ <% if service_presenter %>
24
+ <%= service_presenter.name_as_link(:prefix => '&#187;') %>
25
+ <% end %>
26
+ </div>
27
+
28
+ <%= description %>
29
+
30
+ <% if show_request? %>
31
+ <h2>Request</h2>
32
+
33
+ <h3>Example Request</h3>
34
+ <%= example_request %>
35
+
36
+ <h3>Request Parameters</h3>
37
+ <%= request_parameters %>
38
+ <% end %>
39
+
40
+ <h2>Response</h2>
41
+ <% if show_response? %>
42
+ <h3>Example Response</h3>
43
+ <%= example_response %>
44
+
45
+ <h3>Response Parameters</h3>
46
+ <%= response_parameters %>
47
+ <% end %>
48
+
49
+ <h3>Response Codes</h3>
50
+ <% if !successful_response_codes.empty? %>
51
+ <h4>Successful Response Codes</h4>
52
+ <ul>
53
+ <% successful_response_codes.each do |response_code| %>
54
+ <li>
55
+ <%= response_code.to_html %>
56
+ </li>
57
+ <% end %>
58
+ </ul>
59
+ <% end %>
60
+
61
+ <% if !successful_response_codes.empty? %>
62
+ <h4>Failure Response Codes</h4>
63
+ <ul>
64
+ <% failure_response_codes.each do |response_code| %>
65
+ <li>
66
+ <%= response_code.to_html %>
67
+ </li>
68
+ <% end %>
69
+ </ul>
70
+ <% end %>
71
+ </div>
72
+ </div>
73
+ </body>
74
+ </html>
75
+
@@ -0,0 +1,60 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>
5
+ <%= @meta_service.name %>
6
+ </title>
7
+
8
+ <link rel="stylesheet" type="text/css" href="<%= css_path %>" />
9
+ </head>
10
+ <body>
11
+ <div id="page">
12
+ <div id="content">
13
+ <h1>
14
+ <%= @meta_service.name %>
15
+ </h1>
16
+
17
+ <%= description %>
18
+
19
+ <h2>
20
+ Services
21
+ </h2>
22
+
23
+ <ul>
24
+ <% services.each do |serv| %>
25
+ <li>
26
+ <h3>
27
+ <%= serv.name_as_link %>
28
+ </h3>
29
+ </li>
30
+ <% end %>
31
+ </ul>
32
+
33
+ <h2>
34
+ Endpoints
35
+ </h2>
36
+
37
+ <% endpoints.each do |endpoint_ary| %>
38
+ <h3>
39
+ <%= endpoint_ary.first.prefix %>
40
+ </h3>
41
+ <ul>
42
+ <% endpoint_ary.each do |endpoint| %>
43
+ <li>
44
+ <%= endpoint.name_as_link %>
45
+ </li>
46
+ <% end %>
47
+ </ul>
48
+ <% end %>
49
+
50
+ <% if discussion %>
51
+ <h2>
52
+ Discussion
53
+ </h2>
54
+ <%= discussion %>
55
+ <% end %>
56
+ </div>
57
+ </div>
58
+ </body>
59
+ </html>
60
+
@@ -0,0 +1,54 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>
5
+ <%= @service.name %>
6
+ </title>
7
+
8
+ <link rel="stylesheet" type="text/css" href="<%= css_path %>" />
9
+ </head>
10
+ <body>
11
+ <div id="page">
12
+ <div id="content">
13
+ <h1>
14
+ <%= @service.name %>
15
+ </h1>
16
+
17
+ <div id="nav">
18
+ <% if @service.meta_service %>
19
+ <a href="<%= index_path %>">
20
+ &#187; <%= @service.meta_service.name %>
21
+ </a>
22
+ <% end %>
23
+ </div>
24
+
25
+ <%= description %>
26
+
27
+ <h2>
28
+ Endpoints
29
+ </h2>
30
+
31
+ <% endpoints.each do |endpoint_ary| %>
32
+ <h3>
33
+ <%= endpoint_ary.first.prefix %>
34
+ </h3>
35
+ <ul>
36
+ <% endpoint_ary.each do |endpoint| %>
37
+ <li>
38
+ <%= endpoint.name_as_link %>
39
+ </li>
40
+ <% end %>
41
+ </ul>
42
+ <% end %>
43
+
44
+ <% if discussion %>
45
+ <h2>
46
+ Discussion
47
+ </h2>
48
+ <%= discussion %>
49
+ <% end %>
50
+ </div>
51
+ </div>
52
+ </body>
53
+ </html>
54
+
@@ -0,0 +1,63 @@
1
+
2
+ body {
3
+ font: 11pt/14pt "Helvetica Neue", Helvetica, sans-serif;
4
+ margin: 0;
5
+ padding: 0;
6
+ }
7
+
8
+ #page {
9
+ background: white;
10
+ margin: 0 auto;
11
+ width: 540pt;
12
+ }
13
+
14
+ #content {
15
+ padding: 0 12pt 2em 12pt;
16
+ }
17
+
18
+ #nav { padding-bottom: 1em; }
19
+
20
+ h1, h2 {
21
+ margin: 0 -12pt;
22
+ padding: .5em 12pt;
23
+ line-height: 1.2em;
24
+ }
25
+
26
+ h1 { font-weight: bold; }
27
+ h1, h2, h3 { font-weight: 200; }
28
+
29
+ a { text-decoration: none; color: #66f; }
30
+ a:hover, a:active { text-decoration: underline; }
31
+
32
+ ul {
33
+ list-style: square;
34
+ }
35
+
36
+ ul, ol, dl {
37
+ margin: .5em 0;
38
+ }
39
+
40
+ p {
41
+ margin: .5em 0 .5em 0;
42
+ padding: 0;
43
+ }
44
+
45
+ pre code { display: block; }
46
+ tt, kbd, code {
47
+ font-family: Monaco, monospace;
48
+ font-size: 80%;
49
+ background: #eee;
50
+ padding: .1em;
51
+ border: 1pt solid #ddd;
52
+ white-space: pre-wrap;
53
+ }
54
+
55
+ .endpoint-name {}
56
+ .endpoint-name .verb { font-weight: bold; }
57
+ .endpoint-name .root {}
58
+ .endpoint-name .path { font-weight: bold; }
59
+
60
+ a .endpoint-name .verb { display: inline-block; width: 3em; text-align: right; font-size:80% }
61
+
62
+ .endpoint-name.deprecated { color: gray; }
63
+ .schema ul span.deprecated { margin-left:.5em; font-size: 80%; color: red }