shomen-rdoc 0.1.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.
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
+