pdoc 0.2.0

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.
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