yard-api 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,86 +5,45 @@ include YARD::Templates::Helpers::ModuleHelper
5
5
  include YARD::Templates::Helpers::FilterHelper
6
6
 
7
7
  def init
8
- YARD::APIPlugin.logger.info "YARD-API: starting."
8
+ logger.info "YARD-API: starting."
9
9
 
10
10
  options.serializer = YARD::APIPlugin::Serializer.new
11
11
  options.serializer.basepath = api_options.output
12
12
 
13
- options.objects.each do |object|
14
- object[:api_id] = object.tag('API').text.lines.first
15
- end
16
-
17
- options[:resources] = options[:objects].
13
+ options[:resources] = options.objects.
18
14
  group_by { |o| o[:api_id] }.
19
- sort_by { |o| o.first }
15
+ sort_by { |o| o.first }
20
16
 
21
- build_json_objects_map
17
+ generate_endpoint_list(options[:resources])
18
+ generate_code_objects(options[:resources])
19
+ generate_json_object_map
22
20
  generate_assets
23
21
 
24
22
  if api_options.one_file
25
- return serialize_onefile_index
26
- end
27
-
28
- serialize_index if File.exists?(api_options['readme'] || '')
29
- serialize_static_pages
30
- serialize_resource_index if api_options['resource_index']
31
-
32
- options.delete(:objects)
33
-
34
- options[:resources].each do |resource, controllers|
35
- controllers.each do |controller|
36
- if controller.is_a?(YARD::CodeObjects::NamespaceObject)
37
- co = YARD::CodeObjects::ClassObject.new(
38
- YARD::APIPlugin::Registry.root,
39
- controller[:api_id]
40
- )
41
-
42
- YARD::Registry.register co
43
-
44
- (controller.tags(:object) + controller.tags(:model)).each do |tag|
45
- tag_co = YARD::CodeObjects::APIObject.new(co, tag.text.lines[0].strip)
46
- tag_co.object = tag.object
47
-
48
- # Make an alias on the global API namespace, for convenience.
49
- # Now an object called "Bar" under the "Foo" controller can be
50
- # referenced using [API::Bar] as well as [API::Foo::Bar] which will
51
- # never face any conflicts.
52
- shortcut_tag_co = YARD::CodeObjects::APIObject.new(YARD::APIPlugin::Registry.root, tag.text.lines[0].strip)
53
- shortcut_tag_co.object = tag.object
54
-
55
- # We need to override #namespace because #url_for() uses it to
56
- # generate the url, which has to be done usign #object and not
57
- # #namespace (which points to P("API") and we want
58
- # P("API::#{tag.object.path}")).
59
- shortcut_tag_co.namespace = tag.object
60
-
61
- YARD::Registry.register(tag_co)
62
- YARD::Registry.register(shortcut_tag_co)
63
- end
64
- end
65
- end
66
-
67
- # debugger
68
-
69
- if controllers.length > 1
70
- debugger
23
+ serialize_onefile_index
24
+ else
25
+ serialize_index if File.exists?(api_options['readme'] || '')
26
+ serialize_static_pages
27
+ serialize_resource_index if api_options['resource_index']
28
+
29
+ options[:resources].each do |resource, controllers|
30
+ serialize_resource(resource, controllers)
71
31
  end
72
-
73
- serialize_resource(resource, controllers)
74
32
  end
75
33
  end
76
34
 
77
35
  def serialize(object)
78
36
  options[:object] = object
37
+
79
38
  Templates::Engine.with_serializer(object, options[:serializer]) do
80
39
  T('layout').run(options)
81
40
  end
41
+
42
+ options.delete(:object)
82
43
  end
83
44
 
84
45
  def serialize_resource(resource, controllers)
85
- YARD::APIPlugin.logger.info('=' * 80)
86
- YARD::APIPlugin.logger.info ">>> #{resource} <<< (#{controllers})"
87
- YARD::APIPlugin.logger.info('-' * 80)
46
+ logger.debug "[=- #{resource} (#{controllers}) -=]"
88
47
 
89
48
  options[:object] = resource
90
49
  options[:controllers] = controllers
@@ -95,13 +54,17 @@ def serialize_resource(resource, controllers)
95
54
  end
96
55
  end
97
56
 
57
+ options.delete(:object)
98
58
  options.delete(:controllers)
99
- YARD::APIPlugin.logger.info('-' * 80)
59
+
60
+ logger.info('-' * 80)
100
61
  end
101
62
 
102
63
  def serialize_index
103
64
  options[:file] = api_options['readme']
