pdoc 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/README.markdown +34 -0
  2. data/Rakefile +46 -0
  3. data/bin/pdoc +58 -0
  4. data/lib/pdoc.rb +32 -0
  5. data/lib/pdoc/error.rb +4 -0
  6. data/lib/pdoc/generators.rb +6 -0
  7. data/lib/pdoc/generators/abstract_generator.rb +16 -0
  8. data/lib/pdoc/generators/html.rb +8 -0
  9. data/lib/pdoc/generators/html/helpers.rb +256 -0
  10. data/lib/pdoc/generators/html/page.rb +71 -0
  11. data/lib/pdoc/generators/html/syntax_highlighter.rb +41 -0
  12. data/lib/pdoc/generators/html/template.rb +37 -0
  13. data/lib/pdoc/generators/html/website.rb +194 -0
  14. data/lib/pdoc/generators/json.rb +15 -0
  15. data/lib/pdoc/generators/pythonesque.rb +105 -0
  16. data/lib/pdoc/models.rb +47 -0
  17. data/lib/pdoc/models/argument.rb +37 -0
  18. data/lib/pdoc/models/base.rb +107 -0
  19. data/lib/pdoc/models/callable.rb +19 -0
  20. data/lib/pdoc/models/class.rb +28 -0
  21. data/lib/pdoc/models/class_method.rb +18 -0
  22. data/lib/pdoc/models/class_property.rb +9 -0
  23. data/lib/pdoc/models/constant.rb +9 -0
  24. data/lib/pdoc/models/constructor.rb +14 -0
  25. data/lib/pdoc/models/container.rb +114 -0
  26. data/lib/pdoc/models/entity.rb +54 -0
  27. data/lib/pdoc/models/instance_method.rb +18 -0
  28. data/lib/pdoc/models/instance_property.rb +9 -0
  29. data/lib/pdoc/models/mixin.rb +10 -0
  30. data/lib/pdoc/models/namespace.rb +10 -0
  31. data/lib/pdoc/models/root.rb +27 -0
  32. data/lib/pdoc/models/section.rb +19 -0
  33. data/lib/pdoc/models/signature.rb +27 -0
  34. data/lib/pdoc/models/utility.rb +11 -0
  35. data/lib/pdoc/parser.rb +109 -0
  36. data/lib/pdoc/parser/argument_description_nodes.rb +21 -0
  37. data/lib/pdoc/parser/basic_nodes.rb +31 -0
  38. data/lib/pdoc/parser/description_nodes.rb +42 -0
  39. data/lib/pdoc/parser/documentation_nodes.rb +483 -0
  40. data/lib/pdoc/parser/ebnf_arguments_nodes.rb +58 -0
  41. data/lib/pdoc/parser/ebnf_expression_nodes.rb +227 -0
  42. data/lib/pdoc/parser/fragment.rb +55 -0
  43. data/lib/pdoc/parser/section_content_nodes.rb +19 -0
  44. data/lib/pdoc/parser/tags_nodes.rb +14 -0
  45. data/lib/pdoc/parser/treetop_files/argument_description.treetop +31 -0
  46. data/lib/pdoc/parser/treetop_files/basic.treetop +41 -0
  47. data/lib/pdoc/parser/treetop_files/description.treetop +7 -0
  48. data/lib/pdoc/parser/treetop_files/documentation.treetop +75 -0
  49. data/lib/pdoc/parser/treetop_files/ebnf_arguments.treetop +33 -0
  50. data/lib/pdoc/parser/treetop_files/ebnf_expression.treetop +70 -0
  51. data/lib/pdoc/parser/treetop_files/ebnf_javascript.treetop +54 -0
  52. data/lib/pdoc/parser/treetop_files/events.treetop +17 -0
  53. data/lib/pdoc/parser/treetop_files/section_content.treetop +8 -0
  54. data/lib/pdoc/parser/treetop_files/tags.treetop +31 -0
  55. data/lib/pdoc/runner.rb +110 -0
  56. data/lib/pdoc/treemaker.rb +94 -0
  57. data/pdoc.gemspec +31 -0
  58. data/templates/html/assets/images/pdoc/alias.png +0 -0
  59. data/templates/html/assets/images/pdoc/class.png +0 -0
  60. data/templates/html/assets/images/pdoc/class_deprecated.png +0 -0
  61. data/templates/html/assets/images/pdoc/class_method.png +0 -0
  62. data/templates/html/assets/images/pdoc/class_property.png +0 -0
  63. data/templates/html/assets/images/pdoc/constant.png +0 -0
  64. data/templates/html/assets/images/pdoc/constructor.png +0 -0
  65. data/templates/html/assets/images/pdoc/deprecated.png +0 -0
  66. data/templates/html/assets/images/pdoc/description.png +0 -0
  67. data/templates/html/assets/images/pdoc/information.png +0 -0
  68. data/templates/html/assets/images/pdoc/instance_method.png +0 -0
  69. data/templates/html/assets/images/pdoc/instance_property.png +0 -0
  70. data/templates/html/assets/images/pdoc/method.png +0 -0
  71. data/templates/html/assets/images/pdoc/method_deprecated.png +0 -0
  72. data/templates/html/assets/images/pdoc/mixin.png +0 -0
  73. data/templates/html/assets/images/pdoc/namespace.png +0 -0
  74. data/templates/html/assets/images/pdoc/property.png +0 -0
  75. data/templates/html/assets/images/pdoc/related_to.png +0 -0
  76. data/templates/html/assets/images/pdoc/search-background.png +0 -0
  77. data/templates/html/assets/images/pdoc/section-background.png +0 -0
  78. data/templates/html/assets/images/pdoc/section.png +0 -0
  79. data/templates/html/assets/images/pdoc/selected-section-background.png +0 -0
  80. data/templates/html/assets/images/pdoc/subclass.png +0 -0
  81. data/templates/html/assets/images/pdoc/superclass.png +0 -0
  82. data/templates/html/assets/images/pdoc/utility.png +0 -0
  83. data/templates/html/assets/javascripts/pdoc/application.js +478 -0
  84. data/templates/html/assets/javascripts/pdoc/prototype.js +4874 -0
  85. data/templates/html/assets/javascripts/pdoc/tabs.js +506 -0
  86. data/templates/html/assets/stylesheets/pdoc/api.css +677 -0
  87. data/templates/html/assets/stylesheets/pdoc/pygments.css +62 -0
  88. data/templates/html/helpers.rb +35 -0
  89. data/templates/html/index.erb +18 -0
  90. data/templates/html/item_index.js.erb +6 -0
  91. data/templates/html/layout.erb +67 -0
  92. data/templates/html/leaf.erb +22 -0
  93. data/templates/html/node.erb +30 -0
  94. data/templates/html/partials/class_relationships.erb +19 -0
  95. data/templates/html/partials/classes.erb +7 -0
  96. data/templates/html/partials/constructor.erb +5 -0
  97. data/templates/html/partials/description.erb +5 -0
  98. data/templates/html/partials/link_list.erb +1 -0
  99. data/templates/html/partials/method_signatures.erb +14 -0
  100. data/templates/html/partials/methodized_note.erb +9 -0
  101. data/templates/html/partials/mixins.erb +7 -0
  102. data/templates/html/partials/namespaces.erb +7 -0
  103. data/templates/html/partials/related_utilities.erb +5 -0
  104. data/templates/html/partials/relationships.erb +11 -0
  105. data/templates/html/partials/short_description_list.erb +7 -0
  106. data/templates/html/partials/title.erb +22 -0
  107. data/templates/html/section.erb +18 -0
  108. data/test/unit/parser/argument_description_test.rb +40 -0
  109. data/test/unit/parser/basic_test.rb +55 -0
  110. data/test/unit/parser/description_test.rb +34 -0
  111. data/test/unit/parser/documentation_test.rb +520 -0
  112. data/test/unit/parser/ebnf_arguments_test.rb +81 -0
  113. data/test/unit/parser/ebnf_expression_test.rb +382 -0
  114. data/test/unit/parser/ebnf_javascript_test.rb +37 -0
  115. data/test/unit/parser/events_test.rb +27 -0
  116. data/test/unit/parser/section_content_test.rb +44 -0
  117. data/test/unit/parser/tags_test.rb +39 -0
  118. data/test/unit/parser/test_fragment.rb +80 -0
  119. data/test/unit/parser_test_helper.rb +62 -0
  120. data/test/unit/runner/basic_test.rb +14 -0
  121. data/test/unit/templates/html_helpers_test.rb +25 -0
  122. metadata +222 -0
