yard-api 0.1.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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/LICENSE +674 -0
- data/README.md +27 -0
- data/lib/yard-api/markup/redcarpet.rb +42 -0
- data/lib/yard-api/options.rb +13 -0
- data/lib/yard-api/railtie.rb +11 -0
- data/lib/yard-api/tags.rb +16 -0
- data/lib/yard-api/templates/helpers/base_helper.rb +108 -0
- data/lib/yard-api/templates/helpers/html_helper.rb +87 -0
- data/lib/yard-api/templates/helpers/route_helper.rb +19 -0
- data/lib/yard-api/verifier.rb +34 -0
- data/lib/yard-api/version.rb +5 -0
- data/lib/yard-api/yardoc_task.rb +78 -0
- data/lib/yard-api.rb +48 -0
- data/spec/spec_helper.rb +44 -0
- data/tasks/yard_api.rake +13 -0
- data/templates/api/appendix/html/setup.rb +19 -0
- data/templates/api/docstring/html/setup.rb +3 -0
- data/templates/api/docstring/html/text.erb +6 -0
- data/templates/api/fulldoc/html/setup.rb +78 -0
- data/templates/api/layout/html/footer.erb +11 -0
- data/templates/api/layout/html/headers.erb +19 -0
- data/templates/api/layout/html/layout.erb +17 -0
- data/templates/api/layout/html/scripts.erb +35 -0
- data/templates/api/layout/html/setup.rb +63 -0
- data/templates/api/layout/html/sidebar.erb +29 -0
- data/templates/api/method_details/html/header.erb +12 -0
- data/templates/api/method_details/html/method_signature.erb +19 -0
- data/templates/api/method_details/html/setup.rb +35 -0
- data/templates/api/tags/html/_example_code_block.erb +8 -0
- data/templates/api/tags/html/argument.erb +32 -0
- data/templates/api/tags/html/emits.erb +7 -0
- data/templates/api/tags/html/example_request.erb +13 -0
- data/templates/api/tags/html/example_response.erb +14 -0
- data/templates/api/tags/html/generic_tag.erb +20 -0
- data/templates/api/tags/html/index.erb +3 -0
- data/templates/api/tags/html/no_content.erb +3 -0
- data/templates/api/tags/html/note.erb +6 -0
- data/templates/api/tags/html/returns.erb +10 -0
- data/templates/api/tags/html/see.erb +8 -0
- data/templates/api/tags/html/throws.erb +49 -0
- data/templates/api/tags/html/warning.erb +6 -0
- data/templates/api/tags/setup.rb +83 -0
- data/templates/api/topic/html/header.erb +7 -0
- data/templates/api/topic/html/method_details_list.erb +5 -0
- data/templates/api/topic/html/setup.rb +58 -0
- data/templates/api/topic/html/topic_doc.erb +27 -0
- data/yard-api.gemspec +22 -0
- metadata +149 -0
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# yard-api
|
2
|
+
|
3
|
+
[](https://travis-ci.org/amireh/yard-api)
|
4
|
+
|
5
|
+
TODO
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
TODO
|
10
|
+
|
11
|
+
## Notes
|
12
|
+
|
13
|
+
- can only document classes and class methods; modules, root objects, and constants are ignored
|
14
|
+
|
15
|
+
## Changelog
|
16
|
+
|
17
|
+
**10/9/2014**
|
18
|
+
|
19
|
+
- Support for github-flavored markdown when you're using Markdown as a markup, and `redcarpet` as the provider
|
20
|
+
- Syntax highlighting for multiple languages (with auto-detection) using [highlight.js](https://highlightjs.org/)
|
21
|
+
- `@example_response` and `@example_request` tags now support a title for the response
|
22
|
+
- A new option: `copyright` for displaying a copyright in the footer of every page
|
23
|
+
- A new option: `footer_note` for displaying a custom note, like linking to the project's source code, in the footer of every page
|
24
|
+
|
25
|
+
## License
|
26
|
+
|
27
|
+
Released under the [AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html) license.
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'redcarpet'
|
2
|
+
require 'yard/templates/helpers/markup_helper'
|
3
|
+
|
4
|
+
module YARD::APIPlugin::Markup
|
5
|
+
# TODO: make this configurable
|
6
|
+
class RedcarpetDelegate
|
7
|
+
Extensions = {
|
8
|
+
no_intra_emphasis: true,
|
9
|
+
fenced_code_blocks: true,
|
10
|
+
autolink: true,
|
11
|
+
tables: true,
|
12
|
+
lax_spacing: false,
|
13
|
+
space_after_headers: true,
|
14
|
+
underline: true,
|
15
|
+
highlight: true,
|
16
|
+
footnotes: true
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
RendererOptions = {
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def initialize(text, extensions_and_options=nil)
|
23
|
+
@renderer = Redcarpet::Render::HTML.new(RendererOptions)
|
24
|
+
@markdown = Redcarpet::Markdown.new(@renderer, Extensions)
|
25
|
+
@text = text
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_html
|
29
|
+
@markdown.render(@text)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module YARD::Templates::Helpers
|
35
|
+
if MarkupHelper.markup_cache.nil?
|
36
|
+
MarkupHelper.clear_markup_cache
|
37
|
+
end
|
38
|
+
|
39
|
+
MarkupHelper.markup_cache[:markdown] ||= {}
|
40
|
+
MarkupHelper.markup_cache[:markdown][:class] = YARD::APIPlugin::Markup::RedcarpetDelegate
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module YARD::APIPlugin
|
2
|
+
class Options < YARD::Options
|
3
|
+
default_attr :title, 'Rails API Project'
|
4
|
+
default_attr :static, []
|
5
|
+
default_attr :files, []
|
6
|
+
default_attr :route_namespace, ''
|
7
|
+
|
8
|
+
default_attr :footer_copyright, nil
|
9
|
+
default_attr :footer_note, nil
|
10
|
+
|
11
|
+
attr_accessor :readme
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
YARD::Tags::Library.define_tag("API endpiont", :API)
|
2
|
+
YARD::Tags::Library.define_tag("API endpiont argument", :argument)
|
3
|
+
YARD::Tags::Library.define_tag("API response field", :request_field)
|
4
|
+
YARD::Tags::Library.define_tag("API response field", :response_field)
|
5
|
+
YARD::Tags::Library.define_tag("API example request", :example_request, :with_title_and_text)
|
6
|
+
YARD::Tags::Library.define_tag("API example response", :example_response, :with_title_and_text)
|
7
|
+
YARD::Tags::Library.define_tag("API subtopic", :subtopic)
|
8
|
+
YARD::Tags::Library.define_tag("API Object Definition", :object)
|
9
|
+
YARD::Tags::Library.define_tag("API Return Type", :returns)
|
10
|
+
YARD::Tags::Library.define_tag("API resource is beta", :beta)
|
11
|
+
YARD::Tags::Library.define_tag("API resource is internal", :internal)
|
12
|
+
YARD::Tags::Library.define_tag("API empty response", :no_content)
|
13
|
+
YARD::Tags::Library.define_tag("API error", :throws)
|
14
|
+
YARD::Tags::Library.define_tag("API warning", :warning)
|
15
|
+
YARD::Tags::Library.define_tag("API note", :note)
|
16
|
+
YARD::Tags::Library.define_tag("API message", :emits)
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'yard/templates/helpers/base_helper'
|
2
|
+
|
3
|
+
module YARD::Templates::Helpers::BaseHelper
|
4
|
+
def api_options()
|
5
|
+
YARD::APIPlugin.options
|
6
|
+
end
|
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
|
31
|
+
|
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]}'"
|
48
|
+
|
49
|
+
unless appendix = lookup_appendix(args[1].to_s)
|
50
|
+
raise __errmsg
|
51
|
+
end
|
52
|
+
|
53
|
+
topic, controller = *lookup_topic(appendix.namespace.to_s)
|
54
|
+
|
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 { |r,cs|
|
75
|
+
cs.any? { |c|
|
76
|
+
controller = c if c.name.to_s == controller_name
|
77
|
+
!controller.nil?
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
[ topic, controller ]
|
82
|
+
end
|
83
|
+
|
84
|
+
def lookup_appendix(title)
|
85
|
+
appendix = nil
|
86
|
+
|
87
|
+
puts "looking up appendix: #{title}"
|
88
|
+
|
89
|
+
if object
|
90
|
+
# try in the object scope
|
91
|
+
appendix = YARD::Registry.at(".appendix.#{object.path}.#{title}")
|
92
|
+
|
93
|
+
# try in the object's namespace scope
|
94
|
+
if appendix.nil? && object.respond_to?(:namespace)
|
95
|
+
appendix = YARD::Registry.at(".appendix.#{object.namespace.path}.#{title}")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
appendix
|
100
|
+
end
|
101
|
+
|
102
|
+
def tag_partial(name, tag)
|
103
|
+
options[:tag] = tag
|
104
|
+
out = erb(name)
|
105
|
+
options.delete(:tag)
|
106
|
+
out
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'yard/templates/helpers/html_helper'
|
2
|
+
|
3
|
+
module YARD::Templates::Helpers::HtmlHelper
|
4
|
+
def topicize(str)
|
5
|
+
str.gsub(' ', '_').underscore
|
6
|
+
end
|
7
|
+
|
8
|
+
def url_for_file(filename, anchor = nil)
|
9
|
+
link = filename.filename
|
10
|
+
link += (anchor ? '#' + urlencode(anchor) : '')
|
11
|
+
link
|
12
|
+
end
|
13
|
+
|
14
|
+
def static_pages()
|
15
|
+
options = YARD::APIPlugin.options
|
16
|
+
|
17
|
+
paths = Array(options.static).map do |entry|
|
18
|
+
pages = if entry.is_a?(Hash)
|
19
|
+
glob = entry['glob']
|
20
|
+
blacklist = Array(entry['exclude'])
|
21
|
+
|
22
|
+
unless glob
|
23
|
+
raise "Invalid static page entry, expected Hash to contain a 'glob' parameter: #{entry}"
|
24
|
+
end
|
25
|
+
|
26
|
+
pages = Dir.glob(entry['glob'])
|
27
|
+
|
28
|
+
if blacklist.any?
|
29
|
+
pages.delete_if { |p| blacklist.any? { |filter| p.match(filter) } }
|
30
|
+
end
|
31
|
+
|
32
|
+
pages
|
33
|
+
elsif entry.is_a?(Array)
|
34
|
+
entry.map(&:to_s)
|
35
|
+
elsif entry.is_a?(String)
|
36
|
+
[ entry ]
|
37
|
+
end
|
38
|
+
end.flatten.compact
|
39
|
+
|
40
|
+
markdown_exts = YARD::Templates::Helpers::MarkupHelper::MARKUP_EXTENSIONS[:markdown]
|
41
|
+
readme_page = options.readme
|
42
|
+
pages = Dir.glob(paths)
|
43
|
+
|
44
|
+
if readme_page.present?
|
45
|
+
pages.delete_if { |page| page.match(readme_page) }
|
46
|
+
end
|
47
|
+
|
48
|
+
pages.map do |page|
|
49
|
+
filename = 'file.' + File.split(page).last.sub(/\..*$/, '.html')
|
50
|
+
|
51
|
+
# extract page title if it's a markdown document; title is expected to
|
52
|
+
# be an h1 on the very first line:
|
53
|
+
title = if markdown_exts.include?(File.extname(page).sub('.', ''))
|
54
|
+
(File.open(page, &:gets) || '').strip.sub('# ', '')
|
55
|
+
else
|
56
|
+
# otherwise we'll just sanitize the file name
|
57
|
+
File.basename(page).sub(/\.\w+$/, '').gsub(/\W/, ' ').gsub(/\s+/, ' ').capitalize
|
58
|
+
end
|
59
|
+
|
60
|
+
{
|
61
|
+
src: page,
|
62
|
+
filename: filename,
|
63
|
+
title: title
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# override yard-appendix link_appendix
|
69
|
+
def link_appendix(ref)
|
70
|
+
__errmsg = "unable to locate referenced appendix '#{ref}'"
|
71
|
+
|
72
|
+
puts "looking up appendix: #{ref}"
|
73
|
+
unless appendix = lookup_appendix(ref.to_s)
|
74
|
+
raise __errmsg
|
75
|
+
end
|
76
|
+
|
77
|
+
topic, controller = *lookup_topic(appendix.namespace.to_s)
|
78
|
+
|
79
|
+
unless topic
|
80
|
+
raise __errmsg
|
81
|
+
end
|
82
|
+
|
83
|
+
html_file = "#{topicize topic.first}.html"
|
84
|
+
bookmark = "#{appendix.name.to_s.gsub(' ', '+')}-appendix"
|
85
|
+
link_url("#{html_file}##{bookmark}", appendix.title)
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module YARD::Templates::Helpers
|
2
|
+
module RouteHelper
|
3
|
+
def self.routes_for(prefix)
|
4
|
+
Rails.application.routes.set
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.matches_controller_and_action?(route, controller, action)
|
8
|
+
route.requirements[:controller] == controller &&
|
9
|
+
route.requirements[:action] == action
|
10
|
+
end
|
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
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module YARD
|
2
|
+
module APIPlugin
|
3
|
+
class Verifier < ::YARD::Verifier
|
4
|
+
def initialize(verbose=false)
|
5
|
+
@verbose = verbose
|
6
|
+
super()
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(list)
|
10
|
+
relevant = list.select { |o| relevant_object?(o) }
|
11
|
+
|
12
|
+
if @verbose && relevant.any?
|
13
|
+
puts "#{relevant.length}/#{list.length} objects are relevant:"
|
14
|
+
relevant.each do |object|
|
15
|
+
puts "\t- #{object.path}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
relevant
|
20
|
+
end
|
21
|
+
|
22
|
+
def relevant_object?(object)
|
23
|
+
case object.type
|
24
|
+
when :root, :module, :constant
|
25
|
+
false
|
26
|
+
when :method, :class
|
27
|
+
!object.tags('API').empty? && object.tags('internal').empty?
|
28
|
+
else
|
29
|
+
object.parent.nil? && relevant_object?(object.parent)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module YARD::APIPlugin
|
4
|
+
class YardocTask < ::YARD::Rake::YardocTask
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def initialize()
|
8
|
+
super(:yard_api, &:run)
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
@config = load_config
|
13
|
+
t = self
|
14
|
+
|
15
|
+
YARD::APIPlugin.options.update(@config)
|
16
|
+
|
17
|
+
t.verifier = YARD::APIPlugin::Verifier.new(config['verbose'])
|
18
|
+
t.before = proc { FileUtils.rm_rf(config['output']) }
|
19
|
+
t.files = config['files']
|
20
|
+
|
21
|
+
config['debug'] ||= ENV['DEBUG']
|
22
|
+
config['verbose'] ||= ENV['VERBOSE']
|
23
|
+
|
24
|
+
set_option('template', 'api')
|
25
|
+
set_option('no-yardopts')
|
26
|
+
set_option('no-document')
|
27
|
+
|
28
|
+
set_option('markup', config['markup']) if config['markup']
|
29
|
+
set_option('markup-provider', config['markup_provider']) if config['markup_provider']
|
30
|
+
|
31
|
+
if config['markup_provider'] == 'redcarpet'
|
32
|
+
require 'yard-api/markup/redcarpet'
|
33
|
+
end
|
34
|
+
|
35
|
+
set_option('title', config['title'])
|
36
|
+
set_option('output-dir', config['output'])
|
37
|
+
set_option('readme', config['readme']) if File.exists?(config['readme'])
|
38
|
+
set_option('verbose') if config['verbose']
|
39
|
+
set_option('debug') if config['debug']
|
40
|
+
|
41
|
+
get_assets(config).each_pair do |asset_id, rpath|
|
42
|
+
asset_path = File.join(config['source'], rpath)
|
43
|
+
|
44
|
+
if File.directory?(asset_path)
|
45
|
+
set_option 'asset', [ asset_path, asset_id ].join(':')
|
46
|
+
elsif config['strict']
|
47
|
+
raise <<-Error
|
48
|
+
Expected assets of type "#{asset_id}" to be found within
|
49
|
+
"#{asset_path}", but they are not.
|
50
|
+
Error
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if config['debug']
|
55
|
+
puts "Invoking YARD with options: #{self.options.to_json}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def load_config
|
62
|
+
path = Rails.root.join('config', 'yard_api.yml')
|
63
|
+
|
64
|
+
# load defaults
|
65
|
+
config = YAML.load_file(File.join(YARD::APIPlugin::CONFIG_PATH, 'yard_api.yml'))
|
66
|
+
config.merge!(YAML.load_file(path)) if File.exists?(path)
|
67
|
+
config
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_option(k, *vals)
|
71
|
+
self.options.concat(["--#{k}", *vals])
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_assets(config)
|
75
|
+
config['assets'] || {}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/yard-api.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# yard-api - a YARD plugin for generating API documentation in Rails.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2014 Ahmad Amireh <ahmad@instructure.com>
|
4
|
+
# Copyright (C) 2011 Instructure, Inc.
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as
|
8
|
+
# published by the Free Software Foundation, either version 3 of the
|
9
|
+
# License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
20
|
+
|
21
|
+
require 'yard'
|
22
|
+
require 'yard-appendix'
|
23
|
+
|
24
|
+
module YARD
|
25
|
+
module APIPlugin
|
26
|
+
ROOT = File.dirname(__FILE__)
|
27
|
+
CONFIG_PATH = File.join(%W[#{ROOT} .. config])
|
28
|
+
TEMPLATE_PATH = File.join(%W[#{ROOT} .. templates])
|
29
|
+
TASK_PATH = File.join(%W[#{ROOT} .. tasks])
|
30
|
+
|
31
|
+
def self.options
|
32
|
+
@@options ||= Options.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'yard-api/version'
|
37
|
+
require 'yard-api/options'
|
38
|
+
require 'yard-api/tags'
|
39
|
+
require 'yard-api/verifier'
|
40
|
+
require 'yard-api/templates/helpers/base_helper'
|
41
|
+
require 'yard-api/templates/helpers/html_helper'
|
42
|
+
require 'yard-api/templates/helpers/route_helper'
|
43
|
+
require 'yard-api/railtie' if defined?(Rails)
|
44
|
+
|
45
|
+
module Templates
|
46
|
+
Engine.register_template_path YARD::APIPlugin::TEMPLATE_PATH
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2013 Instructure, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
5
|
+
# copy of this software and associated documentation files (the "Software"),
|
6
|
+
# to deal in the Software without restriction, including without limitation
|
7
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
8
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
9
|
+
# Software is furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
17
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20
|
+
# SOFTWARE.
|
21
|
+
#
|
22
|
+
|
23
|
+
require 'yard'
|
24
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'yard-api')
|
25
|
+
|
26
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
27
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
28
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
29
|
+
# loaded once.
|
30
|
+
#
|
31
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
32
|
+
RSpec.configure do |config|
|
33
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
34
|
+
config.run_all_when_everything_filtered = true
|
35
|
+
config.filter_run :focus
|
36
|
+
|
37
|
+
# Run specs in random order to surface order dependencies. If you find an
|
38
|
+
# order dependency and want to debug it, you can fix the order by providing
|
39
|
+
# the seed, which is printed after each run.
|
40
|
+
# --seed 1234
|
41
|
+
config.order = 'random'
|
42
|
+
|
43
|
+
include YARD
|
44
|
+
end
|
data/tasks/yard_api.rake
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yard-api/yardoc_task'
|
3
|
+
|
4
|
+
runner = YARD::APIPlugin::YardocTask.new
|
5
|
+
|
6
|
+
desc 'generate YARD API docs'
|
7
|
+
task :yard_api => :environment do |t|
|
8
|
+
output = runner.config['output']
|
9
|
+
puts <<-Message
|
10
|
+
API Documentation successfully generated in #{output}
|
11
|
+
See #{output}/index.html
|
12
|
+
Message
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
include T('default/appendix/html')
|
2
|
+
|
3
|
+
def init
|
4
|
+
super
|
5
|
+
end
|
6
|
+
|
7
|
+
def appendix
|
8
|
+
controllers = options[:controllers]
|
9
|
+
|
10
|
+
unless controllers && controllers.is_a?(Array)
|
11
|
+
return
|
12
|
+
end
|
13
|
+
|
14
|
+
@appendixes = controllers.collect { |c|
|
15
|
+
c.children.select { |o| :appendix == o.type }
|
16
|
+
}.flatten
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
include YARD::Templates::Helpers::ModuleHelper
|
4
|
+
include YARD::Templates::Helpers::FilterHelper
|
5
|
+
|
6
|
+
def init
|
7
|
+
options[:resources] = options[:objects].
|
8
|
+
group_by { |o| o.tags('API').first.text }.
|
9
|
+
sort_by { |o| o.first }
|
10
|
+
|
11
|
+
build_json_objects_map
|
12
|
+
generate_assets
|
13
|
+
serialize_index
|
14
|
+
serialize_static_pages
|
15
|
+
|
16
|
+
options.delete(:objects)
|
17
|
+
|
18
|
+
options[:resources].each do |resource, controllers|
|
19
|
+
serialize_resource(resource, controllers)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def serialize(object)
|
24
|
+
options[:object] = object
|
25
|
+
Templates::Engine.with_serializer(object, options[:serializer]) do
|
26
|
+
T('layout').run(options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def serialize_resource(resource, controllers)
|
31
|
+
options[:object] = resource
|
32
|
+
options[:controllers] = controllers
|
33
|
+
Templates::Engine.with_serializer("#{topicize resource}.html", options[:serializer]) do
|
34
|
+
T('layout').run(options)
|
35
|
+
end
|
36
|
+
options.delete(:controllers)
|
37
|
+
end
|
38
|
+
|
39
|
+
def serialize_index
|
40
|
+
options[:file] = api_options['readme']
|
41
|
+
serialize('index.html')
|
42
|
+
options.delete(:file)
|
43
|
+
end
|
44
|
+
|
45
|
+
def asset(path, content)
|
46
|
+
options[:serializer].serialize(path, content) if options[:serializer]
|
47
|
+
end
|
48
|
+
|
49
|
+
def generate_assets
|
50
|
+
asset_root = Pathname.new(File.dirname(__FILE__))
|
51
|
+
(Dir[asset_root + "css/**/*.css"] + Dir[asset_root + "js/**/*.js"]).each do |file|
|
52
|
+
file = Pathname.new(file).relative_path_from(asset_root).to_s
|
53
|
+
asset(file, file(file, true))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def serialize_static_pages
|
58
|
+
static_pages.each do |page|
|
59
|
+
options[:file] = page[:src]
|
60
|
+
serialize(page[:filename])
|
61
|
+
options.delete(:file)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_json_objects_map
|
66
|
+
options[:json_objects_map] = {}
|
67
|
+
options[:json_objects] = {}
|
68
|
+
options[:resources].each do |r,cs|
|
69
|
+
cs.each do |controller|
|
70
|
+
(controller.tags(:object) + controller.tags(:model)).each do |obj|
|
71
|
+
name, json = obj.text.split(%r{\n+}, 2).map(&:strip)
|
72
|
+
options[:json_objects_map][name] = topicize r
|
73
|
+
options[:json_objects][r] ||= []
|
74
|
+
options[:json_objects][r] << [name, json]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|