65
+
104
66
  serialize('index.html')
67
+
105
68
  options.delete(:file)
106
69
  end
107
70
 
@@ -117,23 +80,21 @@ end
117
80
 
118
81
  def serialize_resource_index
119
82
  options[:all_resources] = true
83
+ options[:object] = 'all_resources.html'
120
84
 
121
85
  Templates::Engine.with_serializer("all_resources.html", options[:serializer]) do
122
86
  T('layout').run(options)
123
87
  end
124
88
 
89
+ options.delete(:object)
125
90
  options.delete(:all_resources)
126
91
  end
127
92
 
128
- def asset(path, content)
129
- options[:serializer].serialize(path, content) if options[:serializer]
130
- end
131
-
132
93
  def generate_assets
133
94
  layout = Object.new.extend(T('layout'))
134
95
 
135
- [].concat(layout.stylesheets).concat(layout.javascripts).uniq.each do |file|
136
- asset(file, file(file, true))
96
+ (layout.stylesheets + layout.javascripts).uniq.each do |file|
97
+ options.serializer.serialize(file, file(file, true))
137
98
  end
138
99
  end
139
100
 
@@ -145,7 +106,57 @@ def serialize_static_pages
145
106
  end
146
107
  end
147
108
 
148
- def build_json_objects_map
109
+ def generate_code_objects(resources)
110
+ resources.each do |resource, controllers|
111
+ controllers.each do |controller|
112
+ if controller.is_a?(YARD::CodeObjects::NamespaceObject)
113
+ co = YARD::CodeObjects::ClassObject.new(
114
+ YARD::APIPlugin::Registry.root,
115
+ controller[:api_id]
116
+ )
117
+
118
+ YARD::Registry.register co
119
+
120
+ (controller.tags(:object) + controller.tags(:model)).each do |tag|
121
+ id = YARD::CodeObjects::APIObject.sanitize_id(tag.text.lines[0].strip)
122
+ tag_co = YARD::CodeObjects::APIObject.new(co, id)
123
+ tag_co.object = tag.object
124
+
125
+ # Make an alias on the global API namespace, for convenience.
126
+ # Now an object called "Bar" under the "Foo" controller can be
127
+ # referenced using [API::Bar] as well as [API::Foo::Bar] which will
128
+ # never face any conflicts.
129
+ shortcut_tag_co = YARD::CodeObjects::APIObject.new(YARD::APIPlugin::Registry.root, id)
130
+ shortcut_tag_co.object = tag.object
131
+
132
+ # We need to override #namespace because #url_for() uses it to
133
+ # generate the url, which has to be done usign #object and not
134
+ # #namespace (which points to P("API") and we want
135
+ # P("API::#{tag.object.path}")).
136
+ shortcut_tag_co.namespace = tag.object
137
+
138
+ YARD::Registry.register(tag_co)
139
+ YARD::Registry.register(shortcut_tag_co)
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ def generate_endpoint_list(resources)
147
+ options[:endpoints] ||= begin
148
+ resources.reduce({}) do |hsh, (resource, controllers)|
149
+ meths = controllers.map do |controller|
150
+ controller.meths(:inherited => false, :included => false)
151
+ end
152
+
153
+ hsh[resource] = run_verifier(meths.flatten)
154
+ hsh
155
+ end
156
+ end
157
+ end
158
+
159
+ def generate_json_object_map
149
160
  options[:json_objects_map] = {}
150
161
  options[:json_objects] = {}
151
162
  options[:resources].each do |r,cs|
@@ -21,6 +21,9 @@
21
21
  <% end %>
22
22
  <% end %>
23
23
 
24
+ <% @endpoints = options[:endpoints].values.flatten %>
25
+ <%= erb "../../topic/html/endpoints" %>
26
+
24
27
  <% options[:resources].each do |resource, controllers| %>
25
28
  <%= yieldall object: resource, controllers: controllers %>
26
29
  <% end %>
@@ -7,9 +7,18 @@
7
7
  <h2 class="sidebar__heading">API</h2>
8
8
 
9
9
  <nav id="static_pages">
10
- <%= sidebar_link(api_options.readme_page_title, 'index.html') %>
10
+ <%=
11
+ sidebar_link(
12
+ 'index.html',
13
+ api_options.readme_page_title,
14
+ options[:file] == api_options['readme']
15
+ )
16
+ %>
17
+
11
18
  <% static_pages.each do |page| %>
12
- <%= sidebar_link(page[:title], page[:filename]) %>
19
+ <%=
20
+ sidebar_link(page[:filename], page[:title], object == page[:filename])
21
+ %>
13
22
  <% end %>
