docjs 0.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.
- data/CONCEPT.md +80 -0
- data/DOCUMENTATION.md +41 -0
- data/LICENSE.md +19 -0
- data/README.md +19 -0
- data/RENDERING.md +8 -0
- data/bin/docjs +190 -0
- data/docjs.gemspec +32 -0
- data/lib/boot.rb +34 -0
- data/lib/code_object/base.rb +48 -0
- data/lib/code_object/converter.rb +48 -0
- data/lib/code_object/exceptions.rb +5 -0
- data/lib/code_object/function.rb +84 -0
- data/lib/code_object/object.rb +18 -0
- data/lib/code_object/type.rb +43 -0
- data/lib/configs.rb +53 -0
- data/lib/document/document.rb +25 -0
- data/lib/dom/dom.rb +188 -0
- data/lib/dom/exceptions.rb +12 -0
- data/lib/dom/no_doc.rb +26 -0
- data/lib/dom/node.rb +415 -0
- data/lib/helper/helper.rb +120 -0
- data/lib/helper/linker.rb +130 -0
- data/lib/logger.rb +49 -0
- data/lib/parser/comment.rb +69 -0
- data/lib/parser/comment_parser.rb +90 -0
- data/lib/parser/exceptions.rb +6 -0
- data/lib/parser/meta_container.rb +20 -0
- data/lib/parser/parser.rb +269 -0
- data/lib/processor.rb +123 -0
- data/lib/renderer.rb +108 -0
- data/lib/tasks/render_task.rb +112 -0
- data/lib/thor.rb +27 -0
- data/lib/token/container.rb +84 -0
- data/lib/token/exceptions.rb +6 -0
- data/lib/token/handler.rb +242 -0
- data/lib/token/token.rb +46 -0
- data/templates/application.rb +14 -0
- data/templates/helpers/template.rb +66 -0
- data/templates/resources/css/.sass-cache/98c121fba905284c2c8ca6220fe3c590e5c9ec19/application.scssc +0 -0
- data/templates/resources/css/application.css +836 -0
- data/templates/resources/img/arrow_down.png +0 -0
- data/templates/resources/img/arrow_right.png +0 -0
- data/templates/resources/img/arrow_up.png +0 -0
- data/templates/resources/img/bullet_toggle_minus.png +0 -0
- data/templates/resources/img/bullet_toggle_plus.png +0 -0
- data/templates/resources/img/constructor.png +0 -0
- data/templates/resources/img/function.png +0 -0
- data/templates/resources/img/object.png +0 -0
- data/templates/resources/img/page.png +0 -0
- data/templates/resources/img/prototype.png +0 -0
- data/templates/resources/img/tag.png +0 -0
- data/templates/resources/js/application.js +318 -0
- data/templates/resources/js/jcore.js +129 -0
- data/templates/resources/js/jquery.cookie.js +92 -0
- data/templates/resources/js/jquery.js +16 -0
- data/templates/resources/js/jquery.tooltip.js +77 -0
- data/templates/resources/js/jquery.treeview.js +238 -0
- data/templates/resources/scss/_footer.scss +10 -0
- data/templates/resources/scss/_header.scss +184 -0
- data/templates/resources/scss/_helpers.scss +91 -0
- data/templates/resources/scss/_print.scss +20 -0
- data/templates/resources/scss/_resets.scss +132 -0
- data/templates/resources/scss/_tooltip.scss +26 -0
- data/templates/resources/scss/application.scss +442 -0
- data/templates/tasks/api_index_task.rb +26 -0
- data/templates/tasks/docs_task.rb +33 -0
- data/templates/tasks/json_data_task.rb +55 -0
- data/templates/tasks/typed_task.rb +54 -0
- data/templates/tokens/tokens.rb +22 -0
- data/templates/types/prototype.rb +20 -0
- data/templates/views/api_index.html.erb +21 -0
- data/templates/views/doc_page.html.erb +11 -0
- data/templates/views/function/_detail.html.erb +8 -0
- data/templates/views/function/index.html.erb +53 -0
- data/templates/views/index.html.erb +0 -0
- data/templates/views/layout/application.html.erb +73 -0
- data/templates/views/layout/json.html.erb +3 -0
- data/templates/views/object/index.html.erb +63 -0
- data/templates/views/tokens/_default.html.erb +11 -0
- data/templates/views/tokens/_default_token.html.erb +19 -0
- data/templates/views/tokens/_example.html.erb +2 -0
- data/templates/views/tokens/_examples.html.erb +1 -0
- data/test/code_object/converter.rb +78 -0
- data/test/code_object/prototype.rb +70 -0
- data/test/configs.rb +65 -0
- data/test/docs/README.CONCEPT.md +83 -0
- data/test/docs/README.md +14 -0
- data/test/dom/dom.absolute_nodes.rb +40 -0
- data/test/dom/dom.rb +72 -0
- data/test/dom/node.rb +53 -0
- data/test/integration/converter.rb +72 -0
- data/test/integration/parser_factory.rb +28 -0
- data/test/interactive.rb +7 -0
- data/test/js-files/absolute.js +11 -0
- data/test/js-files/comments_in_strings.js +31 -0
- data/test/js-files/core-doc-relative.js +77 -0
- data/test/js-files/core-doc.js +145 -0
- data/test/js-files/nested.js +34 -0
- data/test/js-files/nested_with_strings.js +35 -0
- data/test/js-files/prototype.js +33 -0
- data/test/js-files/simple.js +17 -0
- data/test/js-files/tokens.js +32 -0
- data/test/parser/comments_in_strings.rb +51 -0
- data/test/parser/intelligent_skip_until.rb +110 -0
- data/test/parser/parser.rb +273 -0
- data/test/rspec_helper.rb +23 -0
- data/test/token/handler.rb +136 -0
- data/test/token/tokens.rb +52 -0
- metadata +184 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
# ../data.img#1769990:1
|
2
|
+
require_relative 'function'
|
3
|
+
require_relative 'exceptions'
|
4
|
+
require_relative 'type'
|
5
|
+
|
6
|
+
module CodeObject
|
7
|
+
|
8
|
+
|
9
|
+
# here the dependencies to {Dom::Node} and {Parser::Comment} should be described
|
10
|
+
module Converter
|
11
|
+
|
12
|
+
attr_reader :code_object, :path
|
13
|
+
|
14
|
+
def to_code_object
|
15
|
+
|
16
|
+
# 1. Create a new CodeObject from Type-Token
|
17
|
+
@code_object = Type.create_matching_object(@tokenlines) or return nil
|
18
|
+
|
19
|
+
# join all documentation-contents
|
20
|
+
@code_object.docs = @doclines.join ''
|
21
|
+
|
22
|
+
# move meta-information from comment to code_object
|
23
|
+
# (This includes filepath, source and line_start
|
24
|
+
@code_object.clone_meta(self)
|
25
|
+
|
26
|
+
# 2. Process Tokenlines with registered handlers
|
27
|
+
@code_object.process_tokens(@tokenlines)
|
28
|
+
|
29
|
+
# 3.Continue with all children of this comment and add them as
|
30
|
+
# child nodes
|
31
|
+
convert_children { |child| @code_object.add_node(child) }
|
32
|
+
|
33
|
+
return @code_object
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
|
39
|
+
# recursivly process all child-tokens
|
40
|
+
def convert_children
|
41
|
+
@children.each do |child_comment|
|
42
|
+
code_object = child_comment.to_code_object
|
43
|
+
yield(code_object) unless code_object.nil?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# ../data.img#1858563:1
|
2
|
+
require_relative 'object'
|
3
|
+
|
4
|
+
module CodeObject
|
5
|
+
|
6
|
+
class Function < CodeObject::Object
|
7
|
+
|
8
|
+
token_reader :params, :param
|
9
|
+
token_reader :returns, :return
|
10
|
+
|
11
|
+
def constructor?
|
12
|
+
@constructor || false
|
13
|
+
end
|
14
|
+
|
15
|
+
# @todo i need a @prototype token in object
|
16
|
+
def prototype
|
17
|
+
children[:prototype]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
CodeObject::Type.register :function, CodeObject::Function
|
25
|
+
Token::Handler.register :function, :handler => :noop, :area => :none
|
26
|
+
|
27
|
+
# @todo rewrite as default-handler, because this looks a little distracting
|
28
|
+
module Token::Handler
|
29
|
+
|
30
|
+
# @todo maybe allow multipled nested layers of params by parsing them recursivly
|
31
|
+
register :param, :area => :body do |tokenklass, content|
|
32
|
+
|
33
|
+
# We want to support either named-typed-tokens like
|
34
|
+
# @param [Foo] barname some description
|
35
|
+
#
|
36
|
+
# or multiline tokens like:
|
37
|
+
# @param configs
|
38
|
+
# [String] foo some string
|
39
|
+
# [Bar] bar and another one
|
40
|
+
#
|
41
|
+
#
|
42
|
+
# if out content matches something with `[` at the beginning, it seems to be
|
43
|
+
# a normal named-typed-token
|
44
|
+
# it's a little tricky because we still want to allow multiline descriptions of each param
|
45
|
+
def parse_token(content)
|
46
|
+
typestring, name, content = TOKEN_W_TYPE_NAME.match(content).captures
|
47
|
+
types = typestring.split /,\s*/
|
48
|
+
Token::Token::ParamToken.new(:name => name, :types => types, :content => content)
|
49
|
+
end
|
50
|
+
|
51
|
+
# it's @param [String] name some content
|
52
|
+
if content.lines.first.match TOKEN_W_TYPE_NAME
|
53
|
+
self.add_token parse_token(content)
|
54
|
+
|
55
|
+
# it maybe a multiline
|
56
|
+
else
|
57
|
+
lines = content.split(/\n/)
|
58
|
+
name = lines.shift.strip
|
59
|
+
types = ['PropertyObject']
|
60
|
+
|
61
|
+
# now split line at opening bracket, not at line-break to enable multiline properties
|
62
|
+
children = lines.join("\n").strip.gsub(/\s+\[/, "<--SPLIT_HERE-->[").split("<--SPLIT_HERE-->")
|
63
|
+
|
64
|
+
children.map! do |child|
|
65
|
+
parse_token(child)
|
66
|
+
end
|
67
|
+
|
68
|
+
self.add_token tokenklass.new(:name => name, :types => types, :children => children)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
Token::Handler.register :return, :handler => :typed
|
76
|
+
Token::Handler.register :throws, :handler => :typed
|
77
|
+
|
78
|
+
# MethodAlias
|
79
|
+
CodeObject::Type.register :method, CodeObject::Function
|
80
|
+
Token::Handler.register :method, :handler => :noop, :area => :none
|
81
|
+
|
82
|
+
# @constructor Foo.bar
|
83
|
+
CodeObject::Type.register :constructor, CodeObject::Function
|
84
|
+
Token::Handler.register(:constructor) { |token, content| @constructor = true }
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# ../data.img#1858561:1
|
2
|
+
require_relative 'base'
|
3
|
+
require_relative 'type'
|
4
|
+
|
5
|
+
module CodeObject
|
6
|
+
|
7
|
+
class Object < CodeObject::Base
|
8
|
+
|
9
|
+
token_reader :props, :prop
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
CodeObject::Type.register :object, CodeObject::Object
|
16
|
+
Token::Handler.register :object, :handler => :noop, :area => :none
|
17
|
+
|
18
|
+
Token::Handler.register :prop, :handler => :typed_with_name
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative 'exceptions'
|
2
|
+
|
3
|
+
module CodeObject
|
4
|
+
|
5
|
+
module Type
|
6
|
+
|
7
|
+
@@types = {}
|
8
|
+
|
9
|
+
def self.register(tokenid, klass)
|
10
|
+
@@types[tokenid.to_sym] = klass
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.create_matching_object(tokenlines)
|
14
|
+
klass = self.find_klass(tokenlines) or return nil
|
15
|
+
self[klass.token].new(klass.content)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def self.[](tokenid)
|
21
|
+
@@types[tokenid.to_sym]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.include?(tokenid)
|
25
|
+
@@types.has_key? tokenid.to_sym
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.find_klass(tokenlines)
|
29
|
+
klass = tokenlines.select {|t| self.include? t.token }
|
30
|
+
|
31
|
+
if klass.size > 1
|
32
|
+
raise CodeObject::MultipleTypeDeclarations.new, "Wrong number of TypeDeclarations: #{klass}"
|
33
|
+
elsif klass.size == 0
|
34
|
+
# it's not possible to create instance
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
klass.first
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/lib/configs.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Configs
|
2
|
+
|
3
|
+
def self.set(sym_or_hash, value = nil)
|
4
|
+
|
5
|
+
unless sym_or_hash.is_a? Hash
|
6
|
+
sym_or_hash = { sym_or_hash => value }
|
7
|
+
end
|
8
|
+
|
9
|
+
sym_or_hash.each_pair do |attr, value|
|
10
|
+
set_attribute(attr, value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.attributes
|
15
|
+
class_variables.map do |var|
|
16
|
+
var.to_s.scan(/@@(.*)/).first.first.to_sym
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.method_missing(method_name, *args)
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.clear
|
25
|
+
class_variables.each do |var|
|
26
|
+
class_variable_set(var, nil)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def self.set_attribute(key, value)
|
33
|
+
|
34
|
+
key = key.to_s
|
35
|
+
|
36
|
+
class_variable_set("@@#{key}", value)
|
37
|
+
|
38
|
+
class_eval <<-EOS
|
39
|
+
def self.#{key}
|
40
|
+
return @@#{key}
|
41
|
+
end
|
42
|
+
EOS
|
43
|
+
|
44
|
+
if value.is_a? TrueClass or value.is_a? FalseClass
|
45
|
+
class_eval <<-EOS
|
46
|
+
def self.#{key}?
|
47
|
+
return @@#{key}
|
48
|
+
end
|
49
|
+
EOS
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../dom/dom'
|
2
|
+
|
3
|
+
module Document
|
4
|
+
|
5
|
+
class Document
|
6
|
+
|
7
|
+
include Dom::Node
|
8
|
+
|
9
|
+
FILE_ENDINGS = /\.(markdown|mdown|md|mkd|mkdn)$/i
|
10
|
+
|
11
|
+
attr_reader :name, :path, :content
|
12
|
+
|
13
|
+
def initialize(path, content)
|
14
|
+
|
15
|
+
path = File.basename(path).gsub(FILE_ENDINGS, '')
|
16
|
+
|
17
|
+
# please do not confuse Filepath of Document (like docs/README.md) with path in Dom
|
18
|
+
# an .md will be stripped from the name and README.SOMEMORE.md can be used as path information
|
19
|
+
@path, @name, @content = ".#{path}", extract_name_from(path), content
|
20
|
+
super()
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/lib/dom/dom.rb
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
# ../data.img#1811391:1 && #1811396:1
|
2
|
+
require_relative 'no_doc'
|
3
|
+
require_relative 'exceptions'
|
4
|
+
|
5
|
+
# Storing {CodeObject::Base Objects}
|
6
|
+
# ==================================
|
7
|
+
#
|
8
|
+
# The datastructure is based on a treelike concept. Every new inserted
|
9
|
+
# {CodeObject::Base object} is represented by a {Dom::Node node} of the tree.
|
10
|
+
# There are three types of nodes:
|
11
|
+
#
|
12
|
+
# 1. {Dom::NoDoc Not documented nodes} that contain other nodes
|
13
|
+
# 2. {Dom::Node Documented nodes}, that contain other nodes
|
14
|
+
# 3. Leafs of the tree, without children. (Those leafs have to be
|
15
|
+
# {Dom::Node documented nodes})
|
16
|
+
#
|
17
|
+
# The architecure of the Dom looks pretty much like this:
|
18
|
+
#
|
19
|
+
# 
|
20
|
+
#
|
21
|
+
# Take the following code-sample:
|
22
|
+
#
|
23
|
+
# /**
|
24
|
+
# * @function Foo.bar
|
25
|
+
# */
|
26
|
+
#
|
27
|
+
# The tree could look like:
|
28
|
+
#
|
29
|
+
# ROOT
|
30
|
+
# |
|
31
|
+
# Foo (not documented)
|
32
|
+
# |
|
33
|
+
# bar
|
34
|
+
#
|
35
|
+
# Extending this example with two other comments
|
36
|
+
#
|
37
|
+
# /**
|
38
|
+
# * @object Foo
|
39
|
+
# */
|
40
|
+
#
|
41
|
+
# /**
|
42
|
+
# * @object Foo.baz
|
43
|
+
# */
|
44
|
+
#
|
45
|
+
# leads to this tree:
|
46
|
+
#
|
47
|
+
# ROOT
|
48
|
+
# |
|
49
|
+
# Foo
|
50
|
+
# / \
|
51
|
+
# bar baz
|
52
|
+
#
|
53
|
+
# Now all nodes are documented (i.e. a CodeObject exists for every node) and `Foo`
|
54
|
+
# contains two other CodeObjects. `bar` and `baz` are leafs of the tree.
|
55
|
+
#
|
56
|
+
# Adding a new node to the tree is as simple as:
|
57
|
+
#
|
58
|
+
# Dom.add_node "Foo.bar", my_code_object
|
59
|
+
#
|
60
|
+
# Traversing the Tree
|
61
|
+
# -------------------
|
62
|
+
# There are several method, which you can use to navigate throught the dom. The
|
63
|
+
# most important is the {Dom::Node#[] children selector}.
|
64
|
+
#
|
65
|
+
# The tree above could be traversed using the following operations:
|
66
|
+
#
|
67
|
+
# Dom[:Foo]
|
68
|
+
# #=> #<CodeObject::Base:72903230 @parent=__ROOT__ @children=[:bar, :baz]>
|
69
|
+
#
|
70
|
+
# Dom[:Foo][:bar]
|
71
|
+
# #=> #<CodeObject::Base:72919910 @parent=Foo @children=[]>
|
72
|
+
#
|
73
|
+
#
|
74
|
+
# The Root Node
|
75
|
+
# -------------
|
76
|
+
# The Dom inherits functionality from it's **root-node**. So all method's
|
77
|
+
# invoked on the root node, can be expressed equivalent as member of the Dom.
|
78
|
+
#
|
79
|
+
# Dom.root[:some_child] <=> Dom[:some_child]
|
80
|
+
# Dom.root.children <=> Dom.children
|
81
|
+
# Dom.root.print_tree <=> Dom.print_tree
|
82
|
+
#
|
83
|
+
# Please note, that some methods of the root-node are hidden behind direct
|
84
|
+
# implementations.
|
85
|
+
#
|
86
|
+
# Dom.add_node != Dom.root.add_node
|
87
|
+
#
|
88
|
+
# For the example above the full UML-Graph, including the root-node, could look
|
89
|
+
# like:
|
90
|
+
#
|
91
|
+
# 
|
92
|
+
#
|
93
|
+
# @example Adding some Nodes
|
94
|
+
# o1 = CodeObject::Object.new "foo"
|
95
|
+
# o2 = CodeObject::Object.new "poo"
|
96
|
+
# o3 = CodeObject::Object.new "bar"
|
97
|
+
# o4 = CodeObject::Object.new "baz"
|
98
|
+
# Dom.add_node "foo", o1
|
99
|
+
# Dom.add_node "foo.poo", o2
|
100
|
+
# Dom.add_node "foo.poo.bar", o3
|
101
|
+
# Dom.add_node "foo.poo.baz", o4
|
102
|
+
#
|
103
|
+
# Dom.print_tree
|
104
|
+
# # -foo
|
105
|
+
# # -poo
|
106
|
+
# # -bar
|
107
|
+
# # -baz
|
108
|
+
#
|
109
|
+
# Using the Dom for Documents
|
110
|
+
# -------------------------------
|
111
|
+
# The Dom can be used in **two** ways. Because Document-structure can be very similiar to the one
|
112
|
+
# of our documentation-elements there are two kind of `root-nodes`:
|
113
|
+
#
|
114
|
+
# 1. {Dom.root}
|
115
|
+
# 2. {Dom.docs}
|
116
|
+
#
|
117
|
+
# @see Dom::Node
|
118
|
+
# @see Dom::NoDoc
|
119
|
+
# @see CodeObject::Base
|
120
|
+
# @see Document::Document
|
121
|
+
module Dom
|
122
|
+
|
123
|
+
@@cache_path = File.expand_path("../../../cache/dom.cache", __FILE__)
|
124
|
+
|
125
|
+
@@root = NoDoc.new('__ROOT__')
|
126
|
+
@@docs = NoDoc.new('__DOCUMENTS__')
|
127
|
+
|
128
|
+
|
129
|
+
# @group Dom Access-Methods
|
130
|
+
|
131
|
+
# @return [Dom::NoDoc] The Dom's root-node
|
132
|
+
def self.root
|
133
|
+
@@root
|
134
|
+
end
|
135
|
+
|
136
|
+
# @group Dom Management Methods
|
137
|
+
|
138
|
+
# Reset the Dom to it's initial state by creating an empty {.root root-node}
|
139
|
+
def self.clear
|
140
|
+
@@root = NoDoc.new('__ROOT__')
|
141
|
+
end
|
142
|
+
|
143
|
+
# @group Caching Methods
|
144
|
+
|
145
|
+
# Serializes and dumps the complete Domtree (but without last_position) to the
|
146
|
+
# specified `path`. If no `path` is given, the default `@@cache_path` will be
|
147
|
+
# used instead.
|
148
|
+
#
|
149
|
+
# This Method can be useful, to save a specific state of the Domtree to disk
|
150
|
+
# and reuse it later, without the need to reconstruct it from zero.
|
151
|
+
#
|
152
|
+
# @note To recreate the Dom from the dump-file, use {.load}.
|
153
|
+
#
|
154
|
+
# @param [String] file the filepath, where to write the serialized data
|
155
|
+
def self.dump(file = @@cache_path)
|
156
|
+
File.open(file, 'w') do |f|
|
157
|
+
f.write Marshal.dump @@root
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Loads the {.dump serialized Dom} and replaces the current root node with
|
162
|
+
# the one created from the file.
|
163
|
+
#
|
164
|
+
# @see .dump
|
165
|
+
# @param [String] file the filepath from which to load the Dom
|
166
|
+
def self.load(file = @@cache_path)
|
167
|
+
@@root = Marshal.load(File.read(file))
|
168
|
+
end
|
169
|
+
|
170
|
+
# @group Document Objects
|
171
|
+
|
172
|
+
# @return [Dom::NoDoc] the root of the Documenttree, consisting of {Document::Document}
|
173
|
+
def self.docs
|
174
|
+
@@docs
|
175
|
+
end
|
176
|
+
|
177
|
+
protected
|
178
|
+
|
179
|
+
# try to look up missing methods in @@root
|
180
|
+
def self.method_missing(method_name, *args)
|
181
|
+
if @@root.respond_to? method_name
|
182
|
+
@@root.send method_name, *args
|
183
|
+
else
|
184
|
+
raise NoMethodError.new(method_name.to_s)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|