shomen 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby +43 -0
- data/.yardopts +7 -0
- data/HISTORY.rdoc +10 -0
- data/NOTES.rdoc +5 -0
- data/README.rdoc +68 -0
- data/bin/shomen +4 -0
- data/lib/shomen.yml +43 -0
- data/lib/shomen/cli.rb +22 -0
- data/lib/shomen/cli/abstract.rb +120 -0
- data/lib/shomen/cli/rdoc.rb +122 -0
- data/lib/shomen/cli/tomdoc.rb +119 -0
- data/lib/shomen/cli/yard.rb +118 -0
- data/lib/shomen/core_ext/hash.rb +3 -0
- data/lib/shomen/metadata.rb +81 -0
- data/lib/shomen/model.rb +21 -0
- data/lib/shomen/model/abstract.rb +84 -0
- data/lib/shomen/model/attribute.rb +25 -0
- data/lib/shomen/model/class.rb +19 -0
- data/lib/shomen/model/constant.rb +34 -0
- data/lib/shomen/model/document.rb +36 -0
- data/lib/shomen/model/interface.rb +35 -0
- data/lib/shomen/model/method.rb +104 -0
- data/lib/shomen/model/module.rb +56 -0
- data/lib/shomen/model/script.rb +49 -0
- data/lib/shomen/rdoc.rb +598 -0
- data/lib/shomen/rdoc/extensions.rb +145 -0
- data/lib/shomen/server.rb +10 -0
- data/lib/shomen/tomdoc.rb +151 -0
- data/lib/shomen/yard.rb +471 -0
- data/spec/01_metadata.rdoc +24 -0
- data/spec/02_class.rdoc +63 -0
- data/spec/03_module.rdoc +59 -0
- data/spec/04_constant.rdoc +59 -0
- data/spec/05_method.rdoc +286 -0
- data/spec/08_rdoc/01_interface_overloading.rdoc +94 -0
- data/spec/09_yard/01_interface_overloading.rdoc +93 -0
- data/spec/applique/shomen.rb +26 -0
- data/spec/fixture/lib/example.rb +52 -0
- metadata +122 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module Shomen
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
require 'shomen/model/abstract'
|
6
|
+
|
7
|
+
#
|
8
|
+
class Constant < Abstract
|
9
|
+
#
|
10
|
+
def self.type; 'constant'; end
|
11
|
+
|
12
|
+
# Constant's basename, must start with a capitalized letter.
|
13
|
+
attr_accessor :name
|
14
|
+
|
15
|
+
#
|
16
|
+
attr_accessor :namespace
|
17
|
+
|
18
|
+
#
|
19
|
+
attr_accessor :comment
|
20
|
+
|
21
|
+
# Format of comment (rdoc, markdown or plain).
|
22
|
+
attr_accessor :format
|
23
|
+
|
24
|
+
#
|
25
|
+
attr_accessor :value
|
26
|
+
|
27
|
+
#
|
28
|
+
attr_accessor :files
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Shomen
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
require 'shomen/model/abstract'
|
6
|
+
|
7
|
+
#
|
8
|
+
class Document < Abstract
|
9
|
+
#
|
10
|
+
def self.type
|
11
|
+
'document'
|
12
|
+
end
|
13
|
+
|
14
|
+
#def key=(path)
|
15
|
+
# path = '/' + path unless path[0,1] == '/'
|
16
|
+
# super(path)
|
17
|
+
#end
|
18
|
+
|
19
|
+
attr_accessor :name
|
20
|
+
|
21
|
+
#attr_accessor :parent
|
22
|
+
|
23
|
+
attr_accessor :path
|
24
|
+
|
25
|
+
attr_accessor :mtime
|
26
|
+
|
27
|
+
attr_accessor :text
|
28
|
+
|
29
|
+
# Format of comment (rdoc, markdown or plain).
|
30
|
+
attr_accessor :format
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Shomen
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
require 'shomen/model/abstract'
|
6
|
+
|
7
|
+
#
|
8
|
+
class Interface < AbstractPrime
|
9
|
+
|
10
|
+
# TODO: validate that there is an interface image.
|
11
|
+
def initialize(settings={})
|
12
|
+
#@table = {'arguments'=>[], 'parameters'=>[]}
|
13
|
+
super(settings)
|
14
|
+
end
|
15
|
+
|
16
|
+
# The source code "image" of the method's inteface.
|
17
|
+
attr_accessor :signature
|
18
|
+
|
19
|
+
# Arguments breakdown.
|
20
|
+
attr_accessor :arguments
|
21
|
+
|
22
|
+
# Parameters breakdown.
|
23
|
+
attr_accessor :parameters
|
24
|
+
|
25
|
+
# Block
|
26
|
+
attr_accessor :block
|
27
|
+
|
28
|
+
# Return value.
|
29
|
+
attr_accessor :returns
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Shomen
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
require 'shomen/model/abstract'
|
6
|
+
require 'shomen/model/interface'
|
7
|
+
|
8
|
+
#
|
9
|
+
class Method < Abstract
|
10
|
+
|
11
|
+
#
|
12
|
+
def initialize(settings={})
|
13
|
+
super(settings)
|
14
|
+
@table['declarations'] ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Method's name.
|
18
|
+
attr_accessor :name
|
19
|
+
|
20
|
+
# Method's namespace.
|
21
|
+
attr_accessor :namespace
|
22
|
+
|
23
|
+
# Comment accompanying method definition.
|
24
|
+
attr_accessor :comment
|
25
|
+
|
26
|
+
# Format of comment (rdoc, markdown or plain).
|
27
|
+
attr_accessor :format
|
28
|
+
|
29
|
+
# Singleton method `true` or `false/nil`.
|
30
|
+
attr_accessor :singleton
|
31
|
+
|
32
|
+
# Delarations is a list of keywords that designates characteristics
|
33
|
+
# about a method. Common characteristics include `reader`, `writer`
|
34
|
+
# or `accessor` if the method is defined via an attr method; `public`
|
35
|
+
# `private` or `protected` given the methods visibility; and `class`
|
36
|
+
# or `instance` given the methods scope. Default designations are
|
37
|
+
# are impled if not specifically stated, such as `public` and `instance`.
|
38
|
+
#
|
39
|
+
# Using a declarations list simplifies the Shomen data format by allowing
|
40
|
+
# declarations to be freely defined, rather than creating a field for each
|
41
|
+
# possible designation possible.
|
42
|
+
attr_accessor :declarations
|
43
|
+
|
44
|
+
# Aliases.
|
45
|
+
attr_accessor :aliases
|
46
|
+
|
47
|
+
# Aliases.
|
48
|
+
attr_accessor :alias_for
|
49
|
+
|
50
|
+
# Breakdown of interfaces signature, arguments, parameters, block argument
|
51
|
+
# an return values.
|
52
|
+
attr_accessor :interfaces
|
53
|
+
|
54
|
+
#
|
55
|
+
def interfaces=(array)
|
56
|
+
self['interfaces'] = (
|
57
|
+
array.map do |settings|
|
58
|
+
case settings
|
59
|
+
when Interface
|
60
|
+
settings
|
61
|
+
else
|
62
|
+
Interface.new(settings)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
# List of possible returns types.
|
69
|
+
attr_accessor :returns
|
70
|
+
|
71
|
+
# List of possible raised errors.
|
72
|
+
attr_accessor :raises
|
73
|
+
|
74
|
+
# Method generated dynamically?
|
75
|
+
attr_accessor :dynamic
|
76
|
+
|
77
|
+
# Filename.
|
78
|
+
attr_accessor :file
|
79
|
+
|
80
|
+
# Line number.
|
81
|
+
attr_accessor :line
|
82
|
+
|
83
|
+
# Source code.
|
84
|
+
attr_accessor :source
|
85
|
+
|
86
|
+
# Source code language.
|
87
|
+
attr_accessor :language
|
88
|
+
|
89
|
+
|
90
|
+
# Deprecated method.
|
91
|
+
alias :parent :namespace
|
92
|
+
|
93
|
+
#
|
94
|
+
def to_h
|
95
|
+
h = super
|
96
|
+
h['!'] = 'method'
|
97
|
+
h['interfaces'] = (interfaces || []).map{ |s| s.to_h }
|
98
|
+
h
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Shomen
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
require 'shomen/model/abstract'
|
6
|
+
|
7
|
+
#
|
8
|
+
class Module < Abstract
|
9
|
+
|
10
|
+
#
|
11
|
+
def self.type; 'module'; end
|
12
|
+
|
13
|
+
# Method's name.
|
14
|
+
attr_accessor :name
|
15
|
+
|
16
|
+
# Namespace of module is the path of the class or module
|
17
|
+
# containing this module.
|
18
|
+
attr_accessor :namespace
|
19
|
+
|
20
|
+
# Comment associated with module.
|
21
|
+
attr_accessor :comment
|
22
|
+
|
23
|
+
# Format of comment (rdoc, markdown or plain).
|
24
|
+
attr_accessor :format
|
25
|
+
|
26
|
+
# Mixins.
|
27
|
+
attr_accessor :includes
|
28
|
+
|
29
|
+
# Metaclass mixins.
|
30
|
+
attr_accessor :extensions
|
31
|
+
|
32
|
+
# Constants defined within this module.
|
33
|
+
attr_accessor :constants
|
34
|
+
|
35
|
+
#
|
36
|
+
attr_accessor :modules
|
37
|
+
|
38
|
+
#
|
39
|
+
attr_accessor :classes
|
40
|
+
|
41
|
+
# List of instance methods defined in the module.
|
42
|
+
attr_accessor :methods
|
43
|
+
|
44
|
+
# List of attributes.
|
45
|
+
attr_accessor :accessors
|
46
|
+
|
47
|
+
# The files in which the module is defined.
|
48
|
+
attr_accessor :files
|
49
|
+
|
50
|
+
#
|
51
|
+
alias :fullname :path
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Shomen
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
require 'shomen/model/document'
|
6
|
+
|
7
|
+
#
|
8
|
+
class Script < Document
|
9
|
+
#
|
10
|
+
def self.type; 'script'; end
|
11
|
+
|
12
|
+
#
|
13
|
+
attr_accessor :source
|
14
|
+
|
15
|
+
# Route textto source.
|
16
|
+
alias :text :source
|
17
|
+
alias :text= :source=
|
18
|
+
|
19
|
+
#
|
20
|
+
attr_accessor :language
|
21
|
+
|
22
|
+
#
|
23
|
+
attr_accessor :name
|
24
|
+
|
25
|
+
attr_accessor :path
|
26
|
+
|
27
|
+
attr_accessor :mtime
|
28
|
+
|
29
|
+
attr_accessor :header
|
30
|
+
|
31
|
+
attr_accessor :footer
|
32
|
+
|
33
|
+
attr_accessor :requires
|
34
|
+
|
35
|
+
attr_accessor :constants
|
36
|
+
|
37
|
+
attr_accessor :modules
|
38
|
+
|
39
|
+
attr_accessor :classes
|
40
|
+
|
41
|
+
attr_accessor :class_methods
|
42
|
+
|
43
|
+
attr_accessor :methods
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/lib/shomen/rdoc.rb
ADDED
@@ -0,0 +1,598 @@
|
|
1
|
+
#begin
|
2
|
+
# # requiroing rubygems is needed here b/c ruby comes with
|
3
|
+
# # rdoc but it's not the latest version.
|
4
|
+
# require 'rubygems'
|
5
|
+
# #gem 'rdoc', '>= 2.4' unless ENV['RDOC_TEST'] or defined?($rdoc_rakefile)
|
6
|
+
# gem "rdoc", ">= 2.4.2"
|
7
|
+
#rescue
|
8
|
+
#end
|
9
|
+
|
10
|
+
require 'fileutils'
|
11
|
+
require 'pathname'
|
12
|
+
require 'yaml'
|
13
|
+
require 'json'
|
14
|
+
|
15
|
+
require 'rdoc/rdoc'
|
16
|
+
require 'rdoc/generator'
|
17
|
+
require 'rdoc/generator/markup'
|
18
|
+
|
19
|
+
require 'shomen/metadata'
|
20
|
+
require 'shomen/model' # TODO: have metadata in model
|
21
|
+
require 'shomen/rdoc/extensions'
|
22
|
+
|
23
|
+
# Shomen Adaptor for RDoc utilizes the rdoc tool to parse ruby source code
|
24
|
+
# to build a Shomen documenation file.
|
25
|
+
#
|
26
|
+
# RDoc is almost entirely a free-form documentation system, so it is not
|
27
|
+
# possible for Shomen to fully harness all the details it can support from
|
28
|
+
# the RDoc documentation, such as method argument descriptions.
|
29
|
+
|
30
|
+
class RDoc::Generator::Shomen
|
31
|
+
|
32
|
+
# Register shomen generator with RDoc.
|
33
|
+
RDoc::RDoc.add_generator(self)
|
34
|
+
|
35
|
+
#include RDocShomen::Metadata
|
36
|
+
|
37
|
+
# Standard generator factory method.
|
38
|
+
def self.for(options)
|
39
|
+
new(options)
|
40
|
+
end
|
41
|
+
|
42
|
+
# User options from the command line.
|
43
|
+
attr :options
|
44
|
+
|
45
|
+
# List of all classes and modules.
|
46
|
+
#def all_classes_and_modules
|
47
|
+
# @all_classes_and_modules ||= RDoc::TopLevel.all_classes_and_modules
|
48
|
+
#end
|
49
|
+
|
50
|
+
# In the world of the RDoc Generators #classes is the same
|
51
|
+
# as #all_classes_and_modules. Well, except that its sorted
|
52
|
+
# too. For classes sans modules, see #types.
|
53
|
+
|
54
|
+
def classes
|
55
|
+
@classes ||= RDoc::TopLevel.all_classes_and_modules.sort
|
56
|
+
end
|
57
|
+
|
58
|
+
# Only toplevel classes and modules.
|
59
|
+
def classes_toplevel
|
60
|
+
@classes_toplevel ||= classes.select {|klass| !(RDoc::ClassModule === klass.parent) }
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
def files
|
65
|
+
@files ||= (
|
66
|
+
@files_rdoc.select{ |f| f.parser != RDoc::Parser::Simple }
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
# List of toplevel files. RDoc supplies this via the #generate method.
|
71
|
+
def files_toplevel
|
72
|
+
@files_toplevel ||= (
|
73
|
+
@files_rdoc.select{ |f| f.parser == RDoc::Parser::Simple }
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
|
79
|
+
def files_hash
|
80
|
+
@files ||= RDoc::TopLevel.files_hash
|
81
|
+
end
|
82
|
+
|
83
|
+
# List of all methods in all classes and modules.
|
84
|
+
def methods_all
|
85
|
+
@methods_all ||= classes.map{ |m| m.method_list }.flatten.sort
|
86
|
+
end
|
87
|
+
|
88
|
+
# List of all attributes in all classes and modules.
|
89
|
+
def attributes_all
|
90
|
+
@attributes_all ||= classes.map{ |m| m.attributes }.flatten.sort
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
def constants_all
|
95
|
+
@constants_all ||= classes.map{ |c| c.constants }.flatten
|
96
|
+
end
|
97
|
+
|
98
|
+
## TODO: What's this then?
|
99
|
+
##def json_creatable?
|
100
|
+
## RDoc::TopLevel.json_creatable?
|
101
|
+
##end
|
102
|
+
|
103
|
+
# RDoc needs this to function.
|
104
|
+
def class_dir ; nil ; end
|
105
|
+
|
106
|
+
# RDoc needs this to function.
|
107
|
+
def file_dir ; nil ; end
|
108
|
+
|
109
|
+
# TODO: Rename ?
|
110
|
+
def shomen
|
111
|
+
@table || {}
|
112
|
+
end
|
113
|
+
|
114
|
+
# Build the initial indices and output objects
|
115
|
+
# based on an array of top level objects containing
|
116
|
+
# the extracted information.
|
117
|
+
def generate(files)
|
118
|
+
@files_rdoc = files.sort
|
119
|
+
|
120
|
+
@table = {}
|
121
|
+
|
122
|
+
generate_metadata
|
123
|
+
generate_constants
|
124
|
+
generate_classes
|
125
|
+
#generate_attributes
|
126
|
+
generate_methods
|
127
|
+
generate_documents
|
128
|
+
generate_scripts # must be last b/c it depends on the others
|
129
|
+
|
130
|
+
# TODO: method accessor fields need to be handled
|
131
|
+
|
132
|
+
# THINK: Internal referencing model, YAML and JSYNC ?
|
133
|
+
#ref_table = reference_table(@table)
|
134
|
+
|
135
|
+
#rescue StandardError => err
|
136
|
+
# debug_msg "%s: %s\n %s" % [ err.class.name, err.message, err.backtrace.join("\n ") ]
|
137
|
+
# raise err
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
protected
|
142
|
+
|
143
|
+
#
|
144
|
+
def initialize(options)
|
145
|
+
@options = options
|
146
|
+
#@options.diagram = false # why?
|
147
|
+
|
148
|
+
@path_base = Pathname.pwd.expand_path
|
149
|
+
|
150
|
+
# TODO: This is probably not needed any more.
|
151
|
+
@path_output = Pathname.new(@options.op_dir).expand_path(@path_base)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Current pathname.
|
155
|
+
attr :path_base
|
156
|
+
|
157
|
+
# The output path.
|
158
|
+
attr :path_output
|
159
|
+
|
160
|
+
#
|
161
|
+
def path_output_relative(path=nil)
|
162
|
+
if path
|
163
|
+
path.to_s.sub(path_base.to_s+'/', '')
|
164
|
+
else
|
165
|
+
@path_output_relative ||= path_output.to_s.sub(path_base.to_s+'/', '')
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
def generate_metadata
|
171
|
+
metadata = Shomen::Metadata.new
|
172
|
+
@table['(metadata)'] = metadata.to_h
|
173
|
+
end
|
174
|
+
|
175
|
+
# Add constants to table.
|
176
|
+
def generate_constants
|
177
|
+
debug_msg "Generating constant documentation:"
|
178
|
+
constants_all.each do |rdoc|
|
179
|
+
model = Shomen::Model::Constant.new
|
180
|
+
|
181
|
+
model.path = rdoc.parent.full_name + '::' + rdoc.name
|
182
|
+
model.name = rdoc.name
|
183
|
+
model.namespace = rdoc.parent.full_name
|
184
|
+
model.comment = rdoc.comment
|
185
|
+
model.format = 'rdoc'
|
186
|
+
model.value = rdoc.value
|
187
|
+
model.files = ["/#{rdoc.file.full_name}"]
|
188
|
+
|
189
|
+
@table[model.path] = model.to_h
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Add classes (and modules) to table.
|
194
|
+
def generate_classes
|
195
|
+
debug_msg "Generating class/module documentation:"
|
196
|
+
|
197
|
+
classes.each do |rdoc_class|
|
198
|
+
debug_msg "%s (%s)" % [ rdoc_class.full_name, rdoc_class.path ]
|
199
|
+
|
200
|
+
if rdoc_class.type=='class'
|
201
|
+
model = Shomen::Model::Class.new
|
202
|
+
else
|
203
|
+
model = Shomen::Model::Module.new
|
204
|
+
end
|
205
|
+
|
206
|
+
model.path = rdoc_class.full_name
|
207
|
+
model.name = rdoc_class.name
|
208
|
+
model.namespace = rdoc_class.full_name.split('::')[0...-1].join('::')
|
209
|
+
model.includes = rdoc_class.includes.map{ |x| x.name } # FIXME: How to "lookup" full name?
|
210
|
+
model.extensions = [] # TODO: How to get extensions?
|
211
|
+
model.comment = rdoc_class.comment
|
212
|
+
model.format = 'rdoc'
|
213
|
+
model.constants = rdoc_class.constants.map{ |x| complete_name(x.name, rdoc_class.full_name) }
|
214
|
+
model.modules = rdoc_class.modules.map{ |x| complete_name(x.name, rdoc_class.full_name) }
|
215
|
+
model.classes = rdoc_class.classes.map{ |x| complete_name(x.name, rdoc_class.full_name) }
|
216
|
+
model.methods = rdoc_class.method_list.map{ |m| method_name(m) }.uniq
|
217
|
+
model.accessors = rdoc_class.attributes.map{ |a| method_name(a) }.uniq #+ ":#{a.rw}" }.uniq
|
218
|
+
model.files = rdoc_class.in_files.map{ |x| "/#{x.full_name}" }
|
219
|
+
|
220
|
+
if rdoc_class.type == 'class'
|
221
|
+
# HACK: No idea why RDoc is returning some weird superclass:
|
222
|
+
# <RDoc::NormalClass:0xd924d4 class Object < BasicObject includes: []
|
223
|
+
# attributes: [] methods: [#<RDoc::AnyMethod:0xd92b8c Object#fileutils
|
224
|
+
# (public)>] aliases: []>
|
225
|
+
# Maybe it has something to do with #fileutils?
|
226
|
+
model.superclass = (
|
227
|
+
case rdoc_class.superclass
|
228
|
+
when nil
|
229
|
+
when String
|
230
|
+
rdoc_class.superclass
|
231
|
+
else
|
232
|
+
rdoc_class.superclass.full_name
|
233
|
+
end
|
234
|
+
)
|
235
|
+
end
|
236
|
+
|
237
|
+
@table[model.path] = model.to_h
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Transform RDoc methods to Shomen model and add to table.
|
242
|
+
#
|
243
|
+
# TODO: How to get literal interface separate from call-sequnces?
|
244
|
+
def generate_methods
|
245
|
+
debug_msg "Generating method documentation:"
|
246
|
+
|
247
|
+
list = methods_all + attributes_all
|
248
|
+
|
249
|
+
list.each do |rdoc_method|
|
250
|
+
#debug_msg "%s" % [rdoc_method.full_name]
|
251
|
+
|
252
|
+
#full_name = method_name(m)
|
253
|
+
#'prettyname' => m.pretty_name,
|
254
|
+
#'type' => m.type, # class or instance
|
255
|
+
|
256
|
+
model = Shomen::Model::Method.new
|
257
|
+
|
258
|
+
model.path = method_name(rdoc_method)
|
259
|
+
model.name = rdoc_method.name
|
260
|
+
model.namespace = rdoc_method.parent_name
|
261
|
+
model.comment = rdoc_method.comment
|
262
|
+
model.format = 'rdoc'
|
263
|
+
model.aliases = rdoc_method.aliases.map{ |a| method_name(a) }
|
264
|
+
model.alias_for = method_name(rdoc_method.is_alias_for)
|
265
|
+
model.singleton = rdoc_method.singleton
|
266
|
+
|
267
|
+
model.declarations << rdoc_method.type.to_s #singleton ? 'class' : 'instance'
|
268
|
+
model.declarations << rdoc_method.visibility.to_s
|
269
|
+
|
270
|
+
model.interfaces = []
|
271
|
+
if rdoc_method.call_seq
|
272
|
+
rdoc_method.call_seq.split("\n").each do |cs|
|
273
|
+
cs = cs.to_s.strip
|
274
|
+
model.interfaces << parse_interface(cs) unless cs == ''
|
275
|
+
end
|
276
|
+
end
|
277
|
+
model.interfaces << parse_interface("#{rdoc_method.name}#{rdoc_method.params}")
|
278
|
+
|
279
|
+
model.returns = [] # RDoc doesn't support specifying return values
|
280
|
+
model.file = '/'+rdoc_method.source_code_location.first
|
281
|
+
model.line = rdoc_method.source_code_location.last.to_i
|
282
|
+
model.source = rdoc_method.source_code_raw
|
283
|
+
|
284
|
+
if rdoc_method.respond_to?(:c_function)
|
285
|
+
model.language = rdoc_method.c_function ? 'c' : 'ruby'
|
286
|
+
else
|
287
|
+
model.language = 'ruby'
|
288
|
+
end
|
289
|
+
|
290
|
+
@table[model.path] = model.to_h
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
#--
|
295
|
+
=begin
|
296
|
+
#
|
297
|
+
def generate_attributes
|
298
|
+
#$stderr.puts "HERE!"
|
299
|
+
#$stderr.puts attributes_all.inspect
|
300
|
+
#exit
|
301
|
+
debug_msg "Generating attributes documentation:"
|
302
|
+
attributes_all.each do |rdoc_attribute|
|
303
|
+
debug_msg "%s" % [rdoc_attribute.full_name]
|
304
|
+
|
305
|
+
adapter = Shomen::RDoc::MethodAdapter.new(rdoc_attribute)
|
306
|
+
data = Shomen::Model::Method.new(adapter).to_h
|
307
|
+
|
308
|
+
@table[data['path']] = data
|
309
|
+
|
310
|
+
#code = m.source_code_raw
|
311
|
+
#file, line = m.source_code_location
|
312
|
+
|
313
|
+
#full_name = method_name(m)
|
314
|
+
|
315
|
+
#'prettyname' => m.pretty_name,
|
316
|
+
#'type' => m.type, # class or instance
|
317
|
+
|
318
|
+
#model_class = m.singleton ? Shomen::Model::Function : Shomen::Model::Method
|
319
|
+
#model_class = Shomen::Model::Attribute
|
320
|
+
|
321
|
+
#@table[full_name] = model_class.new(
|
322
|
+
# 'path' => full_name,
|
323
|
+
# 'name' => m.name,
|
324
|
+
# 'namespace' => m.parent_name,
|
325
|
+
# 'comment' => m.comment,
|
326
|
+
# 'access' => m.visibility.to_s,
|
327
|
+
# 'rw' => m.rw, # TODO: better name ?
|
328
|
+
# 'singleton' => m.singleton,
|
329
|
+
# 'aliases' => m.aliases.map{ |a| method_name(a) },
|
330
|
+
# 'alias_for' => method_name(m.is_alias_for),
|
331
|
+
# 'image' => m.params,
|
332
|
+
# 'arguments' => [],
|
333
|
+
# 'parameters' => [],
|
334
|
+
# 'block' => m.block_params, # TODO: what is block?
|
335
|
+
# 'interface' => m.arglists,
|
336
|
+
# 'returns' => [],
|
337
|
+
# 'file' => file,
|
338
|
+
# 'line' => line,
|
339
|
+
# 'source' => code
|
340
|
+
#).to_h
|
341
|
+
end
|
342
|
+
end
|
343
|
+
=end
|
344
|
+
#++
|
345
|
+
|
346
|
+
# Parse method interface.
|
347
|
+
#
|
348
|
+
# TODO: remove any trailing comment too
|
349
|
+
def parse_interface(interface)
|
350
|
+
args, block = [], {}
|
351
|
+
|
352
|
+
interface, returns = interface.split(/[=-]\>/)
|
353
|
+
interface = interface.strip
|
354
|
+
if i = interface.index(/\)\s*\{/)
|
355
|
+
block['signature'] = interface[i+1..-1].strip
|
356
|
+
interface = interface[0..i].strip
|
357
|
+
end
|
358
|
+
|
359
|
+
arguments = interface.strip.sub(/^.*?\(/,'').chomp(')')
|
360
|
+
arguments = arguments.split(/\s*\,\s*/)
|
361
|
+
arguments.each do |a|
|
362
|
+
if a.start_with?('&')
|
363
|
+
block['name'] = a
|
364
|
+
else
|
365
|
+
n,v = a.split('=')
|
366
|
+
args << (v ? {'name'=>n,'default'=>v} : {'name'=>n})
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
result = {}
|
371
|
+
result['signature'] = interface
|
372
|
+
result['arguments'] = args
|
373
|
+
result['block'] = block unless block.empty?
|
374
|
+
result['returns'] = returns.strip if returns
|
375
|
+
return result
|
376
|
+
end
|
377
|
+
private :parse_interface
|
378
|
+
|
379
|
+
# Generate entries for information files, e.g. `README.rdoc`.
|
380
|
+
def generate_documents
|
381
|
+
files_toplevel.each do |rdoc_document|
|
382
|
+
absolute_path = File.join(path_base, rdoc_document.full_name)
|
383
|
+
|
384
|
+
model = Shomen::Model::Document.new
|
385
|
+
|
386
|
+
model.path = rdoc_document.full_name
|
387
|
+
model.name = File.basename(absolute_path)
|
388
|
+
model.mtime = File.mtime(absolute_path)
|
389
|
+
model.text = File.read(absolute_path) #file.comment
|
390
|
+
model.format = mime_type(absolute_path)
|
391
|
+
|
392
|
+
@table['/'+model.path] = model.to_h
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# TODO: Add loadpath and make file path relative to it?
|
397
|
+
|
398
|
+
# Generate script entries.
|
399
|
+
def generate_scripts
|
400
|
+
#debug_msg "Generating file documentation in #{path_output_relative}:"
|
401
|
+
#templatefile = self.path_template + 'file.rhtml'
|
402
|
+
|
403
|
+
files.each do |rdoc_file|
|
404
|
+
debug_msg "%s" % [rdoc_file.full_name]
|
405
|
+
|
406
|
+
absolute_path = File.join(path_base, rdoc_file.full_name)
|
407
|
+
#rel_prefix = self.path_output.relative_path_from(outfile.dirname)
|
408
|
+
|
409
|
+
model = Shomen::Model::Script.new
|
410
|
+
|
411
|
+
model.path = rdoc_file.full_name
|
412
|
+
model.name = File.basename(rdoc_file.full_name)
|
413
|
+
model.mtime = File.mtime(absolute_path)
|
414
|
+
|
415
|
+
if Shomen.source?
|
416
|
+
model.source = File.read(absolute_path) #file.comment
|
417
|
+
model.language = mime_type(absolute_path)
|
418
|
+
end
|
419
|
+
|
420
|
+
#model.header =
|
421
|
+
#model.footer =
|
422
|
+
model.requires = rdoc_file.requires.map{ |r| r.name }
|
423
|
+
model.constants = rdoc_file.constants.map{ |c| c.full_name }
|
424
|
+
|
425
|
+
# note that this utilizes the table we are building
|
426
|
+
# so it needs to be the last thing done.
|
427
|
+
@table.each do |k, h|
|
428
|
+
case h['!']
|
429
|
+
when 'module'
|
430
|
+
model.modules ||= []
|
431
|
+
model.modules << k if h['files'].include?(rdoc_file.full_name)
|
432
|
+
when 'class'
|
433
|
+
model.classes ||= []
|
434
|
+
model.classes << k if h['files'].include?(rdoc_file.full_name)
|
435
|
+
when 'method'
|
436
|
+
model.methods ||= []
|
437
|
+
model.methods << k if h['file'] == rdoc_file.full_name
|
438
|
+
when 'class-method'
|
439
|
+
model.class_methods ||= []
|
440
|
+
model.class_methods << k if h['file'] == rdoc_file.full_name
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
@table['/'+model.path] = model.to_h
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
# Returns String of fully qualified name.
|
449
|
+
def complete_name(name, namespace)
|
450
|
+
if name !~ /^#{namespace}/
|
451
|
+
"#{namespace}::#{name}"
|
452
|
+
else
|
453
|
+
name
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
#
|
458
|
+
def collect_methods(class_module, singleton=false)
|
459
|
+
list = []
|
460
|
+
class_module.method_list.each do |m|
|
461
|
+
next if singleton ^ m.singleton
|
462
|
+
list << method_name(m)
|
463
|
+
end
|
464
|
+
list.uniq
|
465
|
+
end
|
466
|
+
|
467
|
+
#
|
468
|
+
def collect_attributes(class_module, singleton=false)
|
469
|
+
list = []
|
470
|
+
class_module.attributes.each do |a|
|
471
|
+
next if singleton ^ a.singleton
|
472
|
+
#p a.rw
|
473
|
+
#case a.rw
|
474
|
+
#when :write, 'W'
|
475
|
+
# list << "#{method_name(a)}="
|
476
|
+
#else
|
477
|
+
list << method_name(a)
|
478
|
+
#end
|
479
|
+
end
|
480
|
+
list.uniq
|
481
|
+
end
|
482
|
+
|
483
|
+
#
|
484
|
+
def method_name(method)
|
485
|
+
return nil if method.nil?
|
486
|
+
if method.singleton
|
487
|
+
i = method.full_name.rindex('::')
|
488
|
+
method.full_name[0...i] + '.' + method.full_name[i+2..-1]
|
489
|
+
else
|
490
|
+
method.full_name
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
#
|
495
|
+
def mime_type(path)
|
496
|
+
case File.extname(path)
|
497
|
+
when '.rb', '.rbx' then 'text/ruby'
|
498
|
+
when '.c' then 'text/c-source'
|
499
|
+
when '.rdoc' then 'text/rdoc'
|
500
|
+
when '.md', '.markdown' then 'text/markdown'
|
501
|
+
else 'text/plain'
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
# Output progress information if rdoc debugging is enabled
|
506
|
+
|
507
|
+
def debug_msg(msg)
|
508
|
+
return unless $DEBUG_RDOC
|
509
|
+
case msg[-1,1]
|
510
|
+
when '.' then tab = "= "
|
511
|
+
when ':' then tab = "== "
|
512
|
+
else tab = "* "
|
513
|
+
end
|
514
|
+
$stderr.puts(tab + msg)
|
515
|
+
end
|
516
|
+
|
517
|
+
end
|
518
|
+
|
519
|
+
|
520
|
+
|
521
|
+
#--
|
522
|
+
=begin
|
523
|
+
#
|
524
|
+
# N O T U S E D
|
525
|
+
#
|
526
|
+
|
527
|
+
# Sort based on how often the top level namespace occurs, and then on the
|
528
|
+
# name of the module -- this works for projects that put their stuff into
|
529
|
+
# a namespace, of course, but doesn't hurt if they don't.
|
530
|
+
def sort_salient(classes)
|
531
|
+
nscounts = classes.inject({}) do |counthash, klass|
|
532
|
+
top_level = klass.full_name.gsub( /::.*/, '' )
|
533
|
+
counthash[top_level] ||= 0
|
534
|
+
counthash[top_level] += 1
|
535
|
+
counthash
|
536
|
+
endfiles_toplevel
|
537
|
+
classes.sort_by{ |klass|
|
538
|
+
top_level = klass.full_name.gsub( /::.*/, '' )
|
539
|
+
[nscounts[top_level] * -1, klass.full_name]
|
540
|
+
}.select{ |klass|
|
541
|
+
klass.document_self
|
542
|
+
}
|
543
|
+
end
|
544
|
+
=end
|
545
|
+
|
546
|
+
=begin
|
547
|
+
# Loop through table and convert all named references into bonofied object
|
548
|
+
# references.
|
549
|
+
def reference_table(table)
|
550
|
+
debug_msg "== Generating Reference Table"
|
551
|
+
new_table = {}
|
552
|
+
table.each do |key, entry|
|
553
|
+
debug_msg "%s" % [key]
|
554
|
+
data = entry.dup
|
555
|
+
new_table[key] = data
|
556
|
+
case data['!']
|
557
|
+
when 'script'
|
558
|
+
data["constants"] = ref_list(data["constants"])
|
559
|
+
data["modules"] = ref_list(data["modules"])
|
560
|
+
data["classes"] = ref_list(data["classes"])
|
561
|
+
data["functions"] = ref_list(data["functions"])
|
562
|
+
data["methods"] = ref_list(data["methods"])
|
563
|
+
when 'file'
|
564
|
+
when 'constant'
|
565
|
+
data["namespace"] = ref_item(data["namespace"])
|
566
|
+
when 'module', 'class'
|
567
|
+
data["namespace"] = ref_item(data["namespace"])
|
568
|
+
data["includes"] = ref_list(data["includes"])
|
569
|
+
#data["extended"] = ref_list(data["extended"])
|
570
|
+
data["constants"] = ref_list(data["constants"])
|
571
|
+
data["modules"] = ref_list(data["modules"])
|
572
|
+
data["classes"] = ref_list(data["classes"])
|
573
|
+
data["functions"] = ref_list(data["functions"])
|
574
|
+
data["methods"] = ref_list(data["methods"])
|
575
|
+
data["files"] = ref_list(data["files"])
|
576
|
+
data["superclass"] = ref_item(data["superclass"]) if data.key?("superclass")
|
577
|
+
when 'method', 'function'
|
578
|
+
data["namespace"] = ref_item(data["namespace"])
|
579
|
+
data["file"] = ref_item(data["file"])
|
580
|
+
end
|
581
|
+
end
|
582
|
+
new_table
|
583
|
+
end
|
584
|
+
|
585
|
+
# Given a key, return the matching table item. If not found return the key.
|
586
|
+
def ref_item(key)
|
587
|
+
@table[key] || key
|
588
|
+
end
|
589
|
+
|
590
|
+
# Given a list of keys, return the matching table items.
|
591
|
+
def ref_list(keys)
|
592
|
+
#keys.map{ |k| @table[k] || k }
|
593
|
+
keys.map{ |k| @table[k] || nil }.compact
|
594
|
+
end
|
595
|
+
|
596
|
+
=end
|
597
|
+
#++
|
598
|
+
|