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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4174d158765fdfb8f72c2625f74d77e6bff1cf27
4
- data.tar.gz: e50d4045d98142419848d2898b6a4fcf0989d9dc
3
+ metadata.gz: 57a2787c3ce762f1d68efe7aba2d1407443a7fb0
4
+ data.tar.gz: 9abaebf7932f1dffbaa3f84fe8c43cce1cf7c266
5
5
  SHA512:
6
- metadata.gz: da95663074496a8b87e5ffaa29e922b95a6d2a75f04fd64165bd9f635ea79333777f196d8ecc8f0d6725b32603b27f0bf138a518e6bf6700afec4075e6fd99db
7
- data.tar.gz: b6672a201e37c8f16829dd9f19a6836c956fb530799dbcab0cdd335a730008732a7f3d0bc8665c9bd2162f9ee991d8ee8c97946125dd977a962a1c8ddd8933c8
6
+ metadata.gz: b41c9b4518e06b4a772b846004c9f8b849526091604e6d3051d855d14caaf6ebc5ec34b557e83c4b1edd3e9b60242c670d4e345204a18a42b80a397a0930c17a
7
+ data.tar.gz: ec814f8e0e70e29a2ec528f08a1f434def22383fc41f159e25cd31eda57cd67496b1b0f8e4565c424c59125a9a75b2d6296ae33235b19a2f0ca827e027b5c98c
data/README.md CHANGED
@@ -40,6 +40,11 @@ Read that file to view all the available options.
40
40
 
41
41
  ## Changelog
42
42
 
43
+ **30/7/2015 [0.3.1]**
44
+
45
+ - Fixed a bug that was listing all the endpoints of a certain controller even if they do not have a route defined. Now, YARD-API will warn about endpoints that have an `@API` tag but could not be routed.
46
+ - Greatly improved the performance of generating the Quicklinks table.
47
+
43
48
  **29/7/2015 [0.3.0]**
44
49
 
45
50
  - major rework of the linking logic, much improvements but some stuff is broken now
@@ -1,19 +1,17 @@
1
- require 'yard/code_objects/class_object'
1
+ require 'yard/code_objects/method_object'
2
2
 
3
3
  module YARD::CodeObjects
4
4
  class APIObject < Base
5
- def path
6
- super().gsub(/\s/, '')
5
+ def self.sanitize_id(text)
6
+ text.gsub(/\s/, '')
7
7
  end
8
8
 
9
- def title
10
- [ object.title, name ].join('::')
9
+ def path
10
+ self.class.sanitize_id(super)
11
11
  end
12
- end
13
12
 
14
- class ClassObject < NamespaceObject
15
13
  def title
16
- self[:api_id] || super
14
+ [ object.title, name ].join(NSEP)
17
15
  end
18
16
  end
19
17
  end
@@ -0,0 +1,19 @@
1
+ require 'yard/code_objects/class_object'
2
+
3
+ module YARD::CodeObjects
4
+ class ClassObject < NamespaceObject
5
+ attr_reader :api_id
6
+
7
+ def api_id
8
+ @api_id ||= begin
9
+ if tag = tag('API')
10
+ tag.text.lines.first
11
+ end
12
+ end
13
+ end
14
+
15
+ def title
16
+ api_id
17
+ end
18
+ end
19
+ end
@@ -1,20 +1,14 @@
1
1
  module YARD::APIPlugin
2
- class Serializer < ::YARD::Serializers::FileSystemSerializer
3
- USNSEP = '__' # url-safe namespace separator
4
- FSSEP = '/'
2
+ class Serializer < ::YARD::Serializers::FileSystemSerializer
3
+ USNSEP = '__' # url-safe namespace separator
4
+ FSSEP = '/'
5
5
 
6
- def self.topicize(str)
7
- str.lines.first.gsub(/\W+/, '_').downcase
8
- end
6
+ def self.topicize(str)
7
+ str.lines.first.gsub(/\W+/, '_').downcase
8
+ end
9
9
 
10
10
  def serialize(object, data)
11
11
  path = File.join(basepath, serialized_path(object))