14
23
  </nav>
15
24
 
@@ -17,11 +26,19 @@
17
26
 
18
27
  <nav>
19
28
  <% if api_options['resource_index'] %>
20
- <%= sidebar_link('All Resources', 'all_resources.html') %>
29
+ <%=
30
+ sidebar_link(
31
+ 'all_resources.html',
32
+ 'All Resources',
33
+ !!options[:all_resources]
34
+ )
35
+ %>
21
36
  <% end %>
22
37
 
23
38
  <% options[:resources].each do |resource, controllers| %>
24
- <%= sidebar_link resource, controllers.first %>
39
+ <%=
40
+ sidebar_link(url_for(controllers[0]), resource.dup, resource == object)
41
+ %>
25
42
  <% end %>
26
43
  </nav>
27
44
  </div>
@@ -1,12 +1,8 @@
1
1
  <div class='method-details'>
2
- <h2
3
- class='method-details__name'
4
- name='<%= @props[:method_link] %>'
5
- data-subtopic='<%= @props[:subtopic] %>'
6
- >
2
+ <h2 class='method-details__name'>
7
3
  <a
8
- name='<%= @props[:method_link] %>'
9
- href='#<%= @props[:method_link] %>'
4
+ name='<%= anchor_for(object) %>'
5
+ href='<%= url_for(object) %>'
10
6
  class='method-details__name-link'
11
7
  >
12
8
  <%= object.tag('API').text %>
@@ -4,7 +4,6 @@ RouteHelper = YARD::Templates::Helpers::RouteHelper
4
4
 
5
5
  def init
6
6
  sections :header, [:method_signature, T('docstring')]
7
-
8
7
 
9
8
  super
10
9
  end
@@ -23,19 +22,13 @@ def header
23
22
  end
24
23
 
25
24
  @props = {}
26
- @props[:method_link] = [
27
- 'method',
28
- route.requirements[:controller],
29
- route.requirements[:action]
30
- ].join('.')
31
-
32
- @props[:subtopic] = (object.parent.tag('subtopic') || object.parent.tag('API')).text
25
+ @props[:method_link] = url_for(object)
33
26
 
34
27
  controller_path = "app/controllers/#{route.requirements[:controller]}_controller.rb"
35
28
 
36
29
  # TODO: can't we just test using object.file instead of relying on Rails ?
37
30
  controller_path = nil unless File.file?(Rails.root+controller_path)
38
-
31
+
39
32
  if controller_path
40
33
  @props[:path] = controller_path
41
34
  @props[:controller] = route.requirements[:controller].camelize
@@ -2,20 +2,25 @@
2
2
  <% multi_dialect = options[:multi_dialect] %>
3
3
  <% title = tag.name %>
4
4
  <%
5
- json_blob = begin
6
- JSON.parse(tag.text).to_json
7
- rescue JSON::ParserError => e
8
- puts '*' * 80
9
- puts " Invalid JSON payload in endpoint: #{object.path.to_s}"
10
- puts " Please make sure it is valid JSON."
11
- puts '*' * 80
12
-
13
- if api_options.strict then
14
- raise e
15
- else
16
- return
17
- end
18
- end
5
+ json_blob = begin
6
+ text = tag.text.strip
7
+ if text[0] == '{' && text.last == '}'
8
+ JSON.parse(tag.text).to_json
9
+ else
10
+ nil
11
+ end
12
+ rescue JSON::ParserError => e
13
+ puts '*' * 80
14
+ puts " Invalid JSON payload in endpoint: #{object.path.to_s}"
15
+ puts " Please make sure it is valid JSON."
16
+ puts '*' * 80
17
+
18
+ if api_options.strict then
19
+ raise e
20
+ else
21
+ return
22
+ end
23
+ end
19
24
  %>
20
25
 
21
26
  <% if title && !title.empty? %>
@@ -23,29 +28,30 @@
23
28
  <% end %>
24
29
 
25
30
  <div class="example-codeblocks">
26
- <% if multi_dialect %>
27
- <div class="example-codeblocks__tabs">
28
- <a class="example-codeblocks__tab">JSON</a>
29
- <a class="example-codeblocks__tab">cURL</a>
30
- </div>
31
- <% end %>
31
+ <% if multi_dialect && json_blob %>
32
+ <div class="example-codeblocks__tabs">
33
+ <a class="example-codeblocks__tab">JSON</a>
34
+ <a class="example-codeblocks__tab">cURL</a>
35
+ </div>
36
+ <% end %>
37
+
38
+ <pre class="example-codeblocks__example example code js"><%= html_syntax_highlight(tag.text, :plain) %></pre>
32
39
 
