fdoc 0.2.1

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