12
-
13
- if path.include?(' ')
14
- debugger
15
- end
16
-
17
- log.debug "Serializing to #{path}"
18
12
  File.open!(path, "wb") {|f| f.write data }
19
13
  end
20
14
 
@@ -27,35 +21,31 @@ module YARD::APIPlugin
27
21
  fspath = 'file.' + object.name + (extension.empty? ? '' : ".#{extension}")
28
22
  else
29
23
  fspath = if object == YARD::Registry.root
30
- "top-level-namespace"
24
+ "top-level-namespace"
31
25
  else
32
- self.class.topicize(get_api_id(object))
26
+ self.class.topicize(get_api_id(object))
33
27
  end
34
28
 
35
29
  if object.is_a?(YARD::CodeObjects::MethodObject)
36
- fspath += '_' + object.scope.to_s[0,1]
30
+ fspath += '_' + object.scope.to_s[0,1]
37
31
  end
38
32
 
39
33
  unless extension.empty?
40
- fspath += ".#{extension}"
41
- end
42
- end
43
-
44
- if (fspath.include?(' '))
45
- debugger
34
+ fspath += ".#{extension}"
35
+ end
46
36
  end
47
37
 
48
38
  fspath.gsub(/[^\w\.\-_\/]+/, '-')
49
39
  end
50
40
 
51
- def get_api_id(object)
52
- if object[:api_id]
53
- object.api_id
54
- elsif tag = object.tag(:API)
55
- tag.text.lines.first.strip
56
- else
57
- object.name.to_s
58
- end
59
- end
60
- end
41
+ def get_api_id(object)
42
+ if object[:api_id]
43
+ object.api_id
44
+ elsif tag = object.tag(:API)
45
+ tag.text.lines.first.strip
46
+ else
47
+ object.to_s
48
+ end
49
+ end
50
+ end
61
51
  end
data/lib/yard-api/tags.rb CHANGED
@@ -6,13 +6,12 @@ YARD::Tags::Library.define_tag("API response field", :request_field)
6
6
  YARD::Tags::Library.define_tag("API response field", :response_field)
7
7
  YARD::Tags::Library.define_tag("API example request", :example_request, :with_title_and_text)
8
8
  YARD::Tags::Library.define_tag("API example response", :example_response, :with_title_and_text)
9
- YARD::Tags::Library.define_tag("API subtopic", :subtopic)
10
9
  YARD::Tags::Library.define_tag("API Object Definition", :object)
11
10
  YARD::Tags::Library.define_tag("API Return Type", :returns)
12
11
  YARD::Tags::Library.define_tag("API resource is beta", :beta)
13
12
  YARD::Tags::Library.define_tag("API resource is internal", :internal)
14
13
  YARD::Tags::Library.define_tag("API empty response", :no_content)
15
- YARD::Tags::Library.define_tag("API error", :throws)
14
+ YARD::Tags::Library.define_tag("API error", :throws, :with_types)
16
15
  YARD::Tags::Library.define_tag("API warning", :warning)
17
16
  YARD::Tags::Library.define_tag("API note", :note)
18
17
  YARD::Tags::Library.define_tag("API message", :emits)
@@ -5,100 +5,23 @@ module YARD::Templates::Helpers::BaseHelper
5
5
  YARD::APIPlugin.options
6
6
  end
7
7
 
8
- # def linkify_with_api(*args)
9
- # # References to controller actions
10
- # #
11
- # # Syntax: api:ControllerName#method_name [TITLE OVERRIDE]
12
- # #
13
- # # @example Explicit reference with title defaulting to the action
14
- # # # @see api:Assignments#create
15
- # # # => <a href="assignments.html#method.assignments_api.create">create</a>
16
- # #
17
- # # @example Inline reference with an overriden title
18
- # # # Here's a link to absolute {api:Assignments#destroy destruction}
19
- # # # => <a href="assignments.html#method.assignments_api.destroy">destruction</a>
20
- # #
21
- # # @note Action links inside the All Resources section will be relative.
22
- # if args.first.is_a?(String) && args.first =~ %r{^api:([^#]+)#(.*)}
23
- # topic, controller = *lookup_topic($1.to_s)
24
- # if topic
25
- # html_file = "#{topicize topic.first}.html"
26
- # action = $2
27
- # link_url("#{html_file}#method.#{topicize(controller.name.to_s).sub("_controller", "")}.#{action}", args[1])
28
- # else
29
- # raise "couldn't find API link for #{args.first}"
30
- # end
8
+ def lookup_appendix(title)
9
+ appendix = nil
31
10
 