33
- <pre class="example-codeblocks__example example code js"><%= html_syntax_highlight(tag.text, :plain) %></pre>
34
- <% if multi_dialect %>
35
- <pre class="example-codeblocks__example example code shell">
40
+ <% if multi_dialect && json_blob %>
41
+ <pre class="example-codeblocks__example example code shell">
36
42
  curl \
37
43
  -X <%= YARD::Templates::Helpers::RouteHelper.get_route_verb(options[:current_route]) %> \
38
44
  -H "Authorization: Bearer $TOKEN" \
39
45
  -H 'Content-Type: application/json' \
40
46
  -H 'Accept: application/json' \
41
- -d '<%= JSON.parse(tag.text).to_json %>' \
47
+ -d '<%= json_blob %>' \
42
48
  <%=
43
- [
44
- "http://&lt;#{api_options.url_title}&gt;",
45
- api_options.url_prefix,
46
- YARD::Templates::Helpers::RouteHelper.get_route_path(options[:current_route])
47
- ].compact.reject(&:empty?).join('')
49
+ [
50
+ "http://&lt;#{api_options.url_title}&gt;",
51
+ api_options.url_prefix,
52
+ YARD::Templates::Helpers::RouteHelper.get_route_path(options[:current_route])
53
+ ].compact.reject(&:empty?).join('')
48
54
  %>
49
- </pre>
50
- <% end %>
55
+ </pre>
56
+ <% end %>
51
57
  </div>
@@ -1,11 +1,11 @@
1
+ <% name = @resource_name %>
2
+
1
3
  <p class="returns-tag">
2
4
  <% if @is_list %>
3
5
  Returns a list of
4
6
  <% else %>
5
- Returns <%= %w[a e i o u].include?(@resource_name[0]) ? 'an' : 'a' %>
7
+ Returns <%= %w[a e i o u].include?(name[0]) ? 'an' : 'a' %>
6
8
  <% end %>
7
9
 
8
- <a href='<%= @resource_name %>.html#<%= @object_name %>'>
9
- <%= @is_list ? @object_name.pluralize : @object_name %>
10
- </a>
10
+ <%= link_url url_for(object), @is_list ? name.pluralize : name %>
11
11
  </p>
@@ -1,46 +1,50 @@
1
+ <% has_code = false %>
2
+
1
3
  <h4>Possible Errors:</h4>
2
4
  <p>
3
- Your request may be rejected with a <code>400 Bad Request</code> HTTP status
4
- code and an error from the following set:
5
+ Your request may be rejected with an HTTP status code and an error message
6
+ from the following set:
5
7
  </p>
6
8
 
7
9
  <table class="endpoint-errors">
8
10
  <thead>
9
11
  <tr>
10
- <!-- <th>Error</th> -->
11
- <th>Code</th>
12
- <th>Message</th>
12
+ <th>Response Code</th>
13
+ <th>Details</th>
13
14
  </tr>
14
15
  </thead>
15
16
 
16
17
  <tbody>
17
- <% @error_tags.each do |tag| %>
18
+ <% object.tags(:throws).each do |tag| %>
18
19
  <%
19
- tag.text ||= ''
20
- error = {}
21
- buf = tag.text.strip.sub(/^\[([^\]]+)\]/, '').strip
22
- error[:status] = $1
23
- buf = buf.sub(/"\[([^\]]+)\]([^"]+)"/, '').strip
24
- error[:code] = $1.strip
25
- error[:message] = $2
26
- error[:cause] = buf.strip
20
+ error = JSON.parse(tag.text)
21
+ error[:reason] = error['reason'] || ''
22
+ error[:message] = error['message'] || ''
23
+ error[:status] = tag.type
27
24
  %>
25
+
28
26
  <tr>
29
- <% if false %>
27
+ <td>
28
+ <%= error[:status] %>
29
+ <%= Rack::Utils::HTTP_STATUS_CODES[error[:status].to_i] %>
30
+ </td>
31
+
32
+ <% if has_code %>
30
33
  <td>
31
- <%= error[:status] %>
32
- <%= Rack::Utils::HTTP_STATUS_CODES[error[:status].to_i] %>
34
+ <code><%= error[:code] %></code>
33
35
  </td>
34
36
  <% end %>
37
+
35
38
  <td>
