rdoc 2.5.11 → 3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rdoc might be problematic. Click here for more details.

Files changed (113) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.document +1 -0
  3. data/History.txt +95 -0
  4. data/Manifest.txt +13 -4
  5. data/README.txt +9 -3
  6. data/Rakefile +1 -1
  7. data/lib/rdoc.rb +15 -298
  8. data/lib/rdoc/alias.rb +65 -16
  9. data/lib/rdoc/any_method.rb +27 -150
  10. data/lib/rdoc/attr.rb +36 -115
  11. data/lib/rdoc/class_module.rb +236 -22
  12. data/lib/rdoc/code_object.rb +76 -31
  13. data/lib/rdoc/constant.rb +32 -4
  14. data/lib/rdoc/context.rb +494 -222
  15. data/lib/rdoc/encoding.rb +79 -0
  16. data/lib/rdoc/erbio.rb +37 -0
  17. data/lib/rdoc/gauntlet.rb +9 -5
  18. data/lib/rdoc/generator.rb +33 -1
  19. data/lib/rdoc/generator/darkfish.rb +284 -375
  20. data/lib/rdoc/generator/markup.rb +72 -36
  21. data/lib/rdoc/generator/ri.rb +4 -4
  22. data/lib/rdoc/generator/template/darkfish/classpage.rhtml +267 -274
  23. data/lib/rdoc/generator/template/darkfish/filepage.rhtml +91 -91
  24. data/lib/rdoc/generator/template/darkfish/index.rhtml +45 -45
  25. data/lib/rdoc/generator/template/darkfish/rdoc.css +298 -298
  26. data/lib/rdoc/include.rb +40 -1
  27. data/lib/rdoc/known_classes.rb +1 -0
  28. data/lib/rdoc/markup.rb +467 -2
  29. data/lib/rdoc/markup/attribute_manager.rb +24 -6
  30. data/lib/rdoc/markup/blank_line.rb +11 -3
  31. data/lib/rdoc/markup/document.rb +6 -0
  32. data/lib/rdoc/markup/formatter.rb +10 -0
  33. data/lib/rdoc/markup/formatter_test_case.rb +339 -3
  34. data/lib/rdoc/markup/heading.rb +3 -0
  35. data/lib/rdoc/markup/inline.rb +11 -1
  36. data/lib/rdoc/markup/list.rb +3 -0
  37. data/lib/rdoc/markup/list_item.rb +3 -0
  38. data/lib/rdoc/markup/paragraph.rb +3 -0
  39. data/lib/rdoc/markup/parser.rb +191 -237
  40. data/lib/rdoc/markup/{preprocess.rb → pre_process.rb} +50 -29
  41. data/lib/rdoc/markup/raw.rb +4 -0
  42. data/lib/rdoc/markup/rule.rb +3 -0
  43. data/lib/rdoc/markup/text_formatter_test_case.rb +116 -0
  44. data/lib/rdoc/markup/to_ansi.rb +14 -2
  45. data/lib/rdoc/markup/to_bs.rb +8 -2
  46. data/lib/rdoc/markup/to_html.rb +84 -91
  47. data/lib/rdoc/markup/to_html_crossref.rb +77 -26
  48. data/lib/rdoc/markup/to_rdoc.rb +94 -49
  49. data/lib/rdoc/markup/to_test.rb +9 -1
  50. data/lib/rdoc/markup/verbatim.rb +6 -3
  51. data/lib/rdoc/method_attr.rb +353 -0
  52. data/lib/rdoc/normal_class.rb +11 -2
  53. data/lib/rdoc/normal_module.rb +0 -5
  54. data/lib/rdoc/options.rb +373 -82
  55. data/lib/rdoc/parser.rb +59 -23
  56. data/lib/rdoc/parser/c.rb +224 -86
  57. data/lib/rdoc/parser/ruby.rb +219 -111
  58. data/lib/rdoc/parser/ruby_tools.rb +4 -1
  59. data/lib/rdoc/parser/simple.rb +9 -4
  60. data/lib/rdoc/rdoc.rb +68 -28
  61. data/lib/rdoc/require.rb +21 -0
  62. data/lib/rdoc/ri/driver.rb +20 -10
  63. data/lib/rdoc/ri/paths.rb +2 -2
  64. data/lib/rdoc/ri/store.rb +22 -5
  65. data/lib/rdoc/ruby_lex.rb +11 -12
  66. data/lib/rdoc/ruby_token.rb +2 -2
  67. data/lib/rdoc/single_class.rb +2 -1
  68. data/lib/rdoc/stats.rb +202 -162
  69. data/lib/rdoc/stats/normal.rb +51 -0
  70. data/lib/rdoc/stats/quiet.rb +59 -0
  71. data/lib/rdoc/stats/verbose.rb +45 -0
  72. data/lib/rdoc/text.rb +133 -4
  73. data/lib/rdoc/{tokenstream.rb → token_stream.rb} +0 -2
  74. data/lib/rdoc/top_level.rb +230 -39
  75. data/test/test_attribute_manager.rb +58 -7
  76. data/test/test_rdoc_alias.rb +13 -0
  77. data/test/test_rdoc_any_method.rb +43 -2
  78. data/test/test_rdoc_attr.rb +15 -8
  79. data/test/test_rdoc_class_module.rb +133 -0
  80. data/test/test_rdoc_code_object.rb +62 -5
  81. data/test/test_rdoc_context.rb +72 -26
  82. data/test/test_rdoc_encoding.rb +145 -0
  83. data/test/test_rdoc_generator_darkfish.rb +119 -0
  84. data/test/test_rdoc_generator_ri.rb +22 -2
  85. data/test/test_rdoc_include.rb +79 -0
  86. data/test/test_rdoc_markup_attribute_manager.rb +4 -4
  87. data/test/test_rdoc_markup_parser.rb +134 -95
  88. data/test/test_rdoc_markup_pre_process.rb +7 -2
  89. data/test/test_rdoc_markup_to_ansi.rb +43 -153
  90. data/test/test_rdoc_markup_to_bs.rb +42 -156
  91. data/test/test_rdoc_markup_to_html.rb +130 -58
  92. data/test/test_rdoc_markup_to_html_crossref.rb +10 -10
  93. data/test/test_rdoc_markup_to_rdoc.rb +40 -151
  94. data/test/test_rdoc_method_attr.rb +122 -0
  95. data/test/test_rdoc_normal_class.rb +1 -1
  96. data/test/test_rdoc_normal_module.rb +6 -1
  97. data/test/test_rdoc_options.rb +237 -12
  98. data/test/test_rdoc_parser.rb +3 -22
  99. data/test/test_rdoc_parser_c.rb +203 -2
  100. data/test/test_rdoc_parser_ruby.rb +403 -89
  101. data/test/test_rdoc_parser_simple.rb +25 -1
  102. data/test/test_rdoc_rdoc.rb +44 -32
  103. data/test/test_rdoc_ri_driver.rb +29 -24
  104. data/test/test_rdoc_ri_store.rb +46 -3
  105. data/test/test_rdoc_task.rb +1 -1
  106. data/test/test_rdoc_text.rb +102 -8
  107. data/test/test_rdoc_top_level.rb +13 -4
  108. data/test/xref_data.rb +8 -0
  109. data/test/xref_test_case.rb +6 -0
  110. metadata +29 -19
  111. metadata.gz.sig +0 -0
  112. data/lib/rdoc/parser/perl.rb +0 -165
  113. data/test/test_rdoc_parser_perl.rb +0 -73