32
- # # References to API objects defined by @object
33
- # #
34
- # # Syntax: api:ControllerName:Object+Name [TITLE OVERRIDE]
35
- # #
36
- # # @example Explicit resource reference with title defaulting to its name
37
- # # # @see api:Assignments:Assignment
38
- # # # => <a href="assignments.html#Assignment">Assignment</a>
39
- # #
40
- # # @example Explicit resource reference with an overriden title
41
- # # # @return api:Assignments:AssignmentOverride An Assignment Override
42
- # # # => <a href="assignments.html#Assignment">An Assignment Override</a>
43
- # elsif args.first.is_a?(String) && args.first =~ %r{^api:([^:]+):(.*)}
44
- # scope_name, resource_name = $1.downcase, $2.gsub('+', ' ')
45
- # link_url("#{scope_name}.html##{resource_name}", args[1] || resource_name)
46
- # elsif args.first.is_a?(String) && args.first == 'Appendix:' && args.size > 1
47
- # __errmsg = "unable to locate referenced appendix '#{args[1]}'"
11
+ logger.debug("Looking up appendix: #{title}")
48
12
 
49
- # unless appendix = lookup_appendix(args[1].to_s)
50
- # raise __errmsg
51
- # end
13
+ if object
14
+ # try in the object scope
15
+ appendix = YARD::Registry.at(".appendix.#{object.path}.#{title}")
52
16
 
53
- # topic, controller = *lookup_topic(appendix.namespace.to_s)
17
+ # try in the object's namespace scope
18
+ if appendix.nil? && object.respond_to?(:namespace)
19
+ appendix = YARD::Registry.at(".appendix.#{object.namespace.path}.#{title}")
20
+ end
21
+ end
54
22
 
55
- # if topic
56
- # html_file = "#{topicize topic.first}.html"
57
- # bookmark = "#{appendix.name.to_s.gsub(' ', '+')}-appendix"
58
- # ret = link_url("#{html_file}##{bookmark}", appendix.title)
59
- # else
60
- # raise __errmsg
61
- # end
62
-
63
- # # A non-API link, delegate to YARD's HTML linker
64
- # else
65
- # linkify_without_api(*args)
66
- # end
67
- # end
68
-
69
- # alias_method :linkify_without_api, :linkify
70
- # alias_method :linkify, :linkify_with_api
71
-
72
- # def lookup_topic(controller_name)
73
- # controller = nil
74
- # topic = options[:resources].find do |resource, controllers|
75
- # controllers.detect do |_controller|
76
- # if _controller.path.to_s == controller_name
77
- # controller = _controller
78
- # end
79
- # end
80
- # end
81
-
82
- # [ topic, controller ]
83
- # end
84
-
85
- # def lookup_appendix(title)
86
- # appendix = nil
87
-
88
- # YARD::APIPlugin.log("Looking up appendix: #{title}") if api_options.verbose
89
-
90
- # if object
91
- # # try in the object scope
92
- # appendix = YARD::Registry.at(".appendix.#{object.path}.#{title}")
93
-
94
- # # try in the object's namespace scope
95
- # if appendix.nil? && object.respond_to?(:namespace)
96
- # appendix = YARD::Registry.at(".appendix.#{object.namespace.path}.#{title}")
97
- # end
98
- # end
99
-
100
- # appendix
101
- # end
23
+ appendix
24
+ end
102
25
 
103
26
  def tag_partial(name, tag, locals={})
104
27
  options[:tag] = tag
@@ -110,12 +33,7 @@ module YARD::Templates::Helpers::BaseHelper
110
33
  end
111
34
 
112
35
  def get_current_routes