36
- <code><%= error[:code] %></code>
37
- </td>
38
- <td>
39
- <%= error[:message] %>
40
- <% unless error[:cause].empty? %>
41
- <hr />
42
- <strong>Cause:</strong>
43
- <%= error[:cause].sub(/^because /i, '').capitalize %>
39
+ <code>"<%= error[:message] %>"</code>
40
+
41
+ <% unless error[:reason].empty? %>
42
+ <p class="type-mute">
43
+ <em>
44
+ <!-- <p><strong>Reason:</strong></p> -->
45
+ <%= linkify error[:reason] %>
46
+ </em>
47
+ </p>
44
48
  <% end %>
45
49
  </td>
46
50
  </tr>
@@ -67,10 +67,6 @@ def returns
67
67
  @is_list = false
68
68
  end
69
69
 
70
- # if @object_name =~ /\{(\S+)\}/
71
- # @object_name = $1
72
- # end
73
-
74
70
  @resource_name = options[:json_objects_map][@object_name]
75
71
  return unless @resource_name
76
72
  erb(:returns)
@@ -0,0 +1,21 @@
1
+ <section>
2
+ <% if options[:all_resources] %>
3
+ <h1>All Resources</h1>
4
+ <% end %>
5
+
6
+ <h2>Interfaces</h2>
7
+
8
+ <% if @endpoints.empty? %>
9
+ <p>
10
+ <em class="type-mute">This API currently has no endpoints available.</em>
11
+ </p>
12
+ <% else %>
13
+ <ul class="topic__quicklinks">
14
+ <% @endpoints.each do |endpoint| %>
15
+ <li>
16
+ <%= link_url '#' + anchor_for(endpoint), endpoint.tag(:API).text %>
17
+ </li>
18
+ <% end %>
19
+ </ul>
20
+ <% end %>
21
+ </section>
@@ -1,6 +1,10 @@
1
1
  <div class="service">
2
- <% if @resource %>
3
- <h1><%= @resource %> API</h1>
2
+ <% if object %>
3
+ <% if options[:all_resources] %>
4
+ <h2><%= object.to_s %> API</h2>
5
+ <% else %>
6
+ <h1><%= object.to_s %> API</h2>
7
+ <% end %>
4
8
  <% end %>
5
9
 
6
10
  <%= yieldall %>
@@ -1,5 +1,5 @@
1
1
  <div>
2
- <% @meths.each_with_index do |meth, i| %>
2
+ <% @endpoints.each_with_index do |meth, i| %>
3
3
  <%= yieldall :object => meth, :index => i %>
4
4
  <% end %>
5
5
  </div>
@@ -0,0 +1,26 @@
1
+ <section>
2
+ <h2>Object Synopses</h2>
3
+
4
+ <% @json_objects.each do |name, json| %>
5
+ <div class="object-synopsis">
6
+ <h3 class="object-synopsis__header" id="<%= name %>-api">
7
+ <span class="object-synopsis__header-text"><%= name %></span>
8
+ <button class="object-synopsis__toggler"></button>
9
+ </h3>
10
+
11
+ <div class="object-synopsis__content">
12
+ <div class="object-synopsis__content-description">
13
+ <% if schema_is_model?(json) && !json['description'].empty? %>
14
+ <%= htmlify json['description'] %>
15
+ <% end %>
16
+ </div>
17
+
18
+ <%=
19
+ tag_partial("../../tags/html/argument/_list", nil, {
20
+ argument_tags: properties_of_model_as_tags(json)
21
+ })
22
+ %>
23
+ </div>
24
+ </div>
25
+ <% end %>
26
+ </section>
@@ -2,10 +2,9 @@ require 'json'
2
2
 
3
3
  def init
4
4
  sections :header, [:topic_doc, :method_details_list, [T('method_details')]]
5
- @resource = object
6
- @beta = options[:controllers].any? { |c| c.tag('beta') }
7
- @meths = options[:controllers].map { |c| c.meths(:inherited => false, :included => false) }.flatten
8
- @meths = run_verifier(@meths)
5
+ @endpoints = options[:endpoints][object]
6
+
7
+ super
9
8
  end
10
9
 
11
10
  def method_details_list
@@ -14,10 +13,7 @@ end
14
13
 
15
14
  def topic_doc
16
15
  @docstring = options[:controllers].map { |c| c.docstring }.join("\n\n")
17
- @object = @object.dup
18
- @controller = options[:controllers].first
19
- def @object.source_type; nil; end
20
- @json_objects = options[:json_objects][@resource] || []
16
+ @json_objects = options[:json_objects][object] || []
21
17
  erb(:topic_doc)
22
18
  end
23
19