yard-api 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.
@@ -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