113
- controller_name = object.parent.path.underscore
114
- controller_name.sub!("_controller", '') unless controller_name.include?('/')
115
-
116
- action = object.path.sub(/^.*#/, '').sub(/_with_.*$/, '')
117
-
118
- YARD::Templates::Helpers::RouteHelper.api_methods_for_controller_and_action(controller_name, action)
36
+ YARD::Templates::Helpers::RouteHelper.routes_for_yard_object(object)
119
37
  end
120
38
 
121
39
  def get_current_route
@@ -125,4 +43,8 @@ module YARD::Templates::Helpers::BaseHelper
125
43
  def schema_is_model?(schema)
126
44
  schema.has_key?('description') && schema.has_key?('properties')
127
45
  end
46
+
47
+ def logger
48
+ YARD::APIPlugin.logger
49
+ end
128
50
  end
@@ -5,16 +5,12 @@ module YARD::Templates::Helpers::HtmlHelper
5
5
  ::YARD::APIPlugin::Serializer.topicize(str)
6
6
  end
7
7
 
8
- def url_for_file(filename, anchor = nil)
9
- link = filename.filename
8
+ def url_for_file(file, anchor = nil)
9
+ link = file.filename
10
10
  link += (anchor ? '#' + urlencode(anchor) : '')
11
11
  link
12
12
  end
13
13
 
14
- # def url_for_api_object(name, object)
15
- # "#{object.parent.path}::#{name}"
16
- # end
17
-
18
14
  def static_pages()
19
15
  @@static_pages ||= begin
20
16
  locate_static_pages(YARD::APIPlugin.options)
@@ -61,9 +57,7 @@ module YARD::Templates::Helpers::HtmlHelper
61
57
  .capitalize
62
58
  )
63
59
 
64
- if options.verbose
65
- puts "Serializing static page #{page} (#{title})"
66
- end
60
+ logger.debug "Serializing static page #{page} (#{title})"
67
61
 
