shomen-rdoc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.ruby ADDED
@@ -0,0 +1,59 @@
1
+ ---
2
+ source:
3
+ - meta
4
+ - meta/
5
+ authors:
6
+ - name: trans
7
+ email: transfire@gmail.com
8
+ copyrights: []
9
+ requirements:
10
+ - name: rdoc
11
+ version: 3+
12
+ - name: shomen-model
13
+ - name: detroit
14
+ groups:
15
+ - build
16
+ development: true
17
+ dependencies: []
18
+ alternatives: []
19
+ conflicts: []
20
+ repositories:
21
+ - uri: git://github.com/rubyworks/shomen-rdoc.git
22
+ scm: git
23
+ name: upstream
24
+ resources:
25
+ - uri: http://rubyworks.github.com/shomen-rdoc
26
+ name: home
27
+ type: home
28
+ - uri: http://github.com/rubyworks/shomen-rdoc/wiki
29
+ name: docs
30
+ type: doc
31
+ - uri: http://github.com/rubyworks/shomen-rdoc
32
+ name: code
33
+ type: code
34
+ - uri: http://github.com/rubyworks/shomen-rdoc/issues
35
+ name: bugs
36
+ type: bugs
37
+ - uri: http://groups.google.com/groups/rubyworks-mailinglist
38
+ name: mail
39
+ type: mail
40
+ - uri: http://chat.us.freenode.net/rubyworks
41
+ name: chat
42
+ type: chat
43
+ extra: {}
44
+ load_path:
45
+ - lib
46
+ revision: 0
47
+ created: '2010-07-01'
48
+ summary: Shomen Documentation via RDoc
49
+ title: Shomen RDoc
50
+ version: 0.1.0
51
+ name: shomen-rdoc
52
+ description: ! 'Shomen RDoc provides a command line utility for generating Shomen
53
+ standard
54
+
55
+ documentation via the RDoc''s `.rdoc` cache. It is an alternative to the
56
+
57
+ `rdoc-shomen` format plugin.'
58
+ organization: rubyworks
59
+ date: '2012-04-19'
@@ -0,0 +1,14 @@
1
+ = RELEASE HISTORY
2
+
3
+ == 0.1.0 / 2012-04-20
4
+
5
+ This is the initial public release of Shomen RDoc, spun-off
6
+ from the main Shomen library.
7
+
8
+ Please note that this utility is not in the best of shape
9
+ due to current issues with RDoc itself.
10
+
11
+ Changes:
12
+
13
+ * Happy Release Day!
14
+
@@ -0,0 +1,29 @@
1
+ Shomen RDoc - Shomen Generator via RDoc Documentation
2
+ (http://github.com/rubyworks/shomen-rdoc)
3
+
4
+ Copyright (c) 2011 Rubyworks. All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms,
7
+ with or without modification, are permitted provided that the following
8
+ conditions are met:
9
+
10
+ 1. Redistributions of source code must retain the above copyright notice,
11
+ this list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright
14
+ notice, this list of conditions and the following disclaimer in the
15
+ documentation and/or other materials provided with the distribution.
16
+
17
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
19
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20
+ COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ SPDX License: BSD-2-Clause
29
+
@@ -0,0 +1,6 @@
1
+ = Shomen RDoc
2
+
3
+ The Shomen RDoc utility is a utility to generate Shomen standard documentation
4
+ via RDoc's documentation cache (usually stored in `.rdoc`). This is an aternative
5
+ to using the rdoc-shomen format, which is an RDoc plugin rather then a stand alone
6
+ tool.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'shomen-rdoc'
3
+ Shomen::Rdoc::Command.run(*ARGV)
@@ -0,0 +1,26 @@
1
+ module Shomen
2
+
3
+ # Shomen Rdoc is used to generate Shomen formatted documentation
4
+ # from RDoc cache.
5
+ #
6
+ module Rdoc
7
+ #
8
+ def self.metadata
9
+ @metadata ||= (
10
+ require 'yaml'
11
+ YAML.load_file(File.dirname(__FILE__) + '/shomen-rdoc.yml')
12
+ )
13
+ end
14
+
15
+ #
16
+ def self.const_missing(name)
17
+ metadata[name.to_s.downcase] || super(name)
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ require 'shomen-model'
24
+ require 'shomen-rdoc/generator'
25
+ require 'shomen-rdoc/command'
26
+
@@ -0,0 +1,59 @@
1
+ ---
2
+ source:
3
+ - meta
4
+ - meta/
5
+ authors:
6
+ - name: trans
7
+ email: transfire@gmail.com
8
+ copyrights: []
9
+ requirements:
10
+ - name: rdoc
11
+ version: 3+
12
+ - name: shomen-model
13
+ - name: detroit
14
+ groups:
15
+ - build
16
+ development: true
17
+ dependencies: []
18
+ alternatives: []
19
+ conflicts: []
20
+ repositories:
21
+ - uri: git://github.com/rubyworks/shomen-rdoc.git
22
+ scm: git
23
+ name: upstream
24
+ resources:
25
+ - uri: http://rubyworks.github.com/shomen-rdoc
26
+ name: home
27
+ type: home
28
+ - uri: http://github.com/rubyworks/shomen-rdoc/wiki
29
+ name: docs
30
+ type: doc
31
+ - uri: http://github.com/rubyworks/shomen-rdoc
32
+ name: code
33
+ type: code
34
+ - uri: http://github.com/rubyworks/shomen-rdoc/issues
35
+ name: bugs
36
+ type: bugs
37
+ - uri: http://groups.google.com/groups/rubyworks-mailinglist
38
+ name: mail
39
+ type: mail
40
+ - uri: http://chat.us.freenode.net/rubyworks
41
+ name: chat
42
+ type: chat
43
+ extra: {}
44
+ load_path:
45
+ - lib
46
+ revision: 0
47
+ created: '2010-07-01'
48
+ summary: Shomen Documentation via RDoc
49
+ title: Shomen RDoc
50
+ version: 0.1.0
51
+ name: shomen-rdoc
52
+ description: ! 'Shomen RDoc provides a command line utility for generating Shomen
53
+ standard
54
+
55
+ documentation via the RDoc''s `.rdoc` cache. It is an alternative to the
56
+
57
+ `rdoc-shomen` format plugin.'
58
+ organization: rubyworks
59
+ date: '2012-04-19'
@@ -0,0 +1,151 @@
1
+ module Shomen
2
+
3
+ module Rdoc
4
+
5
+ # TODO: Instead of using command line options for shomen
6
+ # we could use environment variables. ?
7
+
8
+ # The Shomen RDoc command line tool provides a utility to generate
9
+ # a Shomen documentation file using RDoc's .rdoc cache.
10
+ #
11
+ # Examples:
12
+ #
13
+ # $ shomen-rdoc --readme README.md lib - [A-Z]*.*
14
+ #
15
+ class Command
16
+ require 'optparse'
17
+
18
+ # Public: Shortcut for `CLI.new(*argv).run`.
19
+ #
20
+ # Returns nothing.
21
+ def self.run(*argv)
22
+ new(*argv).run
23
+ end
24
+
25
+ # Command line options.
26
+ attr :options
27
+
28
+ # Initialize new command.
29
+ #
30
+ # argv - Command line arguments. [Array]
31
+ #
32
+ # Returns CLI instance.
33
+ def initialize(*argv)
34
+ @options = {}
35
+
36
+ parse(argv)
37
+ end
38
+
39
+ # Public: Run command.
40
+ #
41
+ # Returns nothing.
42
+ def run
43
+ generator = Generator.new(options)
44
+ $stdout.puts generator
45
+ end
46
+
47
+ # Parse command line arguments.
48
+ #
49
+ # argv - List of command line arguments. [Array]
50
+ #
51
+ # Returns list of arguments. [Array]
52
+ #
53
+ def parse(argv)
54
+ if i = argv.index('-')
55
+ options[:documents] = argv[i+1..-1]
56
+ argv = argv[0...i]
57
+ end
58
+
59
+ parser = OptionParser.new
60
+ parser_options(parser)
61
+ parser.parse!(argv)
62
+
63
+ if !(force? or root?)
64
+ $stderr.puts "ERROR: Not a project directory. Use --force to override."
65
+ exit -1
66
+ end
67
+
68
+ options[:scripts] = argv
69
+ end
70
+
71
+ # Define command line options.
72
+ #
73
+ # parser - Instance of {OptionParser}.
74
+ #
75
+ # Returns nothing.
76
+ def parser_options(parser)
77
+ parser.on('-j', '--json', 'output JSON instead of YAML (default)') do
78
+ options[:format] = :json
79
+ end
80
+ parser.on('-y', '--yaml', 'output YAML instead of JSON') do
81
+ options[:format] = :yaml
82
+ end
83
+
84
+ parser.on('-d', '--db DIR', 'documentation store directory (deafult is `.rdoc` or `.yardoc`)') do |dir|
85
+ options[:store] = dir
86
+ end
87
+ parser.on('-c', '--use-cache', 'do not regenerate docs, use pre-existing cache') do
88
+ options[:use_cache] = true
89
+ end
90
+
91
+ parser.on('-s', '--source', 'include full source in script documentation') do
92
+ options[:source] = true
93
+ end
94
+ parser.on('-w', '--webcvs URI', 'prefix link to source code') do |uri|
95
+ options[:webcvs] = uri
96
+ end
97
+ parser.on('-r', '--readme FILE', 'which file to use as main') do |file|
98
+ options[:readme] = file
99
+ end
100
+
101
+ #parser.on('--save', 'save options for future use') do |markup|
102
+ # options[:save] = true
103
+ #end
104
+
105
+ # TODO: shouldn't this be in .yardopts?
106
+ parser.on('--markup TYPE', 'markup type used for comments (rdoc, md, tomdoc)') do |markup|
107
+ options[:markup] = markup.to_sym
108
+ end
109
+
110
+ parser.on('-F', '--force') do
111
+ $FORCE = true
112
+ end
113
+
114
+ parser.on_tail('--debug', 'run with $DEBUG set to true') do
115
+ $DEBUG = true
116
+ end
117
+ parser.on_tail('--warn', 'run with $VERBOSE set to true') do
118
+ $VERBOSE = true
119
+ end
120
+
121
+ parser.on_tail('--help', 'see this help message') do
122
+ puts parser; exit -1
123
+ end
124
+ end
125
+
126
+ # Is `$FORCE` set?
127
+ #
128
+ # Returns true or false.
129
+ def force?
130
+ !!$FORCE
131
+ end
132
+
133
+ # Is this a project directory?
134
+ #
135
+ # Returns true or false.
136
+ def root?
137
+ root = false
138
+ root = true if File.exist?('.ruby')
139
+ root = true if File.exist?('.yardoc')
140
+ root = true if File.exist?('.rdoc')
141
+ root = true if File.exist?('.git')
142
+ root = true if File.exist?('.hg')
143
+ root = true if File.exist?('_darcs')
144
+ root
145
+ end
146
+
147
+ end
148
+
149
+ end
150
+
151
+ end
@@ -0,0 +1,900 @@
1
+ # encoding: UTF-8
2
+
3
+ module Shomen
4
+
5
+ module Rdoc
6
+
7
+ # TODO: what about reading options from .document and .rdoc_options ?
8
+ # also rdoc.options[:document] ?
9
+
10
+ # This adapter is used to convert RDoc's documentation extracted
11
+ # from a local store (`.rdoc`) to Shomen's pure-data format.
12
+ #
13
+ # There's a bit of a limitation with adding scripts to the Shomen
14
+ # table, as it appears rdoc only keeps track of script files for methods.
15
+ # So any file thet doesn't contain at least one method definition won't
16
+ # show up. We'll see if we can fix this in a future version.
17
+ #
18
+ # In addition, documentation files are not tracked at all, so they have
19
+ # to provided on the command line regardless --though by default any
20
+ # README file will be included.
21
+ #
22
+ # WARNING: RDoc's RI::Store has some issues and presently some information
23
+ # is not accessible that otherwise would be included. B/c of this we recommend
24
+ # using the traditional `rdoc-shomen` generator instead until these issues
25
+ # are resolved.
26
+ #
27
+ class Generator < Shomen::Generator
28
+
29
+ # Initialize new RDoc adaptor.
30
+ def initialize(options)
31
+ initialize_rdoc
32
+
33
+ @store = '.rdoc'
34
+
35
+ super(options)
36
+ end
37
+
38
+ # Load RDoc library. Must be RDoc v3 or greater. We invoke the `gem` method
39
+ # in this method in order to ensure we are not using the rdoc library included
40
+ # with the Ruby distribution, which is out of date.
41
+ #
42
+ # Returns nothing.
43
+ def initialize_rdoc
44
+ gem 'rdoc', '>3' # rescue nil
45
+ require 'rdoc'
46
+ require 'shomen-rdoc/rdoc_ext'
47
+ end
48
+
49
+ # Location to of RDoc documentation cache. This defaults to `.rdoc` which is
50
+ # where RDoc normally places it's generated documentation files.
51
+ #
52
+ # Returns String path to RDoc documentation cache.
53
+ attr_accessor :store
54
+
55
+ # Use pre-existant cache instead of regenerating documentation.
56
+ attr_accessor :use_cache
57
+
58
+ # Use pre-existant cache instead of regenerating documentation.
59
+ #
60
+ # Returns true/false.
61
+ def use_cache?
62
+ @use_cache
63
+ end
64
+
65
+ # Files to be documented.
66
+ #
67
+ # Returns Array of file paths.
68
+ def files
69
+ @files ||= (
70
+ list = []
71
+ list.concat scripts
72
+ list.concat documents
73
+ list.concat(['README*']) if list.empty? #['lib', 'README*'] ?
74
+ list
75
+ )
76
+ end
77
+
78
+ # Generate with RDoc as backend processor.
79
+ #
80
+ # Returns documentation table. [Hash]
81
+ def generate
82
+ preconfigure unless use_cache?
83
+ generate_table
84
+ end
85
+
86
+ # The hash object that is used to store the generated
87
+ # documentation.
88
+ #
89
+ # Returns documentation table. [Hash]
90
+ def table
91
+ @table
92
+ end
93
+
94
+ private
95
+
96
+ # Produce RDoc cache.
97
+ #
98
+ # Returns nothing.
99
+ def preconfigure
100
+ argv = []
101
+ argv.concat ["-D"] if $DEBUG
102
+ argv.concat ["-q"]
103
+ argv.concat ["-r"]
104
+ argv.concat ["-o", store]
105
+ argv.concat ["--markup", markup] if markup
106
+ #argv.concat ["--write-options"] #if save
107
+ argv.concat scripts
108
+ #argv.concat ['-', *documents] unless documents.empty?
109
+
110
+ rdoc = ::RDoc::RDoc.new
111
+ $stderr.puts('rdoc ' + argv.join(' ')) if $DEBUG
112
+
113
+ rdoc.document(argv)
114
+ end
115
+
116
+ # Generate documentation table from RDoc.
117
+ #
118
+ # Returns documentation table. [Hash]
119
+ def generate_table
120
+ #options = {}
121
+ #options[:files] = documents # + scripts
122
+ #options[:store] = store
123
+ #options[:webcvs] = webcvs
124
+ #options[:source] = source
125
+
126
+ #rdoc = Shomen::RDocAdaptor.new(options)
127
+ #rdoc.generate
128
+
129
+ if not File.exist?(store)
130
+ $stderr.puts "ERROR: RDoc store not found -- '#{store}`."
131
+ exit -1
132
+ end
133
+
134
+ @table = {}
135
+
136
+ constants = []
137
+ scripts = []
138
+
139
+ db = ::RDoc::RI::Store.new(store)
140
+ db.load_cache
141
+
142
+ generate_metadata
143
+
144
+ debug_msg "Generating class/module documentation:"
145
+ db.modules.each do |name|
146
+ object = db.load_class(name)
147
+ constants.concat(object.constants)
148
+ generate_class(object)
149
+ end
150
+
151
+ debug_msg "Generating class method documentation:"
152
+ db.class_methods.each do |module_name, methods|
153
+ methods.each do |name|
154
+ object = db.load_method(module_name, name)
155
+ scripts.push(object.file)
156
+ generate_method(object)
157
+ end
158
+ end
159
+
160
+ debug_msg "Generating instance method documentation:"
161
+ db.instance_methods.each do |module_name, methods|
162
+ methods.each do |name|
163
+ object = db.load_method(module_name, "##{name}")
164
+ scripts.push(object.file)
165
+ generate_method(object)
166
+ end
167
+ end
168
+
169
+ #debug_msg "Generating attribute method documentation:"
170
+ #db.attributes.each do |module_name, methods|
171
+ # methods.each do |name|
172
+ # object = db.load_method(module_name, "##{name}")
173
+ # generate_method(object)
174
+ # end
175
+ #end
176
+
177
+ debug_msg "Generating constant documentation:"
178
+ constants.each do |object|
179
+ generate_constant(object)
180
+ end
181
+
182
+ debug_msg "Generating file documentation:"
183
+ # TODO: Are c/c++ sourse files working okay?
184
+ # TODO: Add a generator for non-ruby script (e.g. .js)?
185
+ collect_files.each do |file|
186
+ case File.extname(file)
187
+ when '.rb', '.rbx', '.c', '.cpp'
188
+ generate_script(file)
189
+ when '.rdoc', '.md', '.markdown', '.txt'
190
+ generate_document(file)
191
+ else
192
+ generate_document(file)
193
+ end
194
+ end
195
+
196
+ debug_msg "Generating script documentation:"
197
+ scripts.each do |object|
198
+ generate_script(object)
199
+ end
200
+
201
+ return @table
202
+ end
203
+
204
+ # Collect files given list of +globs+.
205
+ #
206
+ # Returns Array of files.
207
+ def collect_files
208
+ globs = self.files
209
+ globs = globs.map{ |glob| Dir[glob] }.flatten.uniq
210
+ globs = globs.map do |glob|
211
+ if File.directory?(glob)
212
+ Dir[File.join(glob, '**/*')]
213
+ else
214
+ glob
215
+ end
216
+ end
217
+ list = globs.flatten.uniq.compact
218
+ list = list.reject{ |path| File.extname(path) == '.html' }
219
+ list = list.select{ |path| File.file?(path) }
220
+ list
221
+ end
222
+
223
+ # Generate project metadata entry.
224
+ #
225
+ # Returns Hash of metadata, as added to the documentation table
226
+ def generate_metadata
227
+ #project_metadata = Metadata.new
228
+ @table['(metadata)'] = project_metadata.to_h
229
+ end
230
+
231
+ # Add constant to table.
232
+ #
233
+ # rdoc_constant - RDoc constant documentation object.
234
+ #
235
+ # Returns Hash for constant documentation entry.
236
+ def generate_constant(rdoc_constant)
237
+ debug_msg " #{rdoc_constant.name}"
238
+
239
+ model = Shomen::Model::Constant.new
240
+
241
+ model.path = rdoc_constant.parent.full_name + '::' + rdoc_constant.name
242
+ model.name = rdoc_constant.name
243
+ model.namespace = rdoc_constant.parent.full_name
244
+ model.comment = comment(rdoc_constant.comment)
245
+ model.format = 'rdoc' # or tomdoc ?
246
+ model.value = rdoc_constant.value
247
+ model.files = ["/#{rdoc_constant.file.full_name}"]
248
+
249
+ @table[model.path] = model.to_h
250
+ end
251
+
252
+ # Add classes (and modules) to table.
253
+ #
254
+ # rdoc_class - RDoc class documentation object.
255
+ #
256
+ # Returns Hash of class or module documentation entry.
257
+ def generate_class(rdoc_class)
258
+ debug_msg " %s" % [ rdoc_class.full_name ]
259
+
260
+ if rdoc_class.type=='class'
261
+ model = Shomen::Model::Class.new
262
+ else
263
+ model = Shomen::Model::Module.new
264
+ end
265
+
266
+ modules = (rdoc_class.modules_hash || {}).values
267
+ classes = (rdoc_class.classes_hash || {}).values
268
+
269
+ model.path = rdoc_class.full_name
270
+ model.name = rdoc_class.name
271
+ model.namespace = rdoc_class.full_name.split('::')[0...-1].join('::')
272
+ model.includes = rdoc_class.includes.map{ |x| x.name } # FIXME: How to "lookup" full name?
273
+ model.extensions = [] # TODO: How to get extension modules?
274
+ model.comment = comment(rdoc_class.comment)
275
+ model.format = 'rdoc' # or tomdoc ?
276
+ model.constants = rdoc_class.constants.map{ |x| complete_name(x.name, rdoc_class.full_name) }
277
+
278
+ model.modules = modules.map{ |x| complete_name(x.name, rdoc_class.full_name) }
279
+ model.classes = classes.map{ |x| complete_name(x.name, rdoc_class.full_name) }
280
+
281
+ model.methods = rdoc_class.method_list.map{ |m| method_name(m) }.uniq
282
+ model.accessors = rdoc_class.attributes.map{ |a| method_name(a) }.uniq #+ ":#{a.rw}" }.uniq
283
+
284
+ model.files = (rdoc_class.in_files || []).map{ |x| "/#{x.full_name}" }
285
+
286
+ if rdoc_class.file
287
+ model.files.unshift("/#{rdoc_class.file.full_name}")
288
+ end
289
+
290
+ if rdoc_class.type == 'class'
291
+ # HACK: No idea why RDoc is returning some weird superclass:
292
+ # <RDoc::NormalClass:0xd924d4 class Object < BasicObject includes: []
293
+ # attributes: [] methods: [#<RDoc::AnyMethod:0xd92b8c Object#fileutils
294
+ # (public)>] aliases: []>
295
+ # Maybe it has something to do with #fileutils?
296
+ model.superclass = (
297
+ case rdoc_class.superclass
298
+ when nil
299
+ when String
300
+ rdoc_class.superclass
301
+ else
302
+ rdoc_class.superclass.full_name
303
+ end
304
+ )
305
+ end
306
+
307
+ @table[model.path] = model.to_h
308
+ end
309
+
310
+ # TODO: How to get literal interface separate from call-sequences?
311
+
312
+ # Transform RDoc method to Shomen model and add to table.
313
+ #
314
+ # rdoc_method - RDoc method documentation object.
315
+ #
316
+ # Returns Hash of method documentation entry.
317
+ def generate_method(rdoc_method)
318
+ #list = methods_all + attributes_all
319
+
320
+ #debug_msg "%s" % [rdoc_method.full_name]
321
+
322
+ #full_name = method_name(m)
323
+ #'prettyname' => m.pretty_name,
324
+ #'type' => m.type, # class or instance
325
+
326
+ model = Shomen::Model::Method.new
327
+
328
+ model.path = method_name(rdoc_method)
329
+ model.name = rdoc_method.name
330
+ model.namespace = rdoc_method.parent_name
331
+ model.comment = comment(rdoc_method.comment)
332
+ model.format = 'rdoc' # or tomdoc ?
333
+ model.aliases = (rdoc_method.aliases || []).map{ |a| method_name(a) }
334
+ model.alias_for = method_name(rdoc_method.is_alias_for)
335
+ model.singleton = rdoc_method.singleton
336
+
337
+ model.declarations << rdoc_method.type.to_s #singleton ? 'class' : 'instance'
338
+ model.declarations << rdoc_method.visibility.to_s
339
+
340
+ model.interfaces = []
341
+ if rdoc_method.call_seq
342
+ rdoc_method.call_seq.split("\n").each do |cs|
343
+ cs = cs.to_s.strip
344
+ model.interfaces << parse_interface(cs) unless cs == ''
345
+ end
346
+ end
347
+ model.interfaces << parse_interface("#{rdoc_method.name}#{rdoc_method.params}")
348
+
349
+ model.returns = [] # RDoc doesn't support specifying return values
350
+ model.file = '/' + rdoc_method.file_name
351
+ model.line = rdoc_method.line.to_i # FIXME: why is this always zero?
352
+ model.source = rdoc_method.source_code_raw
353
+
354
+ if rdoc_method.respond_to?(:c_function)
355
+ model.language = rdoc_method.c_function ? 'c' : 'ruby'
356
+ else
357
+ model.language = 'ruby'
358
+ end
359
+
360
+ @table[model.path] = model.to_h
361
+ end
362
+
363
+ # TODO: remove any trailing comment from interface
364
+
365
+ # Parse method interface.
366
+ #
367
+ # interface - String representation of method interface.
368
+ #
369
+ # Returns Hash entry of method interface.
370
+ def parse_interface(interface)
371
+ args, block = [], {}
372
+
373
+ interface, returns = interface.split(/[=-]\>/)
374
+ interface = interface.strip
375
+ if i = interface.index(/\)\s*\{/)
376
+ block['image'] = interface[i+1..-1].strip
377
+ interface = interface[0..i].strip
378
+ end
379
+
380
+ arguments = interface.strip.sub(/^.*?\(/,'').chomp(')')
381
+ arguments = arguments.split(/\s*\,\s*/)
382
+ arguments.each do |a|
383
+ if a.start_with?('&')
384
+ block['name'] = a
385
+ else
386
+ n,v = a.split('=')
387
+ args << (v ? {'name'=>n,'default'=>v} : {'name'=>n})
388
+ end
389
+ end
390
+
391
+ result = {}
392
+ result['signature'] = interface
393
+ result['arguments'] = args
394
+ result['block'] = block unless block.empty?
395
+ result['returns'] = returns.strip if returns
396
+ return result
397
+ end
398
+
399
+ # Generate entries for information files, e.g. `README.rdoc`.
400
+ #
401
+ # rdoc_document - RDoc file documentation object.
402
+ #
403
+ # Returns Hash of document entry.
404
+ def generate_document(rdoc_document)
405
+ relative_path = (String === rdoc_document ? rdoc_document : rdoc_document.full_name)
406
+ absolute_path = File.join(path_base, relative_path)
407
+
408
+ model = Shomen::Model::Document.new
409
+
410
+ model.path = relative_path
411
+ model.name = File.basename(absolute_path)
412
+ model.mtime = File.mtime(absolute_path)
413
+ model.text = File.read(absolute_path) #file.comment
414
+ model.format = mime_type(absolute_path)
415
+
416
+ @table['/'+model.path] = model.to_h
417
+ end
418
+
419
+ # TODO: Add loadpath and make file path relative to it?
420
+
421
+ # Generate script entries.
422
+ #
423
+ # rdoc_file - RDoc file documentation object.
424
+ #
425
+ # Returns Hash of script entry.
426
+ def generate_script(rdoc_file)
427
+ #debug_msg "Generating file documentation in #{path_output_relative}:"
428
+ #templatefile = self.path_template + 'file.rhtml'
429
+
430
+ file = case rdoc_file
431
+ when String
432
+ rdoc_file
433
+ else
434
+ rdoc_file.full_name
435
+ end
436
+
437
+ debug_msg "%s" % [file]
438
+
439
+ absolute_path = File.join(path_base, file)
440
+ #rel_prefix = self.path_output.relative_path_from(outfile.dirname)
441
+
442
+ model = Shomen::Model::Script.new
443
+
444
+ model.path = file
445
+ model.name = File.basename(file)
446
+ model.mtime = File.mtime(absolute_path)
447
+
448
+ if source?
449
+ model.source = File.read(absolute_path)
450
+ model.language = mime_type(absolute_path)
451
+ end
452
+
453
+ webcvs = project_metadata['webcvs'] || webcvs
454
+ if webcvs
455
+ model.uri = File.join(webcvs, model.path)
456
+ model.language = mime_type(absolute_path)
457
+ end
458
+
459
+ # TODO: what use is rdoc_file.comment ?
460
+ #model.header =
461
+ #model.footer =
462
+
463
+ unless String === rdoc_file
464
+ model.requires = rdoc_file.requires.map{ |r| r.name }
465
+ model.constants = rdoc_file.constants.map{ |c| c.full_name }
466
+ end
467
+
468
+ # note that this utilizes the table we are building
469
+ # so it needs to be the last thing done.
470
+ @table.each do |k, h|
471
+ case h['!']
472
+ when 'module'
473
+ model.modules ||= []
474
+ model.modules << k if h['files'].include?(file)
475
+ when 'class'
476
+ model.classes ||= []
477
+ model.classes << k if h['files'].include?(file)
478
+ when 'method'
479
+ model.methods ||= []
480
+ model.methods << k if h['file'] == file
481
+ when 'class-method'
482
+ model.class_methods ||= []
483
+ model.class_methods << k if h['file'] == file
484
+ end
485
+ end
486
+
487
+ @table['/'+model.path] = model.to_h
488
+ end
489
+
490
+ # Get fully qualified name given +name+ and +namespace+.
491
+ #
492
+ # name - String of name.
493
+ # namespace - String of namespace.
494
+ #
495
+ # Returns String of fully qualified name.
496
+ def complete_name(name, namespace)
497
+ if name !~ /^#{namespace}/
498
+ "#{namespace}::#{name}"
499
+ else
500
+ name
501
+ end
502
+ end
503
+
504
+ # Get full method name.
505
+ #
506
+ # method - Method instance.
507
+ #
508
+ # Returns String of methods full name.
509
+ def method_name(method)
510
+ return nil if method.nil?
511
+ if method.singleton
512
+ i = method.full_name.rindex('::')
513
+ method.full_name[0...i] + '.' + method.full_name[i+2..-1]
514
+ else
515
+ method.full_name
516
+ end
517
+ end
518
+
519
+ # Convert rdoc object comment into RDoc text.
520
+ #
521
+ # document - RDoc document object.
522
+ #
523
+ # Returns String of comment text.
524
+ def comment(document)
525
+ formatter = RDoc::Markup::ToRdoc.new
526
+ text = document.accept(formatter)
527
+ text.strip
528
+ end
529
+
530
+ # Determine mime-type by file extension. If a type can not be determined,
531
+ # then returns `text/plain` type.
532
+ #
533
+ # path - String file path.
534
+ #
535
+ # Returns String of mime-type.
536
+ def mime_type(path)
537
+ case File.extname(path)
538
+ when '.rb', '.rbx' then 'text/ruby'
539
+ when '.c' then 'text/c-source'
540
+ when '.rdoc' then 'text/rdoc'
541
+ when '.md', '.markdown' then 'text/markdown'
542
+ else 'text/plain'
543
+ end
544
+ end
545
+
546
+ # Output progress information if rdoc debugging is enabled
547
+ #
548
+ # msg - String debug message.
549
+ #
550
+ # Returns nothing.
551
+ def debug_msg(msg)
552
+ return unless $DEBUG_RDOC
553
+ case msg[-1,1]
554
+ when '.' then tab = "= "
555
+ when ':' then tab = "== "
556
+ else tab = "* "
557
+ end
558
+ $stderr.puts(tab + msg)
559
+ end
560
+
561
+ # Current working directory.
562
+ #
563
+ # Returns String of working directory.
564
+ def path_base
565
+ Dir.pwd
566
+ end
567
+
568
+ end
569
+
570
+ end
571
+
572
+ end
573
+
574
+
575
+
576
+
577
+
578
+
579
+ =begin
580
+
581
+ require 'fileutils'
582
+ require 'pathname'
583
+ require 'yaml'
584
+ require 'json'
585
+
586
+ require 'rdoc/rdoc'
587
+ require 'rdoc/generator'
588
+ require 'rdoc/generator/markup'
589
+
590
+ require 'shomen/metadata'
591
+ require 'shomen/model' # TODO: have metadata in model
592
+ require 'shomen/rdoc/extensions'
593
+
594
+ # Shomen Adaptor for RDoc utilizes the rdoc tool to parse ruby source code
595
+ # to build a Shomen documenation file.
596
+ #
597
+ # RDoc is almost entirely a free-form documentation system, so it is not
598
+ # possible for Shomen to fully harness all the details it can support from
599
+ # the RDoc documentation, such as method argument descriptions.
600
+
601
+ class RDoc::Generator::Shomen
602
+
603
+ # Register shomen generator with RDoc.
604
+ RDoc::RDoc.add_generator(self)
605
+
606
+ #include RDocShomen::Metadata
607
+
608
+ # Standard generator factory method.
609
+ def self.for(options)
610
+ new(options)
611
+ end
612
+
613
+ # User options from the command line.
614
+ attr :options
615
+
616
+ # List of all classes and modules.
617
+ #def all_classes_and_modules
618
+ # @all_classes_and_modules ||= RDoc::TopLevel.all_classes_and_modules
619
+ #end
620
+
621
+ # In the world of the RDoc Generators #classes is the same
622
+ # as #all_classes_and_modules. Well, except that its sorted
623
+ # too. For classes sans modules, see #types.
624
+
625
+ def classes
626
+ @classes ||= RDoc::TopLevel.all_classes_and_modules.sort
627
+ end
628
+
629
+ # Only toplevel classes and modules.
630
+ def classes_toplevel
631
+ @classes_toplevel ||= classes.select {|klass| !(RDoc::ClassModule === klass.parent) }
632
+ end
633
+
634
+ #
635
+ def files
636
+ @files ||= (
637
+ @files_rdoc.select{ |f| f.parser != RDoc::Parser::Simple }
638
+ )
639
+ end
640
+
641
+ # List of toplevel files. RDoc supplies this via the #generate method.
642
+ def files_toplevel
643
+ @files_toplevel ||= (
644
+ @files_rdoc.select{ |f| f.parser == RDoc::Parser::Simple }
645
+ )
646
+ end
647
+
648
+ #
649
+
650
+ def files_hash
651
+ @files ||= RDoc::TopLevel.files_hash
652
+ end
653
+
654
+ # List of all methods in all classes and modules.
655
+ def methods_all
656
+ @methods_all ||= classes.map{ |m| m.method_list }.flatten.sort
657
+ end
658
+
659
+ # List of all attributes in all classes and modules.
660
+ def attributes_all
661
+ @attributes_all ||= classes.map{ |m| m.attributes }.flatten.sort
662
+ end
663
+
664
+ #
665
+ def constants_all
666
+ @constants_all ||= classes.map{ |c| c.constants }.flatten
667
+ end
668
+
669
+ ## TODO: What's this then?
670
+ ##def json_creatable?
671
+ ## RDoc::TopLevel.json_creatable?
672
+ ##end
673
+
674
+ # RDoc needs this to function.
675
+ def class_dir ; nil ; end
676
+
677
+ # RDoc needs this to function.
678
+ def file_dir ; nil ; end
679
+
680
+ # TODO: Rename ?
681
+ def shomen
682
+ @table || {}
683
+ end
684
+
685
+ # Build the initial indices and output objects
686
+ # based on an array of top level objects containing
687
+ # the extracted information.
688
+ def generate(files)
689
+ @files_rdoc = files.sort
690
+
691
+ @table = {}
692
+
693
+ generate_metadata
694
+ generate_constants
695
+ generate_classes
696
+ #generate_attributes
697
+ generate_methods
698
+ generate_documents
699
+ generate_scripts # must be last b/c it depends on the others
700
+
701
+ # TODO: method accessor fields need to be handled
702
+
703
+ # THINK: Internal referencing model, YAML and JSYNC ?
704
+ #ref_table = reference_table(@table)
705
+
706
+ #rescue StandardError => err
707
+ # debug_msg "%s: %s\n %s" % [ err.class.name, err.message, err.backtrace.join("\n ") ]
708
+ # raise err
709
+ end
710
+
711
+
712
+ protected
713
+
714
+ #
715
+ def initialize(options)
716
+ @options = options
717
+ #@options.diagram = false # why?
718
+
719
+ @path_base = Pathname.pwd.expand_path
720
+
721
+ # TODO: This is probably not needed any more.
722
+ @path_output = Pathname.new(@options.op_dir).expand_path(@path_base)
723
+ end
724
+
725
+ # Current pathname.
726
+ attr :path_base
727
+
728
+ # The output path.
729
+ attr :path_output
730
+
731
+ #
732
+ def path_output_relative(path=nil)
733
+ if path
734
+ path.to_s.sub(path_base.to_s+'/', '')
735
+ else
736
+ @path_output_relative ||= path_output.to_s.sub(path_base.to_s+'/', '')
737
+ end
738
+ end
739
+
740
+ #
741
+ def collect_methods(class_module, singleton=false)
742
+ list = []
743
+ class_module.method_list.each do |m|
744
+ next if singleton ^ m.singleton
745
+ list << method_name(m)
746
+ end
747
+ list.uniq
748
+ end
749
+
750
+ #
751
+ def collect_attributes(class_module, singleton=false)
752
+ list = []
753
+ class_module.attributes.each do |a|
754
+ next if singleton ^ a.singleton
755
+ #p a.rw
756
+ #case a.rw
757
+ #when :write, 'W'
758
+ # list << "#{method_name(a)}="
759
+ #else
760
+ list << method_name(a)
761
+ #end
762
+ end
763
+ list.uniq
764
+ end
765
+
766
+ =end
767
+
768
+
769
+
770
+ #--
771
+ =begin
772
+ #
773
+ def generate_attributes
774
+ #$stderr.puts "HERE!"
775
+ #$stderr.puts attributes_all.inspect
776
+ #exit
777
+ debug_msg "Generating attributes documentation:"
778
+ attributes_all.each do |rdoc_attribute|
779
+ debug_msg "%s" % [rdoc_attribute.full_name]
780
+
781
+ adapter = Shomen::RDoc::MethodAdapter.new(rdoc_attribute)
782
+ data = Shomen::Model::Method.new(adapter).to_h
783
+
784
+ @table[data['path']] = data
785
+
786
+ #code = m.source_code_raw
787
+ #file, line = m.source_code_location
788
+
789
+ #full_name = method_name(m)
790
+
791
+ #'prettyname' => m.pretty_name,
792
+ #'type' => m.type, # class or instance
793
+
794
+ #model_class = m.singleton ? Shomen::Model::Function : Shomen::Model::Method
795
+ #model_class = Shomen::Model::Attribute
796
+
797
+ #@table[full_name] = model_class.new(
798
+ # 'path' => full_name,
799
+ # 'name' => m.name,
800
+ # 'namespace' => m.parent_name,
801
+ # 'comment' => m.comment.text,
802
+ # 'access' => m.visibility.to_s,
803
+ # 'rw' => m.rw, # TODO: better name ?
804
+ # 'singleton' => m.singleton,
805
+ # 'aliases' => m.aliases.map{ |a| method_name(a) },
806
+ # 'alias_for' => method_name(m.is_alias_for),
807
+ # 'image' => m.params,
808
+ # 'arguments' => [],
809
+ # 'parameters' => [],
810
+ # 'block' => m.block_params, # TODO: what is block?
811
+ # 'interface' => m.arglists,
812
+ # 'returns' => [],
813
+ # 'file' => file,
814
+ # 'line' => line,
815
+ # 'source' => code
816
+ #).to_h
817
+ end
818
+ end
819
+ =end
820
+ #++
821
+
822
+
823
+ #--
824
+ =begin
825
+ #
826
+ # N O T U S E D
827
+ #
828
+
829
+ # Sort based on how often the top level namespace occurs, and then on the
830
+ # name of the module -- this works for projects that put their stuff into
831
+ # a namespace, of course, but doesn't hurt if they don't.
832
+ def sort_salient(classes)
833
+ nscounts = classes.inject({}) do |counthash, klass|
834
+ top_level = klass.full_name.gsub( /::.*/, '' )
835
+ counthash[top_level] ||= 0
836
+ counthash[top_level] += 1
837
+ counthash
838
+ endfiles_toplevel
839
+ classes.sort_by{ |klass|
840
+ top_level = klass.full_name.gsub( /::.*/, '' )
841
+ [nscounts[top_level] * -1, klass.full_name]
842
+ }.select{ |klass|
843
+ klass.document_self
844
+ }
845
+ end
846
+ =end
847
+
848
+ =begin
849
+ # Loop through table and convert all named references into bonofied object
850
+ # references.
851
+ def reference_table(table)
852
+ debug_msg "== Generating Reference Table"
853
+ new_table = {}
854
+ table.each do |key, entry|
855
+ debug_msg "%s" % [key]
856
+ data = entry.dup
857
+ new_table[key] = data
858
+ case data['!']
859
+ when 'script'
860
+ data["constants"] = ref_list(data["constants"])
861
+ data["modules"] = ref_list(data["modules"])
862
+ data["classes"] = ref_list(data["classes"])
863
+ data["functions"] = ref_list(data["functions"])
864
+ data["methods"] = ref_list(data["methods"])
865
+ when 'file'
866
+ when 'constant'
867
+ data["namespace"] = ref_item(data["namespace"])
868
+ when 'module', 'class'
869
+ data["namespace"] = ref_item(data["namespace"])
870
+ data["includes"] = ref_list(data["includes"])
871
+ #data["extended"] = ref_list(data["extended"])
872
+ data["constants"] = ref_list(data["constants"])
873
+ data["modules"] = ref_list(data["modules"])
874
+ data["classes"] = ref_list(data["classes"])
875
+ data["functions"] = ref_list(data["functions"])
876
+ data["methods"] = ref_list(data["methods"])
877
+ data["files"] = ref_list(data["files"])
878
+ data["superclass"] = ref_item(data["superclass"]) if data.key?("superclass")
879
+ when 'method', 'function'
880
+ data["namespace"] = ref_item(data["namespace"])
881
+ data["file"] = ref_item(data["file"])
882
+ end
883
+ end
884
+ new_table
885
+ end
886
+
887
+ # Given a key, return the matching table item. If not found return the key.
888
+ def ref_item(key)
889
+ @table[key] || key
890
+ end
891
+
892
+ # Given a list of keys, return the matching table items.
893
+ def ref_list(keys)
894
+ #keys.map{ |k| @table[k] || k }
895
+ keys.map{ |k| @table[k] || nil }.compact
896
+ end
897
+
898
+ =end
899
+ #++
900
+