@@ -0,0 +1,79 @@
1
+ require 'rdoc'
2
+
3
+ ##
4
+ # This class is a wrapper around File IO and Encoding that helps RDoc load
5
+ # files and convert them to the correct encoding.
6
+
7
+ module RDoc::Encoding
8
+
9
+ ##
10
+ # Reads the contents of +filename+ and handles any encoding directives in
11
+ # the file.
12
+ #
13
+ # The content will be converted to the +encoding+. If the file cannot be
14
+ # converted a warning will be printed and nil will be returned.
15
+
16
+ def self.read_file filename, encoding
17
+ content = open filename, "rb" do |f| f.read end
18
+
19
+ utf8 = content.sub!(/\A\xef\xbb\xbf/, '')
20
+
21
+ RDoc::Encoding.set_encoding content
22
+
23
+ if Object.const_defined? :Encoding then
24
+ encoding ||= Encoding.default_external
25
+ orig_encoding = content.encoding
26
+
27
+ if utf8 then
28
+ content.force_encoding Encoding::UTF_8
29
+ content.encode! encoding
30
+ else
31
+ # assume the content is in our output encoding
32
+ content.force_encoding encoding
33
+ end
34
+
35
+ unless content.valid_encoding? then
36
+ # revert and try to transcode
37
+ content.force_encoding orig_encoding
38
+ content.encode! encoding
39
+ end
40
+
41
+ unless content.valid_encoding? then
42
+ warn "unable to convert #{filename} to #{encoding}, skipping"
43
+ content = nil
44
+ end
45
+ end
46
+
47
+ content
48
+ rescue ArgumentError => e
49
+ raise unless e.message =~ /unknown encoding name - (.*)/
50
+ warn "unknown encoding name \"#{$1}\" for #{filename}, skipping"
51
+ nil
52
+ rescue Encoding::UndefinedConversionError => e
53
+ warn "unable to convert #{e.message} for #{filename}, skipping"
54
+ nil
55
+ rescue Errno::EISDIR, Errno::ENOENT
56
+ nil
57
+ end
58
+
59
+ ##
60
+ # Sets the encoding of +string+ based on the magic comment
61
+
62
+ def self.set_encoding string
63
+ return unless Object.const_defined? :Encoding
64
+
65
+ first_line = string[/\A(?:#!.*\n)?.*\n/]
66
+
67
+ name = case first_line
68
+ when /^<\?xml[^?]*encoding=(["'])(.*?)\1/ then $2
69
+ when /\b(?:en)?coding[=:]\s*([^\s;]+)/i then $1
70
+ else return
71
+ end
72
+
73
+ enc = Encoding.find name
74
+ string.force_encoding enc if enc
75
+ end
76
+
77
+ end
78
+
79
+
@@ -0,0 +1,37 @@
1
+ require 'erb'
2
+
3
+ ##
4
+ # A subclass of ERB that writes directly to an IO. Credit to Aaron Patterson
5
+ # and Masatoshi SEKI.
6
+ #
7
+ # To use:
8
+ #
9
+ # erbio = RDoc::ERBIO.new '<%= "hello world" %>', nil, nil
10
+ #
11
+ # open 'hello.txt', 'w' do |io|
12
+ # erbio.result binding
13
+ # end
14
+ #
15
+ # Note that binding must enclose the io you wish to output on.
16
+
17
+ class RDoc::ERBIO < ERB
18
+
19
+ ##
20
+ # Defaults +eoutvar+ to 'io', otherwise is identical to ERB's initialize
21
+
22
+ def initialize str, safe_level = nil, trim_mode = nil, eoutvar = 'io'
23
+ super
24
+ end
25
+
26
+ ##
27
+ # Instructs +compiler+ how to write to +io_variable+
28
+
29
+ def set_eoutvar compiler, io_variable
30
+ compiler.put_cmd = "#{io_variable}.write"
31
+ compiler.insert_cmd = "#{io_variable}.write"
32
+ compiler.pre_cmd = []
33
+ compiler.post_cmd = []
34
+ end
35
+
36
+ end
37
+
@@ -7,16 +7,19 @@ require 'fileutils'
7
7
 
8
8
  class RDoc::Gauntlet < Gauntlet
9
9
 
10
+ ##
11
+ # Runs an RDoc generator for gem +name+
12
+
10
13
  def run name
11
- next if self.data[name]
14
+ return if self.data.key? name
12
15
 
13
- ri_dir = File.expand_path "~/.gauntlet/data/ri/#{name}"
14
- FileUtils.rm_rf ri_dir if File.exist? ri_dir
16
+ dir = File.expand_path "~/.gauntlet/data/rdoc/#{name}"
17
+ FileUtils.rm_rf dir if File.exist? dir
15
18
 
16
19
  yaml = File.read 'gemspec'
17
20
  spec = Gem::Specification.from_yaml yaml
18
21
 
19
- args = %W[--ri --op #{ri_dir}]
22
+ args = %W[--ri --op #{dir}]
20
23
  args.push(*spec.rdoc_options)
21
24
  args << spec.require_paths
22
25
  args << spec.extra_rdoc_files
@@ -32,7 +35,8 @@ class RDoc::Gauntlet < Gauntlet
32
35
  r.document args
33
36
  self.data[name] = true
34
37
  puts 'passed'
35
- rescue StandardError, RDoc::Error => e
38
+ FileUtils.rm_rf dir
39
+ rescue Interrupt, StandardError, RDoc::Error, SystemStackError => e
36
40
  puts "failed - (#{e.class}) #{e.message}"
37
41
  self.data[name] = false
38
42
  end
@@ -1,7 +1,39 @@
1
1
  require 'rdoc'
2
2
 
3
3
  ##
4
- # Namespace for generators
4
+ # RDoc uses generators to turn parsed source code in the form of an
5
+ # RDoc::CodeObject tree into some form of output. RDoc comes with the HTML
6
+ # generator RDoc::Generator::Darkfish and an ri data generator
7
+ # RDoc::Generator::RI.
8
+ #
9
+ # = Registering a Generator
10
+ #
11
+ # Generators are registered by calling RDoc::RDoc.add_generator with the class
12
+ # of the generator:
13
+ #
14
+ # class My::Awesome::Generator
15
+ # RDoc::RDoc.add_generator self
16
+ # end
17
+ #
18
+ # = Adding Options to +rdoc+
19
+ #
20
+ # Before option processing in +rdoc+, RDoc::Options will call ::setup_options
21
+ # on the generator class with an RDoc::Options instance. The generator can
22
+ # use RDoc::Options#option_parser to add command-line options to the +rdoc+
23
+ # tool. See OptionParser for details on how to add options.
24
+ #
25
+ # You can extend the RDoc::Options instance with additional accesors for your
26
+ # generator.
27
+ #
28
+ # = Generator Instantiation
29
+ #
30
+ # After parsing, RDoc::RDoc will instantiate a generator by calling
31
+ # #initialize with an RDoc::Options instance.
32
+ #
33
+ # RDoc will then call #generate on the generator instance and pass in an Array
34
+ # of RDoc::TopLevel instances, each representing a parsed file. You can use
35
+ # the various class methods on RDoc::TopLevel and in the RDoc::CodeObject tree
36
+ # to create your desired output format.
5
37
 
6
38
  module RDoc::Generator
7
39
  end
@@ -1,15 +1,12 @@
1
1
  # -*- mode: ruby; ruby-indent-level: 2; tab-width: 2 -*-
2
- # vim: noet ts=2 sts=8 sw=2
3
2
 
4
3
  require 'pathname'
5
4
  require 'fileutils'
6
- require 'erb'
5
+ require 'rdoc/erbio'
7
6
 
8
7
  require 'rdoc/generator/markup'
9
8
 
10
- $DARKFISH_DRYRUN = false # TODO make me non-global
11
-
12
- #
9
+ ##
13
10
  # Darkfish RDoc HTML Generator
14
11
  #
15
12
  # $Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $
@@ -52,402 +49,314 @@ $DARKFISH_DRYRUN = false # TODO make me non-global
52
49
  #
53
50
  class RDoc::Generator::Darkfish
54
51
 
55
- RDoc::RDoc.add_generator( self )
52
+ RDoc::RDoc.add_generator self
53
+
54
+ include ERB::Util
55
+
56
+ # Path to this file's parent directory. Used to find templates and other
57
+ # resources.
58
+
59
+ GENERATOR_DIR = File.join 'rdoc', 'generator'
60
+
61
+ ##
62
+ # Release Version
63
+
64
+ VERSION = '2'
65
+
66
+ ##
67
+ # Initialize a few instance variables before we start
56
68
 
57
- include ERB::Util
69
+ def initialize options
70
+ @options = options
58
71
 
59
- # Subversion rev
60
- SVNRev = %$Rev: 52 $
72
+ @template_dir = Pathname.new options.template_dir
73
+ @template_cache = {}
61
74
 
62
- # Subversion ID
63
- SVNId = %$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $
75
+ @files = nil
76
+ @classes = nil
64
77
 
65
- # Path to this file's parent directory. Used to find templates and other
66
- # resources.
67
- GENERATOR_DIR = File.join 'rdoc', 'generator'
78
+ @basedir = Pathname.pwd.expand_path
79
+ end
68
80
 
69
- # Release Version
70
- VERSION = '1.1.6'
81
+ ##
82
+ # The output directory
71
83
 
72
- # Directory where generated classes live relative to the root
73
- CLASS_DIR = nil
84
+ attr_reader :outputdir
74
85
 
75
- # Directory where generated files live relative to the root
76
- FILE_DIR = nil
86
+ ##
87
+ # Output progress information if debugging is enabled
77
88
 
89
+ def debug_msg *msg
90
+ return unless $DEBUG_RDOC
91
+ $stderr.puts(*msg)
92
+ end
78
93
 
79
- #################################################################
80
- ### C L A S S M E T H O D S
81
- #################################################################
94
+ ##
95
+ # Directory where generated class HTML files live relative to the output
96
+ # dir.
82
97
 
83
- ### Standard generator factory method
84
- def self::for( options )
85
- new( options )
86
- end
98
+ def class_dir
99
+ nil
100
+ end
87
101
 
102
+ ##
103
+ # Directory where generated class HTML files live relative to the output
104
+ # dir.
88
105
 
89
- #################################################################
90
- ### I N S T A N C E M E T H O D S
91
- #################################################################
106
+ def file_dir
107
+ nil
108
+ end
92
109
 
93
- ### Initialize a few instance variables before we start
94
- def initialize( options )
95
- @options = options
110
+ ##
111
+ # Create the directories the generated docs will live in if they don't
112
+ # already exist.
96
113
 
97
- template = @options.template || 'darkfish'
114
+ def gen_sub_directories
115
+ @outputdir.mkpath
116
+ end
98
117
 
99
- template_dir = $LOAD_PATH.map do |path|
100
- File.join File.expand_path(path), GENERATOR_DIR, 'template', template
101
- end.find do |dir|
102
- File.directory? dir
103
- end
118
+ ##
119
+ # Copy over the stylesheet into the appropriate place in the output
120
+ # directory.
104
121
 
105
- raise RDoc::Error, "could not find template #{template.inspect}" unless
106
- template_dir
122
+ def write_style_sheet
123
+ debug_msg "Copying static files"
124
+ options = { :verbose => $DEBUG_RDOC, :noop => @options.dry_run }
107
125
 
108
- @template_dir = Pathname.new File.expand_path(template_dir)
126
+ FileUtils.cp @template_dir + 'rdoc.css', '.', options
109
127
 
110
- @files = nil
111
- @classes = nil
128
+ Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path|
129
+ next if File.directory? path
130
+ next if File.basename(path) =~ /^\./
112
131
 
113
- @basedir = Pathname.pwd.expand_path
114
- end
132
+ dst = Pathname.new(path).relative_path_from @template_dir
115
133
 
116
- ######
117
- public
118
- ######
134
+ # I suck at glob
135
+ dst_dir = dst.dirname
136
+ FileUtils.mkdir_p dst_dir, options unless File.exist? dst_dir
119
137
 
120
- # The output directory
121
- attr_reader :outputdir
138
+ FileUtils.cp @template_dir + path, dst, options
139
+ end
140
+ end
122
141
 
142
+ ##
143
+ # Build the initial indices and output objects based on an array of TopLevel
144
+ # objects containing the extracted information.
123
145
 
124
- ### Output progress information if debugging is enabled
125
- def debug_msg( *msg )
126
- return unless $DEBUG_RDOC
127
- $stderr.puts( *msg )
128
- end
146
+ def generate top_levels
147
+ @outputdir = Pathname.new(@options.op_dir).expand_path(@basedir)
129
148
 
130
- def class_dir
131
- CLASS_DIR
132
- end
149
+ @files = top_levels.sort
150
+ @classes = RDoc::TopLevel.all_classes_and_modules.sort
151
+ @methods = @classes.map { |m| m.method_list }.flatten.sort
152
+ @modsort = get_sorted_module_list(@classes)
133
153
 
134
- def file_dir
135
- FILE_DIR
136
- end
154
+ # Now actually write the output
155
+ write_style_sheet
156
+ generate_index
157
+ generate_class_files
158
+ generate_file_files
137
159
 
138
- ### Create the directories the generated docs will live in if
139
- ### they don't already exist.
140
- def gen_sub_directories
141
- @outputdir.mkpath
142
- end
160
+ rescue StandardError => err
161
+ debug_msg "%s: %s\n %s" % [
162
+ err.class.name, err.message, err.backtrace.join("\n ")
163
+ ]
164
+
165
+ raise
166
+ end
167
+
168
+ protected
169
+
170
+ ##
171
+ # Return a list of the documented modules sorted by salience first, then
172
+ # by name.
173
+
174
+ def get_sorted_module_list(classes)
175
+ nscounts = classes.inject({}) do |counthash, klass|
176
+ top_level = klass.full_name.gsub(/::.*/, '')
177
+ counthash[top_level] ||= 0
178
+ counthash[top_level] += 1
179
+
180
+ counthash
181
+ end
182
+
183
+ # Sort based on how often the top level namespace occurs, and then on the
184
+ # name of the module -- this works for projects that put their stuff into
185
+ # a namespace, of course, but doesn't hurt if they don't.
186
+ classes.sort_by do |klass|
187
+ top_level = klass.full_name.gsub( /::.*/, '' )
188
+ [nscounts[top_level] * -1, klass.full_name]
189
+ end.select do |klass|
190
+ klass.document_self
191
+ end
192
+ end
193
+
194
+ ##
195
+ # Generate an index page which lists all the classes which are documented.
196
+
197
+ def generate_index
198
+ template_file = @template_dir + 'index.rhtml'
199
+ return unless template_file.exist?
200
+
201
+ debug_msg "Rendering the index page..."
202
+
203
+ out_file = @basedir + @options.op_dir + 'index.html'
204
+
205
+ render_template template_file, out_file do |io| binding end
206
+ end
207
+
208
+ ##
209
+ # Generate a documentation file for each class
210
+
211
+ def generate_class_files
212
+ template_file = @template_dir + 'classpage.rhtml'
213
+ return unless template_file.exist?
214
+ debug_msg "Generating class documentation in #@outputdir"
215
+
216
+ @classes.each do |klass|
217
+ debug_msg " working on %s (%s)" % [klass.full_name, klass.path]
218
+ out_file = @outputdir + klass.path
219
+ # suppress 1.9.3 warning
220
+ rel_prefix = rel_prefix = @outputdir.relative_path_from(out_file.dirname)
221
+ svninfo = svninfo = self.get_svninfo(klass)
222
+
223
+ debug_msg " rendering #{out_file}"
224
+ render_template template_file, out_file do |io| binding end
225
+ end
226
+ end
227
+
228
+ ##
229
+ # Generate a documentation file for each file
230
+
231
+ def generate_file_files
232
+ template_file = @template_dir + 'filepage.rhtml'
233
+ return unless template_file.exist?
234
+ debug_msg "Generating file documentation in #@outputdir"
235
+
236
+ @files.each do |file|
237
+ out_file = @outputdir + file.path
238
+ debug_msg " working on %s (%s)" % [ file.full_name, out_file ]
239
+ # suppress 1.9.3 warning
240
+ rel_prefix = rel_prefix = @outputdir.relative_path_from(out_file.dirname)
241
+
242
+ debug_msg " rendering #{out_file}"
243
+ render_template template_file, out_file do |io| binding end
244
+ end
245
+ end
246
+
247
+ ##
248
+ # Return a string describing the amount of time in the given number of
249
+ # seconds in terms a human can understand easily.
250
+
251
+ def time_delta_string seconds
252
+ return 'less than a minute' if seconds < 60
253
+ return "#{seconds / 60} minute#{seconds / 60 == 1 ? '' : 's'}" if
254
+ seconds < 3000 # 50 minutes
255
+ return 'about one hour' if seconds < 5400 # 90 minutes
256
+ return "#{seconds / 3600} hours" if seconds < 64800 # 18 hours
257
+ return 'one day' if seconds < 86400 # 1 day
258
+ return 'about one day' if seconds < 172800 # 2 days
259
+ return "#{seconds / 86400} days" if seconds < 604800 # 1 week
260
+ return 'about one week' if seconds < 1209600 # 2 week
261
+ return "#{seconds / 604800} weeks" if seconds < 7257600 # 3 months
262
+ return "#{seconds / 2419200} months" if seconds < 31536000 # 1 year
263
+ return "#{seconds / 31536000} years"
264
+ end
265
+
266
+ # %q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $"
267
+ SVNID_PATTERN = /
268
+ \$Id:\s
269
+ (\S+)\s # filename
270
+ (\d+)\s # rev
271
+ (\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD)
272
+ (\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ)
273
+ (\w+)\s # committer
274
+ \$$
275
+ /x
276
+
277
+ ##
278
+ # Try to extract Subversion information out of the first constant whose
279
+ # value looks like a subversion Id tag. If no matching constant is found,
280
+ # and empty hash is returned.
281
+
282
+ def get_svninfo klass
283
+ constants = klass.constants or return {}
284
+
285
+ constants.find { |c| c.value =~ SVNID_PATTERN } or return {}
286
+
287
+ filename, rev, date, time, committer = $~.captures
288
+ commitdate = Time.parse "#{date} #{time}"
289
+
290
+ return {
291
+ :filename => filename,
292
+ :rev => Integer(rev),
293
+ :commitdate => commitdate,
294
+ :commitdelta => time_delta_string(Time.now - commitdate),
295
+ :committer => committer,
296
+ }
297
+ end
298
+
299
+ ##
300
+ # Load and render the erb template in the given +template_file+ and write
301
+ # it out to +out_file+.
302
+ #
303
+ # Both +template_file+ and +out_file+ should be Pathname-like objects.
304
+ #
305
+ # An io will be yielded which must be captured by binding in the caller.
306
+
307
+ def render_template template_file, out_file # :yield: io
308
+ template = template_for template_file
309
+
310
+ unless @options.dry_run then
311
+ debug_msg "Outputting to %s" % [out_file.expand_path]
312
+
313
+ out_file.dirname.mkpath
314
+ out_file.open 'w', 0644 do |io|
315
+ io.set_encoding @options.encoding if Object.const_defined? :Encoding
316
+
317
+ context = yield io
318
+
319
+ template_result template, context, template_file
320
+ end
321
+ else
322
+ context = yield nil
323
+
324
+ output = template_result template, context, template_file
325
+
326
+ debug_msg " would have written %d characters to %s" % [
327
+ output.length, out_file.expand_path
328
+ ]
329
+ end
330
+ end
331
+
332
+ ##
333
+ # Creates the result for +template+ with +context+. If an error is raised a
334
+ # Pathname +template_file+ will indicate the file where the error occurred.
335
+
336
+ def template_result template, context, template_file
337
+ template.filename = template_file.to_s
338
+ template.result context
339
+ rescue NoMethodError => e
340
+ raise RDoc::Error, "Error while evaluating %s: %s" % [
341
+ template_file.expand_path,
342
+ e.message,
343
+ ], e.backtrace
344
+ end
345
+
346
+ ##
347
+ # Retrieves a cache template for +file+, if present, or fills the cache.
348
+
349
+ def template_for file
350
+ template = @template_cache[file]
351
+
352
+ return template if template
353
+
354
+ klass = @options.dry_run ? ERB : RDoc::ERBIO
355
+
356
+ template = klass.new file.read, nil, '<>'
357
+ @template_cache[file] = template
358
+ template
359
+ end
143
360
 
144
- ### Copy over the stylesheet into the appropriate place in the output
145
- ### directory.
146
- def write_style_sheet
147
- debug_msg "Copying static files"
148
- options = { :verbose => $DEBUG_RDOC, :noop => $DARKFISH_DRYRUN }
149
-
150
- FileUtils.cp @template_dir + 'rdoc.css', '.', options
151
-
152
- Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path|
153
- next if File.directory? path
154
- next if File.basename(path) =~ /^\./
155
-
156
- dst = Pathname.new(path).relative_path_from @template_dir
157
-
158
- # I suck at glob
159
- dst_dir = dst.dirname
160
- FileUtils.mkdir_p dst_dir, options unless File.exist? dst_dir
161
-
162
- FileUtils.cp @template_dir + path, dst, options
163
- end
164
- end
165
-
166
- ### Build the initial indices and output objects
167
- ### based on an array of TopLevel objects containing
168
- ### the extracted information.
169
- def generate( top_levels )
170
- @outputdir = Pathname.new( @options.op_dir ).expand_path( @basedir )
171
-
172
- @files = top_levels.sort
173
- @classes = RDoc::TopLevel.all_classes_and_modules.sort
174
- @methods = @classes.map { |m| m.method_list }.flatten.sort
175
- @modsort = get_sorted_module_list( @classes )
176
-
177
- # Now actually write the output
178
- write_style_sheet
179
- generate_index
180
- generate_class_files
181
- generate_file_files
182
-
183
- rescue StandardError => err
184
- debug_msg "%s: %s\n %s" % [ err.class.name, err.message, err.backtrace.join("\n ") ]
185
- raise
186
- end
187
-
188
- #########
189
- protected
190
- #########
191
-
192
- ### Return a list of the documented modules sorted by salience first, then
193
- ### by name.
194
- def get_sorted_module_list( classes )
195
- nscounts = classes.inject({}) do |counthash, klass|
196
- top_level = klass.full_name.gsub( /::.*/, '' )
197
- counthash[top_level] ||= 0
198
- counthash[top_level] += 1
199
-
200
- counthash
201
- end
202
-
203
- # Sort based on how often the top level namespace occurs, and then on the
204
- # name of the module -- this works for projects that put their stuff into
205
- # a namespace, of course, but doesn't hurt if they don't.
206
- classes.sort_by do |klass|
207
- top_level = klass.full_name.gsub( /::.*/, '' )
208
- [
209
- nscounts[ top_level ] * -1,
210
- klass.full_name
211
- ]
212
- end.select do |klass|
213
- klass.document_self
214
- end
215
- end
216
-
217
- ### Generate an index page which lists all the classes which
218
- ### are documented.
219
- def generate_index
220
- template_file = @template_dir + 'index.rhtml'
221
- return unless template_file.exist?
222
-
223
- debug_msg "Rendering the index page..."
224
-
225
- template_src = template_file.read
226
- template = ERB.new( template_src, nil, '<>' )
227
- template.filename = template_file.to_s
228
- context = binding()
229
-
230
- output = nil
231
-
232
- begin
233
- output = template.result( context )
234
- rescue NoMethodError => err
235
- raise RDoc::Error, "Error while evaluating %s: %s (at %p)" % [
236
- template_file,
237
- err.message,
238
- eval( "_erbout[-50,50]", context )
239
- ], err.backtrace
240
- end
241
-
242
- outfile = @basedir + @options.op_dir + 'index.html'
243
- unless $DARKFISH_DRYRUN
244
- debug_msg "Outputting to %s" % [outfile.expand_path]
245
- outfile.open( 'w', 0644 ) do |fh|
246
- fh.print( output )
247
- end
248
- else
249
- debug_msg "Would have output to %s" % [outfile.expand_path]
250
- end
251
- end
252
-
253
- ### Generate a documentation file for each class
254
- def generate_class_files
255
- template_file = @template_dir + 'classpage.rhtml'
256
- return unless template_file.exist?
257
- debug_msg "Generating class documentation in #@outputdir"
258
-
259
- @classes.each do |klass|
260
- debug_msg " working on %s (%s)" % [ klass.full_name, klass.path ]
261
- outfile = @outputdir + klass.path
262
- rel_prefix = @outputdir.relative_path_from( outfile.dirname )
263
- svninfo = self.get_svninfo( klass )
264
-
265
- debug_msg " rendering #{outfile}"
266
- self.render_template( template_file, binding(), outfile )
267
- end
268
- end
269
-
270
- ### Generate a documentation file for each file
271
- def generate_file_files
272
- template_file = @template_dir + 'filepage.rhtml'
273
- return unless template_file.exist?
274
- debug_msg "Generating file documentation in #@outputdir"
275
-
276
- @files.each do |file|
277
- outfile = @outputdir + file.path
278
- debug_msg " working on %s (%s)" % [ file.full_name, outfile ]
279
- rel_prefix = @outputdir.relative_path_from( outfile.dirname )
280
- context = binding()
281
-
282
- debug_msg " rendering #{outfile}"
283
- self.render_template( template_file, binding(), outfile )
284
- end
285
- end
286
-
287
-
288
- ### Return a string describing the amount of time in the given number of
289
- ### seconds in terms a human can understand easily.
290
- def time_delta_string( seconds )
291
- return 'less than a minute' if seconds < 1.minute
292
- return (seconds / 1.minute).to_s + ' minute' + (seconds/60 == 1 ? '' : 's') if seconds < 50.minutes
293
- return 'about one hour' if seconds < 90.minutes
294
- return (seconds / 1.hour).to_s + ' hours' if seconds < 18.hours
295
- return 'one day' if seconds < 1.day
296
- return 'about one day' if seconds < 2.days
297
- return (seconds / 1.day).to_s + ' days' if seconds < 1.week
298
- return 'about one week' if seconds < 2.week
299
- return (seconds / 1.week).to_s + ' weeks' if seconds < 3.months
300
- return (seconds / 1.month).to_s + ' months' if seconds < 1.year
301
- return (seconds / 1.year).to_s + ' years'
302
- end
303
-
304
-
305
- # %q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $"
306
- SVNID_PATTERN = /
307
- \$Id:\s
308
- (\S+)\s # filename
309
- (\d+)\s # rev
310
- (\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD)
311
- (\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ)
312
- (\w+)\s # committer
313
- \$$
314
- /x
315
-
316
- ### Try to extract Subversion information out of the first constant whose value looks like
317
- ### a subversion Id tag. If no matching constant is found, and empty hash is returned.
318
- def get_svninfo( klass )
319
- constants = klass.constants or return {}
320
-
321
- constants.find {|c| c.value =~ SVNID_PATTERN } or return {}
322
-
323
- filename, rev, date, time, committer = $~.captures
324
- commitdate = Time.parse( date + ' ' + time )
325
-
326
- return {
327
- :filename => filename,
328
- :rev => Integer( rev ),
329
- :commitdate => commitdate,
330
- :commitdelta => time_delta_string( Time.now.to_i - commitdate.to_i ),
331
- :committer => committer,
332
- }
333
- end
334
-
335
-
336
- ### Load and render the erb template in the given +template_file+ within the
337
- ### specified +context+ (a Binding object) and write it out to +outfile+.
338
- ### Both +template_file+ and +outfile+ should be Pathname-like objects.
339
-
340
- def render_template( template_file, context, outfile )
341
- template_src = template_file.read
342
- template = ERB.new( template_src, nil, '<>' )
343
- template.filename = template_file.to_s
344
-
345
- output = begin
346
- template.result( context )
347
- rescue NoMethodError => err
348
- raise RDoc::Error, "Error while evaluating %s: %s (at %p)" % [
349
- template_file.to_s,
350
- err.message,
351
- eval( "_erbout[-50,50]", context )
352
- ], err.backtrace
353
- end
354
-
355
- unless $DARKFISH_DRYRUN
356
- outfile.dirname.mkpath
357
- outfile.open( 'w', 0644 ) do |ofh|
358
- ofh.print( output )
359
- end
360
- else
361
- debug_msg " would have written %d bytes to %s" %
362
- [ output.length, outfile ]
363
- end
364
- end
365
-
366
- end # Roc::Generator::Darkfish
367
-
368
- # :stopdoc:
369
-
370
- ### Time constants
371
- module TimeConstantMethods # :nodoc:
372
-
373
- ### Number of seconds (returns receiver unmodified)
374
- def seconds
375
- return self
376
- end
377
- alias_method :second, :seconds
378
-
379
- ### Returns number of seconds in <receiver> minutes
380
- def minutes
381
- return self * 60
382
- end
383
- alias_method :minute, :minutes
384
-
385
- ### Returns the number of seconds in <receiver> hours
386
- def hours
387
- return self * 60.minutes
388
- end
389
- alias_method :hour, :hours
390
-
391
- ### Returns the number of seconds in <receiver> days
392
- def days
393
- return self * 24.hours
394
- end
395
- alias_method :day, :days
396
-
397
- ### Return the number of seconds in <receiver> weeks
398
- def weeks
399
- return self * 7.days
400
- end
401
- alias_method :week, :weeks
402
-
403
- ### Returns the number of seconds in <receiver> fortnights
404
- def fortnights
405
- return self * 2.weeks
406
- end
407
- alias_method :fortnight, :fortnights
408
-
409
- ### Returns the number of seconds in <receiver> months (approximate)
410
- def months
411
- return self * 30.days
412
- end
413
- alias_method :month, :months
414
-
415
- ### Returns the number of seconds in <receiver> years (approximate)
416
- def years
417
- return (self * 365.25.days).to_i
418
- end
419
- alias_method :year, :years
420
-
421
-
422
- ### Returns the Time <receiver> number of seconds before the
423
- ### specified +time+. E.g., 2.hours.before( header.expiration )
424
- def before( time )
425
- return time - self
426
- end
427
-
428
-
429
- ### Returns the Time <receiver> number of seconds ago. (e.g.,
430
- ### expiration > 2.hours.ago )
431
- def ago
432
- return self.before( ::Time.now )
433
- end
434
-
435
-
436
- ### Returns the Time <receiver> number of seconds after the given +time+.
437
- ### E.g., 10.minutes.after( header.expiration )
438
- def after( time )
439
- return time + self
440
- end
441
-
442
- # Reads best without arguments: 10.minutes.from_now
443
- def from_now
444
- return self.after( ::Time.now )
445
- end
446
- end # module TimeConstantMethods
447
-
448
-
449
- # Extend Numeric with time constants
450
- class Numeric # :nodoc:
451
- include TimeConstantMethods
452
361
  end
453
362