68
62
  {
69
63
  src: page,
@@ -76,10 +70,11 @@ module YARD::Templates::Helpers::HtmlHelper
76
70
 
77
71
  # override yard-appendix link_appendix
78
72
  def link_appendix(ref)
79
- puts "Linking appendix: #{ref}" if api_options.verbose
73
+ logger.debug "Linking appendix: #{ref}"
80
74
 
81
75
  unless appendix = lookup_appendix(ref.to_s)
82
- raise "Unable to locate referenced appendix '#{ref}'"
76
+ YARD::APIPlugin.on_error "Unable to locate referenced appendix '#{ref}'"
77
+ return ref
83
78
  end
84
79
 
85
80
  html_file = if options[:all_resources] && api_options.one_file
@@ -87,39 +82,23 @@ module YARD::Templates::Helpers::HtmlHelper
87
82
  elsif options[:all_resources]
88
83
  'all_resources.html'
89
84
  else
90
- topic, controller = *lookup_topic(appendix.namespace.to_s)
91
-
92
- unless topic
93
- raise "Unable to locate topic for appendix: #{ref}"
94
- end
95
-
96
- "#{topicize(topic.first)}.html"
85
+ url_for(appendix.namespace)
97
86
  end
98
87
 
99
88
  bookmark = "#{appendix.name.to_s.gsub(' ', '+')}-appendix"
100
89
  link_url("#{html_file}##{bookmark}", appendix.title).tap do |link|
101
- puts "\tAppendix link: #{link}" if api_options.verbose
90
+ logger.debug "\tAppendix link: #{link}"
102
91
  end
103
92
  end
104
93
 
105
- # TODO: this has to work with the All Resources page.
106
- def sidebar_link(title, href, options={})
107
- options[:class_name] ||= begin
108
- if object.is_a?(String) && "#{url_for(topicize(object))}.html" == href
109
- options[:class_name] = 'active'
110
- end
111
- end
112
-
113
- options[:class_name] ||= (object == href ? 'active' : nil)
114
-
115
- <<-HTML
116
- <a href="#{url_for(href)}" class="#{options[:class_name]}">#{title}</a>
117
- HTML
94
+ def sidebar_link(href, title, is_active)
95
+ link_url(href, title, { class: is_active ? 'active' : '' })
118
96
  end
119
97
 
120
98
  # Turns text into HTML using +markup+ style formatting.
121
99
  #
122
100
  # @override Syntax highlighting is not performed on the HTML block.
101
+ #
123
102
  # @param [String] text the text to format
124
103
  # @param [Symbol] markup examples are +:markdown+, +:textile+, +:rdoc+.
125
104
  # To add a custom markup type, see {MarkupHelper}
@@ -155,4 +134,23 @@ module YARD::Templates::Helpers::HtmlHelper
155
134
  tag.type
156
135
  end
157
136
  end
137
+
138
+ # @override
139
+ #
140
+ # Because we want the endpoints' URLs (an anchor part) to show up as
141
+ # "-endpoint" as opposed to "-instance_method"
142
+ def anchor_for_with_api(object)
143
+ case object
144
+ when YARD::CodeObjects::MethodObject
145
+ "#{object.name}-endpoint"
146
+ else
147
+ anchor_for_without_api(object)
148
+ end
149
+ end
150
+ alias_method :anchor_for_without_api, :anchor_for
151
+ alias_method :anchor_for, :anchor_for_with_api
152
+
153
+ def logger
154
+ YARD::APIPlugin.logger
155
+ end
158
156
  end
@@ -1,27 +1,38 @@
1
1
  module YARD::Templates::Helpers
2
2
  module RouteHelper
3
- def self.routes_for(prefix)
4
- Rails.application.routes.set
5
- end
3
+ class << self
4
+ def routes_for(prefix)
5
+ Rails.application.routes.set
6
+ end
6
7
 
7
- def self.matches_controller_and_action?(route, controller, action)
8
- route.requirements[:controller] == controller &&
9
- route.requirements[:action] == action
10
- end
8
+ def routes_for_yard_object(api_object)
9
+ controller_name = api_object.parent.path.underscore
10
+ controller_name.sub!('_controller', '') unless controller_name.include?('/')
11
11
 
12
- def self.api_methods_for_controller_and_action(controller, action)
13
- @routes ||= self.routes_for('/')
14
- controller_path = [ YARD::APIPlugin.options.route_namespace, controller ].join('/')
15
- controller_path.gsub!(/^\/|_controller$/, '')
16
- @routes.find_all { |r| matches_controller_and_action?(r, controller_path, action) }
17
- end
12
+ action = api_object.path.sub(/^.*#/, '').sub(/_with_.*$/, '')
18
13
 
19
- def self.get_route_path(route)
20
- route.path.spec.to_s.gsub("(.:format)", "")
21
- end
14
+ api_methods_for_controller_and_action(controller_name, action)
15
+ end
16
+
17
+ def matches_controller_and_action?(route, controller, action)
18
+ route.requirements[:controller] == controller &&
19
+ route.requirements[:action] == action
20
+ end
21
+
22
+ def api_methods_for_controller_and_action(controller, action)
23
+ @routes ||= routes_for('/')
24
+ controller_path = [ YARD::APIPlugin.options.route_namespace, controller ].join('/')
25
+ controller_path.gsub!(/^\/|_controller$/, '')
26
+ @routes.find_all { |r| matches_controller_and_action?(r, controller_path, action) }
27
+ end
28
+
29
+ def get_route_path(route)
30
+ route.path.spec.to_s.gsub("(.:format)", "")
31
+ end
22
32
 
23
- def self.get_route_verb(route)
24
- route.verb.source =~ /\^?(\w*)\$/ ? $1.upcase : route.verb.source
33
+ def get_route_verb(route)
34
+ route.verb.source =~ /\^?(\w*)\$/ ? $1.upcase : route.verb.source
35
+ end
25
36
  end
26
37
  end
27
38
  end
@@ -3,6 +3,7 @@ module YARD
3
3
  class Verifier < ::YARD::Verifier
4
4
  def initialize(verbose=false)
5
5
  @verbose = verbose
6
+ @routes = {}
6
7
  super()
7
8
  end
8
9
 
@@ -10,10 +11,10 @@ module YARD
10
11
  relevant = list.select { |o| relevant_object?(o) }
11
12
 
12
13
  if @verbose && relevant.any?
13
- log "#{relevant.length}/#{list.length} objects are relevant:"
14
+ logger.debug "#{relevant.length}/#{list.length} objects are relevant:"
14
15
 
15
16
  relevant.each do |object|
16
- log "\t- #{object.path}"
17
+ logger.debug "\t- #{object.path}"
17
18
  end
18
19
  end
19
20
 
@@ -26,21 +27,31 @@ module YARD
26
27
  false
27
28
  when :api
28
29
  true
29
- when :method, :class
30
- return false if object.tags('internal').any?
30
+ when :method
31
+ return false if object.has_tag?(:internal) || !object.has_tag?(:API)
32
+ routes = @routes[object.object_id]
33
+ routes ||= begin
34
+ @routes[object.object_id] = YARD::Templates::Helpers::RouteHelper.routes_for_yard_object(object)
35
+ end
31
36
 
32
- object.tags('API').any?.tap do |is_api|
33
- if @verbose && !is_api
34
- log "Resource #{object} will be ignored as it contains no @API tag."
35
- end
37
+ if routes.empty?
38
+ logger.warn (
39
+ "API Endpoint #{object.path}# has no routes defined " +
40
+ "ib routes.rb and will be ignored."
41
+ )
36
42
  end
43
+
44
+ routes.any?
45
+ when :class
46
+ return false if object.has_tag?(:internal) || !object.has_tag?(:API)
47
+ true
37
48
  else
38
49
  object.parent.nil? && relevant_object?(object.parent)
39
50
  end
40
51
  end
41
52
 
42
- def log(*args)
43
- ::YARD::APIPlugin.log(*args)
53
+ def logger
54
+ ::YARD::APIPlugin.logger
44
55
  end
45
56
  end
46
57
  end
@@ -1,5 +1,5 @@
1
1
  module YARD
2
2
  module APIPlugin
3
- VERSION = "0.3.0"
3
+ VERSION = "0.3.1"
4
4
  end
5
5
  end
data/lib/yard-api.rb CHANGED
@@ -57,6 +57,7 @@ module YARD
57
57
  require 'yard-api/verifier'
58
58
  require 'yard-api/serializer'
59
59
  require 'yard-api/code_objects/api_object'
60
+ require 'yard-api/code_objects/class_object'
60
61
  require 'yard-api/templates/helpers/base_helper'
61
62
  require 'yard-api/templates/helpers/html_helper'
62
63
  require 'yard-api/templates/helpers/route_helper'
@@ -80,6 +81,8 @@ module YARD
80
81
  default_attr :argument_tags, []
81
82
  default_attr :output, nil
82
83
  default_attr :json_objects_map, {}
84
+ default_attr :endpoints, nil
85
+ default_attr :resource_name, nil
83
86
  end
84
87
  end
85
88
  end
@@ -294,6 +294,7 @@ h4 {
294
294
  .argument-listing__argument-values,
295
295
  .argument-listing__argument-required {
296
296
  display: block;
297
+ word-wrap: break-word;
297
298
  }
298
299
 
299
300
  code.argument-listing__argument-name {
@@ -380,4 +381,4 @@ code.argument-listing__argument-name {
380
381
 
381
382
  .object-synopsis__toggler:hover {
382
383
  text-decoration: underline;
383
- }
384
+ }
@@ -29,22 +29,6 @@ function makeObjectSynopsisBlocksTogglable() {
29
29
  }
30
30
 
31
31
  $(function() {
32
- $('.method-details__name').each(function(i, el) {
33
- var $a = $(el).find('a');
34
- var anchorText = $.trim($a[0].innerHTML);
35
-
36
- if (anchorText === '') {
37
- return;
38
- }
39
-
40
- var $row = $('#topicQuicklinks');
41
- var $link = $('<a/>', {
42
- href: '#' + $(el).attr('name')
43
- }).html(anchorText);
44
-
45
- $('<li>').append($link).appendTo($row);
46
- });
47
-
48
32
  $('#content pre').each(function(i, block) {
49
33
  var code;
50
34
  var $block = $(block);