ihelp 0.3.2
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/lib/ihelp.rb +661 -0
- metadata +45 -0
data/lib/ihelp.rb
ADDED
@@ -0,0 +1,661 @@
|
|
1
|
+
require 'rdoc/ri/ri_driver'
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
# Ri bindings for interactive use from within Ruby.
|
5
|
+
# Does a bit of second-guessing (Instance method? Class method?
|
6
|
+
# Try both unless explicitly defined. Not found in this class? Try the
|
7
|
+
# ancestor classes.)
|
8
|
+
#
|
9
|
+
# The goal is that help is given for all methods that have help.
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
#
|
13
|
+
# require 'ihelp'
|
14
|
+
#
|
15
|
+
# a = "string"
|
16
|
+
# a.help
|
17
|
+
# a.help :reverse
|
18
|
+
# a.help :map
|
19
|
+
# String.help
|
20
|
+
# String.help :new
|
21
|
+
# String.help :reverse
|
22
|
+
# String.help :map
|
23
|
+
# String.instance_help :reverse
|
24
|
+
# String.instance_help :new # => No help found.
|
25
|
+
# a.help :new
|
26
|
+
# help "String#reverse"
|
27
|
+
# help "String.reverse"
|
28
|
+
# a.method(:reverse).help # gets help for Method
|
29
|
+
# help "Hash#map"
|
30
|
+
#
|
31
|
+
#
|
32
|
+
# Custom help renderers:
|
33
|
+
#
|
34
|
+
# The help-method calls IHelp::Renderer's method defined by
|
35
|
+
# IHelp.renderer with the RI info object. You can print help
|
36
|
+
# out the way you want by defining your own renderer method
|
37
|
+
# in IHelp::Renderer and setting IHelp.renderer to the name
|
38
|
+
# of the method.
|
39
|
+
#
|
40
|
+
# Example:
|
41
|
+
#
|
42
|
+
# require 'ihelp'
|
43
|
+
#
|
44
|
+
# IHelp.renderers
|
45
|
+
# # => ["emacs", "rubydoc", "ri", "source", "html"]
|
46
|
+
#
|
47
|
+
# class IHelp::Renderer
|
48
|
+
# def print_name(info)
|
49
|
+
# puts info.full_name
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# IHelp.renderers
|
54
|
+
# # => ["emacs", "rubydoc", "ri", "source", "print_name", "html"]
|
55
|
+
#
|
56
|
+
# IHelp.renderer = :print_name
|
57
|
+
#
|
58
|
+
# [1,2,3].help:reject
|
59
|
+
# # Array#reject
|
60
|
+
# # => nil
|
61
|
+
#
|
62
|
+
# The current renderers are:
|
63
|
+
#
|
64
|
+
# ri -- the default renderer
|
65
|
+
# html -- creates a HTML document for the help and opens it
|
66
|
+
# with the program named in IHelp.web_browser
|
67
|
+
# rubydoc -- opens the corresponding www.ruby-doc.org class
|
68
|
+
# documentation page with the program named in
|
69
|
+
# IHelp.web_browser
|
70
|
+
# emacs -- uses gnudoit and ri-emacs to display help in an Emacs buffer.
|
71
|
+
# The Emacs commands that I got it running with were:
|
72
|
+
# ;; make ri-emacs autoload according to its instructions
|
73
|
+
# M-x ruby-mode
|
74
|
+
# M-x gnuserv-start
|
75
|
+
# M-x run-ruby
|
76
|
+
# > IHelp.renderer = :emacs
|
77
|
+
# > String.help:center
|
78
|
+
# source -- uses RubyToRuby to print the source for the method
|
79
|
+
# (experimental)
|
80
|
+
#
|
81
|
+
#
|
82
|
+
# Changelog:
|
83
|
+
#
|
84
|
+
# 0.3.2
|
85
|
+
# Added support for ruby 1.8.5, added emacs renderer from
|
86
|
+
# rubykitch <rubykitch@ruby-lang.org>, made source renderer use the
|
87
|
+
# released version of RubyToRuby.
|
88
|
+
#
|
89
|
+
# License: Ruby's
|
90
|
+
#
|
91
|
+
# Author: Ilmari Heikkinen <kig misfiring net>
|
92
|
+
#
|
93
|
+
module IHelp
|
94
|
+
HELP_VERSION = "0.3.2"
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
module IHelp
|
99
|
+
|
100
|
+
# Returns list of available renderers.
|
101
|
+
def self.renderers
|
102
|
+
Renderer.instance_methods(false)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Contains the help renderer methods to be used by IHelp#help.
|
106
|
+
# The help-method creates a new instance of Renderer and calls
|
107
|
+
# the method defined by IHelp.renderer with the RI info object.
|
108
|
+
#
|
109
|
+
class Renderer
|
110
|
+
|
111
|
+
# Default renderer method, opens the help using the IHelpDriver
|
112
|
+
# gotten from IHelp.ri_driver.
|
113
|
+
#
|
114
|
+
def ri(info)
|
115
|
+
IHelp.ri_driver.display_info(info)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Opens the class documentation page on www.ruby-doc.org using
|
119
|
+
# the program defined in IHelp::Renderer.web_browser.
|
120
|
+
#
|
121
|
+
def rubydoc(info)
|
122
|
+
require 'uri'
|
123
|
+
class_name = parse_ruby_doc_url(info.full_name)
|
124
|
+
puts "Opening help for: #{class_name.gsub(/\//,"::")}"
|
125
|
+
system(IHelp.web_browser, "http://www.ruby-doc.org/core/classes/#{class_name}.html")
|
126
|
+
end
|
127
|
+
|
128
|
+
# Show sources -renderer using RubyToRuby.
|
129
|
+
#
|
130
|
+
# http://seattlerb.rubyforge.org/
|
131
|
+
#
|
132
|
+
# sudo gem install ruby2ruby
|
133
|
+
#
|
134
|
+
def source(info)
|
135
|
+
require 'ruby2ruby'
|
136
|
+
class_name = info.full_name.split(/[#\.]/).first
|
137
|
+
klass = class_name.split("::").inject(Object){|o,i| o.const_get(i)}
|
138
|
+
args = [klass]
|
139
|
+
args << info.name if info.is_a? RI::MethodDescription
|
140
|
+
puts RubyToRuby.translate(*args)
|
141
|
+
end
|
142
|
+
|
143
|
+
# XEmacs renderer.
|
144
|
+
# Uses ri-emacs to show the ri output in Emacs.
|
145
|
+
#
|
146
|
+
# http://rubyforge.org/projects/ri-emacs/
|
147
|
+
# http://www.rubyist.net/~rubikitch/computer/irbsh/index.en.html
|
148
|
+
#
|
149
|
+
def emacs(info)
|
150
|
+
system "gnudoit", %Q[(progn (ri "#{info.full_name}") "#{info.full_name}")]
|
151
|
+
end
|
152
|
+
|
153
|
+
def html(info)
|
154
|
+
puts "Opening help for: #{info.full_name}"
|
155
|
+
doc = REXML::Document.new
|
156
|
+
root = doc.add_element("html")
|
157
|
+
head = root.add_element("head")
|
158
|
+
title = head.add_element("title")
|
159
|
+
title.add_text("#{info.full_name} - RI Documentation")
|
160
|
+
body = root.add_element("body")
|
161
|
+
body.add_element(info.to_html.root)
|
162
|
+
tmp = Tempfile.new("#{info.full_name.gsub(/\W/,"_")}_doc.html")
|
163
|
+
tmp.write( doc.to_s(2) )
|
164
|
+
tmp.flush
|
165
|
+
pid = fork{
|
166
|
+
system(IHelp.web_browser, "file://#{tmp.path}")
|
167
|
+
tmp.close!
|
168
|
+
}
|
169
|
+
Process.detach(pid)
|
170
|
+
pid
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
def parse_ruby_doc_url(item_name)
|
175
|
+
item_name.split(/\.|#/,2).first.gsub(/::/,"/")
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
# Print out help for self.
|
182
|
+
#
|
183
|
+
# If method_name is given, prints help for that method.
|
184
|
+
# If instance is true, tries to find help only for the instance method.
|
185
|
+
# If instance is false, tries to find help for the object's method only.
|
186
|
+
# If instance is nil, checks object's method first, then instance method.
|
187
|
+
#
|
188
|
+
# Uses help_description(method_name, instance).
|
189
|
+
#
|
190
|
+
def help(method_name=nil, instance=nil)
|
191
|
+
info = help_description(method_name, instance)
|
192
|
+
if not info
|
193
|
+
puts "No help found."
|
194
|
+
return
|
195
|
+
end
|
196
|
+
IHelp.render(info)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Print out help for instance method method_name.
|
200
|
+
# If no method_name given, behaves like #help.
|
201
|
+
#
|
202
|
+
def instance_help(method_name = nil)
|
203
|
+
help(method_name, true)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Returns help string in YAML for self.
|
207
|
+
#
|
208
|
+
# If method_name is given, prints help for that method.
|
209
|
+
# If instance is true, tries to find help only for the instance method.
|
210
|
+
# If instance is false, tries to find help for the object's method only.
|
211
|
+
# If instance is nil, checks object's method first, then instance method.
|
212
|
+
# Returns nil if there is no help to be found.
|
213
|
+
#
|
214
|
+
def help_yaml(method_name=nil, instance=nil)
|
215
|
+
info = help_description(method_name, instance)
|
216
|
+
info.to_yaml if info
|
217
|
+
end
|
218
|
+
|
219
|
+
# Returns help string as a HTML REXML::Document with a DIV element as the root.
|
220
|
+
#
|
221
|
+
# If method_name is given, prints help for that method.
|
222
|
+
# If instance is true, tries to find help only for the instance method.
|
223
|
+
# If instance is false, tries to find help for the object's method only.
|
224
|
+
# If instance is nil, checks object's method first, then instance method.
|
225
|
+
# Returns nil if there is no help to be found.
|
226
|
+
#
|
227
|
+
def help_html(method_name=nil, instance=nil)
|
228
|
+
info = help_description(method_name, instance)
|
229
|
+
info.to_html if info
|
230
|
+
end
|
231
|
+
|
232
|
+
# Return RI::ClassDescription / RI::MethodDescription for self
|
233
|
+
# or its method meth, or its instance method meth if instance == true.
|
234
|
+
#
|
235
|
+
def help_description(method_name=nil, instance=nil)
|
236
|
+
IHelp.generate_help_description(self, method_name, instance)
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
class << self
|
241
|
+
|
242
|
+
# Help renderer to use.
|
243
|
+
attr_accessor :renderer
|
244
|
+
|
245
|
+
# Web browser to use for html and rubydoc renderers.
|
246
|
+
attr_accessor :web_browser
|
247
|
+
|
248
|
+
IHelp.renderer ||= :ri
|
249
|
+
IHelp.web_browser ||= 'firefox'
|
250
|
+
|
251
|
+
IHelp::RI_ARGS = []
|
252
|
+
if ENV["RI"]
|
253
|
+
IHelp::RI_ARGS = ENV["RI"].split.concat(ARGV)
|
254
|
+
end
|
255
|
+
|
256
|
+
# Render the RI info object a renderer method in IHelp::Renderer.
|
257
|
+
# The name of the renderer method to use is returned by IHelp.renderer,
|
258
|
+
# and can be set with IHelp.renderer=.
|
259
|
+
#
|
260
|
+
def render(info)
|
261
|
+
IHelp::Renderer.new.send(renderer, info)
|
262
|
+
end
|
263
|
+
|
264
|
+
def ri_driver
|
265
|
+
@ri_driver ||= IHelpDriver.new(RI_ARGS)
|
266
|
+
end
|
267
|
+
|
268
|
+
# Return RI::ClassDescription / RI::MethodDescription for klass
|
269
|
+
# or its method meth, or its instance method meth if instance == true.
|
270
|
+
#
|
271
|
+
def generate_help_description(klass, meth=nil, instance=nil)
|
272
|
+
meth_str = nil
|
273
|
+
double_colon = false
|
274
|
+
if meth
|
275
|
+
meth_str = meth.to_s
|
276
|
+
if /::|\.|#/ === meth_str # called with e.g."Array#str","String.new"
|
277
|
+
meth_str, klass_name, instance_help, double_colon =
|
278
|
+
get_help_klass_info_for_name(meth_str)
|
279
|
+
klass_ancs = find_ancestors(klass_name, instance)
|
280
|
+
else
|
281
|
+
klass_name, klass_ancs, instance_help =
|
282
|
+
get_help_klass_info(klass, instance)
|
283
|
+
end
|
284
|
+
else
|
285
|
+
klass_name, klass_ancs, instance_help =
|
286
|
+
get_help_klass_info(klass, instance)
|
287
|
+
end
|
288
|
+
info = get_help_info(meth_str, klass_name, klass_ancs, instance_help,
|
289
|
+
instance)
|
290
|
+
# Retry with method as class if double_colon-splitted and no info
|
291
|
+
if info.nil? and double_colon
|
292
|
+
klass_name = [klass_name, meth_str].join("::")
|
293
|
+
meth_str = nil
|
294
|
+
klass_ancs = find_ancestors(klass_name, instance)
|
295
|
+
info = get_help_info(
|
296
|
+
meth_str, klass_name, klass_ancs, instance_help, instance)
|
297
|
+
end
|
298
|
+
info
|
299
|
+
end
|
300
|
+
|
301
|
+
private
|
302
|
+
def get_help_klass_info(klass,instance)
|
303
|
+
if klass.is_a? Class or klass.is_a? Module
|
304
|
+
klass_ancs = klass.ancestors + klass.class.ancestors
|
305
|
+
klass_ancs -= [klass, klass.class]
|
306
|
+
instance = false if instance.nil?
|
307
|
+
# If we are an instance, set klass to our class
|
308
|
+
#
|
309
|
+
else
|
310
|
+
klass = klass.class
|
311
|
+
klass_ancs = klass.ancestors - [klass]
|
312
|
+
instance = true if instance.nil?
|
313
|
+
end
|
314
|
+
klass_name = klass.name
|
315
|
+
[klass_name, klass_ancs, instance]
|
316
|
+
end
|
317
|
+
|
318
|
+
def get_help_klass_info_for_name(meth_str)
|
319
|
+
double_colon = false
|
320
|
+
# Maybe we are being called with something like "Array#slice"
|
321
|
+
if /#/ === meth_str
|
322
|
+
klass_name, meth_str = meth_str.split(/#/, 2)
|
323
|
+
instance = true
|
324
|
+
|
325
|
+
# Or maybe the requested item is "Ri::RiDriver.new"
|
326
|
+
elsif /\./ === meth_str
|
327
|
+
klass_name, meth_str = meth_str.reverse.split(/\./, 2).
|
328
|
+
reverse.map{|i| i.reverse}
|
329
|
+
instance = false
|
330
|
+
|
331
|
+
# And the problematic case of "Test::Unit" (is Unit a class name or
|
332
|
+
# a method name? Why does Ri even care?)
|
333
|
+
else
|
334
|
+
klass_name, meth_str = meth_str.reverse.split(/::/, 2).
|
335
|
+
reverse.map{|i| i.reverse}
|
336
|
+
double_colon = true
|
337
|
+
instance = false
|
338
|
+
end
|
339
|
+
[meth_str, klass_name, instance, double_colon]
|
340
|
+
end
|
341
|
+
|
342
|
+
# Find ancestors for klass_name (works if the class has been loaded)
|
343
|
+
def find_ancestors(klass_name, instance)
|
344
|
+
similarily_named_class = nil
|
345
|
+
ObjectSpace.each_object(Class){|k|
|
346
|
+
similarily_named_class = k if k.name == klass_name
|
347
|
+
break if similarily_named_class
|
348
|
+
}
|
349
|
+
if similarily_named_class
|
350
|
+
klass_ancs = similarily_named_class.ancestors
|
351
|
+
klass_ancs += similarily_named_class.class.ancestors unless instance
|
352
|
+
else
|
353
|
+
klass_ancs = []
|
354
|
+
end
|
355
|
+
klass_ancs
|
356
|
+
end
|
357
|
+
|
358
|
+
def get_help_info(meth_str, klass_name, klass_ancs, instance_help, instance)
|
359
|
+
info = get_help_info_str(meth_str, klass_name, klass_ancs, instance_help)
|
360
|
+
# If instance is undefined, try both the class methods and instance
|
361
|
+
# methods.
|
362
|
+
if info.nil? and instance.nil?
|
363
|
+
info = get_help_info_str(
|
364
|
+
meth_str, klass_name, klass_ancs, (not instance_help))
|
365
|
+
end
|
366
|
+
info
|
367
|
+
end
|
368
|
+
|
369
|
+
def get_help_info_str(meth_str, klass_name, klass_ancs, instance)
|
370
|
+
info_str = ri_driver.get_info_str(klass_name, meth_str, instance)
|
371
|
+
if not info_str
|
372
|
+
# Walk through class hierarchy to find an inherited method
|
373
|
+
ancest = klass_ancs.find{|anc|
|
374
|
+
info_str = ri_driver.get_info_str(anc.name, meth_str, instance)
|
375
|
+
}
|
376
|
+
# Avoid returning Object in case of no help.
|
377
|
+
if ancest == Object and meth_str.nil? and klass_name != Object.name
|
378
|
+
info_str = nil
|
379
|
+
end
|
380
|
+
end
|
381
|
+
info_str
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
|
386
|
+
# Version of RiDriver that takes its options
|
387
|
+
# as parameter to #initialize.
|
388
|
+
#
|
389
|
+
class IHelpDriver < RiDriver
|
390
|
+
|
391
|
+
# Create new IHelpDriver, with the given args
|
392
|
+
# passed to @options, which is a RI::Options.instance
|
393
|
+
#
|
394
|
+
def initialize(args = [])
|
395
|
+
@options = RI::Options.instance
|
396
|
+
@options.parse(args)
|
397
|
+
|
398
|
+
paths = (if RUBY_VERSION > "1.8.4"
|
399
|
+
@options.doc_dir
|
400
|
+
else
|
401
|
+
@options.paths
|
402
|
+
end) || RI::Paths::PATH
|
403
|
+
if paths.empty?
|
404
|
+
report_missing_documentation(paths)
|
405
|
+
end
|
406
|
+
@ri_reader = RI::RiReader.new(RI::RiCache.new(paths))
|
407
|
+
@display = @options.displayer
|
408
|
+
end
|
409
|
+
|
410
|
+
# Get info string from ri database for klass_name [method_name]
|
411
|
+
#
|
412
|
+
def get_info_str(klass_name, method_name = nil, instance = false)
|
413
|
+
is_class_method = (not instance)
|
414
|
+
top_level_namespace = @ri_reader.top_level_namespace
|
415
|
+
namespaces = klass_name.split(/::/).inject(top_level_namespace){
|
416
|
+
|ns, current_name|
|
417
|
+
@ri_reader.lookup_namespace_in(current_name, ns)
|
418
|
+
}
|
419
|
+
return nil if namespaces.empty?
|
420
|
+
if method_name.nil?
|
421
|
+
get_class_info_str(namespaces)
|
422
|
+
else
|
423
|
+
methods = @ri_reader.find_methods(
|
424
|
+
method_name, is_class_method, namespaces)
|
425
|
+
return nil if methods.empty?
|
426
|
+
get_method_info_str(method_name, methods)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
# Display the info based on if it's
|
431
|
+
# for a class or a method. Using ri's pager.
|
432
|
+
#
|
433
|
+
def display_info(info)
|
434
|
+
case [info.class] # only info.class doesn't work
|
435
|
+
when [RI::ClassDescription]
|
436
|
+
@display.display_class_info(info, @ri_reader)
|
437
|
+
when [RI::MethodDescription]
|
438
|
+
@display.display_method_info(info)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
# Get info for the class in the given namespaces.
|
443
|
+
#
|
444
|
+
def get_class_info_str(namespaces)
|
445
|
+
return nil if namespaces.empty?
|
446
|
+
klass = nil
|
447
|
+
namespaces.find{|ns|
|
448
|
+
begin
|
449
|
+
klass = @ri_reader.get_class(ns)
|
450
|
+
rescue TypeError
|
451
|
+
nil
|
452
|
+
end
|
453
|
+
}
|
454
|
+
klass
|
455
|
+
end
|
456
|
+
|
457
|
+
# Get info for the method in the given methods.
|
458
|
+
#
|
459
|
+
def get_method_info_str(requested_method_name, methods)
|
460
|
+
if methods.size == 1
|
461
|
+
@ri_reader.get_method(methods.first)
|
462
|
+
else
|
463
|
+
entries = methods.find_all {|m| m.name == requested_method_name}
|
464
|
+
return nil if entries.empty?
|
465
|
+
method = nil
|
466
|
+
entries.find{|entry| method = @ri_reader.get_method(entry)}
|
467
|
+
method
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
end
|
472
|
+
|
473
|
+
|
474
|
+
end
|
475
|
+
|
476
|
+
|
477
|
+
module RI
|
478
|
+
|
479
|
+
class MethodDescription
|
480
|
+
|
481
|
+
# Creates HTML element from the MethodDescription.
|
482
|
+
# Uses container_tag as the root node name and header_tag
|
483
|
+
# as the tag for the header element that contains the method's name.
|
484
|
+
#
|
485
|
+
# Returns a REXML document with container_tag as the root element name.
|
486
|
+
#
|
487
|
+
def to_html(container_tag="div", header_tag="h1")
|
488
|
+
doc = REXML::Document.new
|
489
|
+
root = doc.add_element(container_tag)
|
490
|
+
header = root.add_element(header_tag)
|
491
|
+
header.add_text(full_name)
|
492
|
+
comment.each{|c|
|
493
|
+
tag = c.class.to_s.split("::").last
|
494
|
+
tag = "PRE" if tag == "VERB"
|
495
|
+
xmlstr = "<#{tag}>#{c.body}</#{tag}>"
|
496
|
+
c_doc = REXML::Document.new(xmlstr)
|
497
|
+
root.add_element( c_doc.root )
|
498
|
+
}
|
499
|
+
doc
|
500
|
+
end
|
501
|
+
|
502
|
+
end
|
503
|
+
|
504
|
+
|
505
|
+
class ClassDescription
|
506
|
+
|
507
|
+
# Creates HTML element from the ClassDescription.
|
508
|
+
# Uses container_tag as the root node name and header_tag
|
509
|
+
# as the tag for the header element that contains the classes name.
|
510
|
+
# Uses methods_header_tag as the tag for the "Class/Instance Methods"
|
511
|
+
# method list headers.
|
512
|
+
# Uses methods_tag as the tag for the method lists.
|
513
|
+
#
|
514
|
+
# Returns a REXML document with container_tag as the root element name.
|
515
|
+
#
|
516
|
+
def to_html(container_tag="div", header_tag="h1",
|
517
|
+
methods_header_tag="h2", methods_tag="p")
|
518
|
+
doc = REXML::Document.new
|
519
|
+
root = doc.add_element(container_tag)
|
520
|
+
header = root.add_element(header_tag)
|
521
|
+
header.add_text(full_name)
|
522
|
+
comment.each{|c|
|
523
|
+
tag = c.class.to_s.split("::").last
|
524
|
+
tag = "PRE" if tag == "VERB"
|
525
|
+
xmlstr = "<#{tag}>#{c.body}</#{tag}>"
|
526
|
+
c_doc = REXML::Document.new(xmlstr)
|
527
|
+
root.add_element( c_doc.root )
|
528
|
+
}
|
529
|
+
root.add_element(methods_header_tag).add_text("Class Methods")
|
530
|
+
cmethods = root.add_element(methods_tag)
|
531
|
+
class_methods[0...-1].each{|m|
|
532
|
+
cmethods.add(m.to_html.root)
|
533
|
+
cmethods.add_text(", ")
|
534
|
+
}
|
535
|
+
cmethods.add(class_methods.last.to_html.root)
|
536
|
+
root.add_element(methods_header_tag).add_text("Instance Methods")
|
537
|
+
imethods = root.add_element(methods_tag)
|
538
|
+
instance_methods[0...-1].each{|m|
|
539
|
+
imethods.add(m.to_html.root)
|
540
|
+
imethods.add_text(", ")
|
541
|
+
}
|
542
|
+
imethods.add(instance_methods.last.to_html.root)
|
543
|
+
doc
|
544
|
+
end
|
545
|
+
|
546
|
+
end
|
547
|
+
|
548
|
+
|
549
|
+
class MethodSummary
|
550
|
+
|
551
|
+
# Creates HTML element from the ClassDescription.
|
552
|
+
# Puts the method's name inside the tag named in
|
553
|
+
# container_tag.
|
554
|
+
#
|
555
|
+
# Returns a REXML document with container_tag as the root element name.
|
556
|
+
#
|
557
|
+
def to_html(container_tag="em")
|
558
|
+
doc = REXML::Document.new
|
559
|
+
doc.add_element(container_tag).add_text(name)
|
560
|
+
doc
|
561
|
+
end
|
562
|
+
|
563
|
+
end
|
564
|
+
|
565
|
+
end
|
566
|
+
|
567
|
+
|
568
|
+
class Object
|
569
|
+
include IHelp
|
570
|
+
extend IHelp
|
571
|
+
end
|
572
|
+
|
573
|
+
|
574
|
+
if __FILE__ == $0
|
575
|
+
require 'test/unit'
|
576
|
+
|
577
|
+
# to get around rdoc documenting NoHelp
|
578
|
+
eval("module NoHelp; end")
|
579
|
+
|
580
|
+
class HelpTest < Test::Unit::TestCase
|
581
|
+
|
582
|
+
def no_warn
|
583
|
+
old_w = $-w
|
584
|
+
$-w = nil
|
585
|
+
yield
|
586
|
+
$-w = old_w
|
587
|
+
end
|
588
|
+
|
589
|
+
def setup
|
590
|
+
no_warn{
|
591
|
+
Object.const_set("ARGV", ["--readline", "--prompt-mode", "simple"])
|
592
|
+
}
|
593
|
+
IHelp.instance_variable_set(
|
594
|
+
:@ri_driver,
|
595
|
+
IHelp::IHelpDriver.new(IHelp::RI_ARGS))
|
596
|
+
end
|
597
|
+
|
598
|
+
def test_simple_help
|
599
|
+
assert("string".help_yaml)
|
600
|
+
end
|
601
|
+
|
602
|
+
def test_method_help
|
603
|
+
assert("string".help_yaml(:reverse))
|
604
|
+
end
|
605
|
+
|
606
|
+
def test_inherited_method_help
|
607
|
+
assert("string".help_yaml(:map))
|
608
|
+
end
|
609
|
+
|
610
|
+
def test_class_help
|
611
|
+
assert(String.help_yaml)
|
612
|
+
end
|
613
|
+
|
614
|
+
def test_class_method_help
|
615
|
+
assert(String.help_yaml(:new))
|
616
|
+
end
|
617
|
+
|
618
|
+
def test_class_inherited_method_help
|
619
|
+
assert(String.help_yaml(:map))
|
620
|
+
end
|
621
|
+
|
622
|
+
def test_method_equalities
|
623
|
+
assert(String.help_yaml(:new) ==
|
624
|
+
"string".help_yaml(:new))
|
625
|
+
assert(String.help_yaml(:reverse) ==
|
626
|
+
"string".help_yaml(:reverse))
|
627
|
+
end
|
628
|
+
|
629
|
+
def test_method_constraints
|
630
|
+
assert((not "string".help_yaml(:new,true)))
|
631
|
+
assert((not "string".help_yaml(:reverse,false)))
|
632
|
+
assert((not String.help_yaml(:new,true)))
|
633
|
+
assert((not String.help_yaml(:reverse,false)))
|
634
|
+
end
|
635
|
+
|
636
|
+
def test_help_yamlings
|
637
|
+
assert("string".help_yaml(:reverse) ==
|
638
|
+
help_yaml("String#reverse"))
|
639
|
+
assert(String.help_yaml(:new) ==
|
640
|
+
help_yaml("String::new"))
|
641
|
+
end
|
642
|
+
|
643
|
+
def test_multipart_namespaces
|
644
|
+
assert(Test::Unit.help_yaml)
|
645
|
+
assert(help_yaml("Test::Unit"))
|
646
|
+
assert(Test::Unit.help_yaml("run?"))
|
647
|
+
assert(help_yaml("Test::Unit.run?"))
|
648
|
+
assert(help_yaml("Test::Unit::run?"))
|
649
|
+
assert(help_yaml("Test::Unit#run?"))
|
650
|
+
end
|
651
|
+
|
652
|
+
def test_not_found
|
653
|
+
assert((NoHelp.help_yaml == nil))
|
654
|
+
assert((String.help_yaml(:nonexistent) == nil))
|
655
|
+
end
|
656
|
+
|
657
|
+
end
|
658
|
+
|
659
|
+
|
660
|
+
end
|
661
|
+
|
metadata
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: ihelp
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.3.2
|
7
|
+
date: 2006-11-13 00:00:00 +02:00
|
8
|
+
summary: Interactive help
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: kig@misfiring.net
|
12
|
+
homepage: ihelp.rubyforge.org
|
13
|
+
rubyforge_project: ihelp
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.8.1
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Ilmari Heikkinen
|
30
|
+
files:
|
31
|
+
- lib/ihelp.rb
|
32
|
+
test_files: []
|
33
|
+
|
34
|
+
rdoc_options: []
|
35
|
+
|
36
|
+
extra_rdoc_files: []
|
37
|
+
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
requirements: []
|
43
|
+
|
44
|
+
dependencies: []
|
45
|
+
|