data/README.markdown ADDED
@@ -0,0 +1,34 @@
1
+ PDoc
2
+ ====
3
+
4
+ PDoc is an inline comment parser and JavaScript documentation generator written in Ruby. It is designed for documenting [Prototype](http://prototypejs.org) and Prototype-based libraries.
5
+
6
+ PDoc uses [Treetop](http://treetop.rubyforge.org/), a Ruby-based DSL for text parsing and interpretation, and its own ActionView-inspired, ERB-based templating system for HTML generation. Other documentation generators (e.g., DocBook XML) are planned.
7
+
8
+ Unlike other inline-doc parsers, PDoc does not rely on the JavaScript source code at all; it only parses the comments. This approach, though slightly more verbose, is much better at generating consistent, reliable documentation, and avoids the headaches encountered when documenting highly dynamic languages.
9
+
10
+ ## Installation
11
+
12
+ PDoc depends on Rake, your choice of markdown parser, and treetop, all of which can be obtained through RubyGems:
13
+
14
+ gem install rake bluecloth treetop
15
+
16
+ ## Usage
17
+
18
+ For hints on how to run PDoc on the command line, consult the built-in Rake tasks (in `Rakefile`) and the `PDoc::Runner` class (in `lib/pdoc/runner.rb`).
19
+
20
+ ## How it works
21
+
22
+ The process of turning inline PDoc comments into a human-friendly document has two phases.
23
+
24
+ ### Parsing phase
25
+ In this phase, the source files are scanned for PDoc comments, then parsed with the Ruby files generated from the Treetop language grammar. The product of this phase is a tree full of specialized classes, all of which inherit from `Treetop::Runtime::SyntaxNode`.
26
+
27
+ The root of the tree is an instance of `Documentation::Doc`. It comprises one or more instances of `Documentation::Section`; which in turn comprise language elements like namespaces, classes, constants, etc., all of which have class representations.
28
+
29
+ ### Rendering phase
30
+ Next, PDoc asks a _generator_ how to translate this abstract tree into a hierarchical document. The default generator outputs organized HTML in a manner similar to [RDoc](http://rdoc.sourceforge.net/ "RDoc - Document Generator for Ruby Source")'s.
31
+
32
+ The HTML generator (`PDoc::Generators::Html`) has associated _templates_ (in the `templates` directory) that accept syntax nodes and echo their metadata onto the page using [ERB](http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/index.html "erb: Ruby Standard Library Documentation"). Templates are modular, so it's quite easy to apply a custom "skin" to one's documentation pages.
33
+
34
+ Furthermore, generators themselves are modular; PDoc can, theoretically, parse once and render to several different targets (HTML, [DocBook XML](http://www.docbook.org/ "DocBook.org"), CHM, PDF, even [ScriptDoc](http://www.scriptdoc.org/ "ScriptDoc.org: Dynamic Language Documentation").) We hope many such generators will exist in the future.
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require 'rake'
2
+ require 'lib/pdoc'
3
+
4
+ desc "Builds the documentation"
5
+ task :build_doc do
6
+ PDoc.run({
7
+ :source_files => [File.join(File.dirname(__FILE__), "test", "fixtures", "ajax.js")],
8
+ :destination => OUTPUT_DIR,
9
+ :syntax_highlighter => :pygments,
10
+ :markdown_parser => :bluecloth,
11
+ :src_code_href => proc { |file, line|
12
+ "http://github.com/example/ex/#{file}##{line}"
13
+ },
14
+ :pretty_urls => false,
15
+ :bust_cache => true,
16
+ :name => 'Example JavaScript Framework',
17
+ :short_name => 'Ex',
18
+ :home_url => 'http://example.com',
19
+ :doc_url => 'http://example.com/api',
20
+ :version => "1.2.0",
21
+ :copyright_notice => 'This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-Share Alike 3.0 Unported License</a>.'
22
+ })
23
+ end
24
+
25
+ desc "Empties output directory"
26
+ task :remove_doc do
27
+ rm_rf Dir.glob(File.join(OUTPUT_DIR, "*"))
28
+ end
29
+
30
+ desc "Empties the output directory and builds the documentation."
31
+ task :doc => [:remove_doc, :build_doc]
32
+
33
+ desc "Runs all the unit tests."
34
+ task :test do
35
+ require 'rake/runtest'
36
+ Rake.run_tests '**/*_test.rb'
37
+ end
38
+
39
+ task :compile_parser do
40
+ require 'treetop'
41
+ compiler = Treetop::Compiler::GrammarCompiler.new
42
+ treetop_dir = File.expand_path(File.join(File.dirname(__FILE__), "lib", "pdoc", "parser", "treetop_files"))
43
+ Dir.glob(File.join(treetop_dir, "*.treetop")).each do |treetop_file_path|
44
+ compiler.compile(treetop_file_path)
45
+ end
46
+ end
data/bin/pdoc ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'oyster'
4
+ require File.dirname(__FILE__) + '/../lib/pdoc'
5
+
6
+ spec = Oyster.spec do
7
+ name "pdoc -- Inline comment parser and JavaScript documentation generator"
8
+ author "Tobie Langel <tobie.langel@gmail.com>"
9
+
10
+ synopsis <<-EOS
11
+ pdoc [-o OUTPUT_DIR] [-t TEMPLATE_DIR] SOURCE_FILES
12
+ pdoc [OPTIONS] -d SOURCE_DIRECTORY
13
+ EOS
14
+
15
+ string :directory,
16
+ :desc => "Directory to search for JavaScript files. Will take all *.js " +
17
+ "files from the given directory (including subdirectories) and use " +
18
+ "them to generate documentation. This option takes precedence over " +
19
+ "SOURCE_FILES."
20
+
21
+ string :output,
22
+ :desc => "Directory in which to dump output files",
23
+ :default => "pdoc"
24
+
25
+ string :templates,
26
+ :desc => "Directory containing template files"
27
+
28
+ subcommand :'copy-templates' do
29
+ synopsis <<-EOS
30
+ pdoc copy-templates TYPE DESTINATION
31
+ EOS
32
+
33
+ description <<-EOS
34
+ PDoc includes a set of default templates for each type of output generator.
35
+ This command lets you extract a set of these templates into a local directory
36
+ so you can tweak it to suit your needs. Be sure to specify your set of
37
+ templates next time you run pdoc.
38
+ EOS
39
+ end
40
+ end
41
+
42
+ begin; options = spec.parse
43
+ rescue Oyster::HelpRendered; exit
44
+ end
45
+
46
+ if command = options[:'copy-templates']
47
+ args = command[:unclaimed]
48
+ PDoc.copy_templates(args[0], File.expand_path(args[1]))
49
+ exit
50
+ end
51
+
52
+ files = (d = options[:directory]) ?
53
+ Dir["#{d}/**/*.js"].map(&File.method(:expand_path)) :
54
+ options[:unclaimed].dup
55
+
56
+ files << {:destination => options[:output], :templates => options[:templates]}
57
+ PDoc::Runner.new(*files).run
58
+
data/lib/pdoc.rb ADDED
@@ -0,0 +1,32 @@
1
+ DIR = File.expand_path(File.dirname(__FILE__))
2
+ OUTPUT_DIR = File.join(DIR, '..', 'output')
3
+ TEMPLATES_DIR = File.join(DIR, '..', 'templates')
4
+ VENDOR_DIR = File.join(DIR, '..', 'vendor')
5
+ PARSER_DIR = File.join(DIR, 'pdoc', 'parser')
6
+
7
+ [DIR, VENDOR_DIR, PARSER_DIR, OUTPUT_DIR, TEMPLATES_DIR].each do |c|
8
+ $:.unshift(c)
9
+ end
10
+
11
+ require 'rubygems'
12
+ require 'erb'
13
+ require 'fileutils'
14
+
15
+ require 'pdoc/error'
16
+ require 'pdoc/runner'
17
+ require 'pdoc/generators'
18
+ require 'pdoc/parser'
19
+ require 'pdoc/models'
20
+ require 'pdoc/treemaker'
21
+
22
+ module PDoc
23
+ def self.run(options = {})
24
+ Runner.new(options.dup).run
25
+ end
26
+
27
+ def self.copy_templates(template_type, destination)
28
+ dir = File.expand_path(destination)
29
+ raise "File already exists: #{destination}" if File.exist?(dir)
30
+ FileUtils.cp_r("#{TEMPLATES_DIR}/#{template_type}", dir)
31
+ end
32
+ end
data/lib/pdoc/error.rb ADDED
@@ -0,0 +1,4 @@
1
+ module PDoc
2
+ class PDocError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'generators', 'abstract_generator'))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), 'generators', 'html'))
3
+ require File.expand_path(File.join(File.dirname(__FILE__), 'generators', 'pythonesque'))
4
+ require File.expand_path(File.join(File.dirname(__FILE__), 'generators', 'json'))
5
+
6
+
@@ -0,0 +1,16 @@
1
+ module PDoc
2
+ module Generators
3
+ class AbstractGenerator
4
+ attr_reader :options, :root
5
+ def initialize(root, options = {})
6
+ @root = root
7
+ @options = options
8
+ end
9
+
10
+ # Creates a new directory with read, write and execute permission.
11
+ def mkdir(name)
12
+ Dir.mkdir(name, 0755)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ HTML_DIR = File.expand_path(File.join(File.dirname(__FILE__), "html"))
2
+
3
+ require File.join(HTML_DIR, "helpers")
4
+ require File.join(HTML_DIR, "template")
5
+ require File.join(HTML_DIR, "page")
6
+ require File.join(HTML_DIR, "website")
7
+ require File.join(HTML_DIR, "syntax_highlighter")
8
+
@@ -0,0 +1,256 @@
1
+ module PDoc
2
+ module Generators
3
+ module Html
4
+ module Helpers
5
+ module BaseHelper
6
+ def content_tag(tag_name, content, attributes = {})
7
+ "<#{tag_name}#{attributes_to_html(attributes)}>#{content}</#{tag_name}>"
8
+ end
9
+
10
+ def img_tag(filename, attributes = {})
11
+ attributes.merge! :src => "#{path_prefix}images/#{filename}"
12
+ tag(:img, attributes)
13
+ end
14
+
15
+ def tag(tag_name, attributes = {})
16
+ "<#{tag_name}#{attributes_to_html(attributes)} />"
17
+ end
18
+
19
+ def link_to(name, path, attributes={})
20
+ content_tag(:a, name, attributes.merge(:href => path))
21
+ end
22
+
23
+ def htmlize(markdown)
24
+ markdown = Website.syntax_highlighter.parse(markdown)
25
+ Website.markdown_parser.new(markdown).to_html
26
+ end
27
+
28
+ # Gah, what an ugly hack.
29
+ def inline_htmlize(markdown)
30
+ htmlize(markdown).gsub(/^<p>/, '').gsub(/<\/p>$/, '')
31
+ end
32
+
33
+ def javascript_include_tag(*names)
34
+ names.map do |name|
35
+ attributes = {
36
+ :src => "#{path_prefix}javascripts/#{name}.js",
37
+ :type => "text/javascript",
38
+ :charset => "utf-8"
39
+ }
40
+ content_tag(:script, "", attributes)
41
+ end.join("\n")
42
+ end
43
+
44
+ def stylesheet_link_tag(*names)
45
+ names.map do |name|
46
+ attributes = {
47
+ :href => "#{path_prefix}stylesheets/#{name}.css",
48
+ :type => "text/css",
49
+ :media => "screen, projection",
50
+ :charset => "utf-8",
51
+ :rel => "stylesheet"
52
+ }
53
+ tag(:link, attributes)
54
+ end.join("\n")
55
+ end
56
+
57
+ private
58
+ def attributes_to_html(attributes)
59
+ attributes = attributes.sort { |a, b| a.to_s <=> b.to_s }
60
+ attributes.map do |a|
61
+ k, v = a
62
+ k ? " #{k}=\"#{v}\"" : ""
63
+ end.join
64
+ end
65
+ end
66
+
67
+ module LinkHelper
68
+ def path_prefix
69
+ "../" * depth
70
+ end
71
+
72
+ def path_to(obj)
73
+ path = path_prefix << obj.url << '/'
74
+ Website.pretty_urls? ? path : "#{path}index.html"
75
+ end
76
+
77
+ def auto_link(obj, options = {})
78
+ if obj.is_a?(String)
79
+ original = obj
80
+ obj = root.find(obj)
81
+ return original unless obj
82
+ end
83
+ name = options.delete(:name) == :short ? obj.name : obj.full_name
84
+ if obj.type == 'section'
85
+ title = obj.full_name
86
+ else
87
+ title = "#{obj.full_name} (#{obj.type})"
88
+ end
89
+ link_to(name, path_to(obj), { :title => title }.merge(options))
90
+ end
91
+
92
+ def auto_link_code(obj, options = {})
93
+ "<code>#{auto_link(obj, options)}</code>"
94
+ end
95
+
96
+ def auto_link_content(content)
97
+ return '' if content.nil?
98
+ content.gsub!(/\[\[([a-zA-Z]+)\s+section\]\]/) do |m|
99
+ result = auto_link(root.find($1), :name => :long)
100
+ result
101
+ end
102
+ content.gsub(/\[\[([a-zA-Z$\.#]+)(?:\s+([^\]]+))?\]\]/) do |m|
103
+ if doc_instance = root.find($1)
104
+ $2 ? link_to($2, path_to(doc_instance)) : auto_link_code(doc_instance, :name => :long)
105
+ else
106
+ $1
107
+ end
108
+ end
109
+ end
110
+
111
+ def auto_link_types(types, options = {})
112
+ types = types.split(/\s+\|\s+/) if types.is_a?(String)
113
+ types.map do |t|
114
+ if match = /^\[([\w\d\$\.\(\)#]*[\w\d\$\(\)#])...\s*\]$/.match(t) # e.g.: [Element...]
115
+ "[#{auto_link(match[1], options)}…]"
116
+ else
117
+ auto_link(t, options)
118
+ end
119
+ end
120
+ end
121
+
122
+ def dom_id(obj)
123
+ "#{obj.id}-#{obj.type.gsub(/\s+/, '_')}"
124
+ end
125
+ end
126
+
127
+ module CodeHelper
128
+ def methodize_signature(sig)
129
+ sig.sub(/\.([\w\d\$]+)\((.*?)(,\s*|\))/) do
130
+ first_arg = $2.to_s.strip
131
+ prefix = first_arg[-1, 1] == '[' ? '([' : '('
132
+ rest = $3 == ')' ? $3 : ''
133
+ "##{$1}#{prefix}#{rest}"
134
+ end
135
+ end
136
+
137
+ def methodize_full_name(obj)
138
+ obj.full_name.sub(/\.([^.]+)$/, '#\1')
139
+ end
140
+
141
+ def method_synopsis(object)
142
+ result = []
143
+ object.signatures.each do |signature|
144
+ if return_value = signature.return_value
145
+ types = auto_link_types(return_value, :name => :long).join(' | ')
146
+ result << "#{signature.name} &rarr; #{types}"
147
+ else # Constructors
148
+ result << signature.name
149
+ end
150
+ end
151
+ result
152
+ end
153
+
154
+ def breadcrumb(obj, options = {})
155
+ options = {:name => :short}.merge(options)
156
+ result = []
157
+ begin
158
+ result << auto_link(obj, options.dup)
159
+ obj = obj.parent
160
+ end until obj.is_a?(Models::Root)
161
+ result.reverse!
162
+ end
163
+ end
164
+
165
+ module MenuHelper
166
+ NODES = [
167
+ :namespaces,
168
+ :classes,
169
+ :mixins,
170
+ :utilities
171
+ ]
172
+ LEAVES = [
173
+ :constants,
174
+ :class_methods,
175
+ :class_properties,
176
+ :instance_methods,
177
+ :instance_properties
178
+ ]
179
+
180
+ def menu(obj)
181
+ if obj.parent
182
+ html = menu_item(obj, :name => :long)
183
+
184
+ html << node_submenu(obj)
185
+
186
+ if obj == doc_instance && obj.respond_to?(:constants)
187
+ html << leaf_submenu(obj)
188
+ elsif doc_instance && doc_instance.respond_to?(:parent)
189
+ parent = doc_instance.parent
190
+ html << leaf_submenu(parent) if parent == obj && obj.respond_to?(:constants)
191
+ end
192
+
193
+ content_tag(:li, html)
194
+ else #root
195
+ node_submenu(obj)
196
+ end
197
+ end
198
+
199
+ def node_submenu(obj)
200
+ children = []
201
+ options = {}
202
+
203
+ NODES.each do |prop|
204
+ children.concat(obj.send(prop)) if obj.respond_to?(prop)
205
+ end
206
+
207
+ list_items = children.sort.map { |item| menu(item) }
208
+ if obj.respond_to?(:sections)
209
+ obj.sections.each { |section| list_items << menu(section) }
210
+ options[:class] = "menu-items"
211
+ options[:id] = "api_menu"
212
+ elsif obj.type == "section"
213
+ options[:class] = "menu-section"
214
+ end
215
+ list_items.empty? ? '' : content_tag(:ul, list_items.join("\n"), options)
216
+ end
217
+
218
+ def menu_item(obj, options = {})
219
+ options = options.dup
220
+ options[:class] = class_names_for(obj, options)
221
+ content_tag(:div, auto_link(obj, options), :class => 'menu-item')
222
+ end
223
+
224
+ def leaf_submenu(obj)
225
+ items = []
226
+ if obj.respond_to?(:constructor) && obj.constructor
227
+ items << content_tag(:li, menu_item(obj.constructor, :name => :short))
228
+ end
229
+ LEAVES.each do |prop|
230
+ if obj.respond_to?(prop)
231
+ obj.send(prop).sort!.map do |item|
232
+ items << content_tag(:li, menu_item(item, :name => :short))
233
+ end
234
+ end
235
+ end
236
+ content_tag(:ul, items.join("\n"))
237
+ end
238
+
239
+ def class_names_for(obj, options = {})
240
+ classes = []
241
+ classes << obj.type.gsub(/\s+/, '-')
242
+ classes << "deprecated" if obj.deprecated?
243
+ if doc_instance
244
+ if obj == doc_instance
245
+ classes << "current"
246
+ elsif obj.ancestor_of?(doc_instance)
247
+ classes << "current-parent"
248
+ end
249
+ end
250
+ classes.join(' ')
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end