See below.  Lifted from man.el; tested only briefly with xemacs 21.1 -
CAVEAT EMPTOR


Put this is your .emacs:

(autoload 'ri "ri" "Read output of ri command." t)

Put the code below into ri.el in a place your emacs can find it.
Then, in a buffer of ruby code, put the  point over something, say,
like Kernel, and M-x ri...

Enjoy.

-- 
Colin Steele                                                        CEO, WebG2
colin / webg2.com                                                  www.webg2.com
Main: 804-971-4777                                           Fax: 804-220-4652




;;; ri.el --- browse the output of the ri command

;; Copyright (C) 2001 Colin Steele

;; Original Authors:	ESR, pot, Barry A. Warsaw <bwarsaw / cen.com>
;; Keywords:		help
;; Adapted-By:		Colin Steele

;; This is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;; This code provides a function, `ri', with which you can browse
;; pages of Ruby documentation generated by the command 'ri'.
;; Formatting is done in background so that you can continue to use
;; your Emacs while processing is going on.
;;


;;; Code: (require 'assoc) (setq ri-notify 'aggressive) ;; Use the value of the obsolete user option Ri-notify, if set. (defcustom ri-notify-method (if (boundp 'ri-notify) ri-notify 'aggressive) "*Selects the behavior when ri page is ready. This variable may have one of the following values, where (sf) means that the frames are switched, so the ri page is displayed in the frame where the ri command was called from: newframe -- put the ri page in its own frame (see `ri-frame-parameters') pushy -- make the ri page the current buffer in the current window bully -- make the ri page the current buffer and only window (sf) aggressive -- make the ri page the current buffer in the other window (sf) friendly -- display ri page in the other window but don't make current (sf) polite -- don't display ri page, but prints message and beep when ready quiet -- like `polite', but don't beep meek -- make no indication that the ri page is ready Any other value of `ri-notify-method' is equivalent to `meek'." :type '(radio (const newframe) (const pushy) (const bully) (const aggressive) (const friendly) (const polite) (const quiet) (const meek)) :group 'ri) (defcustom ri-filter-list nil "*ri page cleaning filter command phrases. This variable contains a list of the following form: '((command-string phrase-string*)*) Each phrase-string is concatenated onto the command-string to form a command filter. The (standard) output (and standard error) of the Un*x ri command is piped through each command filter in the order the commands appear in the association list. The final output is placed in the ri page buffer." :type '(repeat (list (string :tag "Command String") (repeat :inline t (string :tag "Phrase String")))) :group 'ri) (defvar ri-switches "" "Switches passed to the ri command, as a single string.") (defvar ri-program "/usr/local/bin/ri" "The UNIX command to invoke ri.") (defsubst ri-build-ri-command () "Builds the entire background ri page and cleaning command." (let ((command (concat ri-program " " ri-switches "%s")) (flist ri-filter-list)) (while (and flist (car flist)) (let ((pcom (car (car flist))) (pargs (cdr (car flist)))) (setq command (concat command " | " pcom " " (mapconcat '(lambda (phrase) (if (not (stringp phrase)) (error "Malformed ri-filter-list")) phrase) pargs " "))) (setq flist (cdr flist)))) command)) (defun ruby-default-ri-entry () (let (word) (save-excursion (setq word (current-word)) (if (string-match "[._]+$" word) (setq word (substring word 0 (match-beginning 0)))) (format "%s%s" word "")))) (defun ri (ri-args) (interactive (list (let* ((default-entry (ruby-default-ri-entry)) (input (read-string (format "Ri entry%s: " (if (string= default-entry "") "" (format " (default %s)" default-entry)))))) (if (string= input "") (if (string= default-entry "") (error "No ri args given") default-entry) input)))) (ri-getpage-in-background ri-args)) (defun ri-getpage-in-background (topic) "Uses TOPIC to build and fire off the ri and cleaning command." (let* ((ri-args topic) (bufname (concat "*ri " ri-args "*")) (buffer (get-buffer bufname))) (if buffer (ri-notify-when-ready buffer) (require 'env) (message "Invoking %s %s in the background" ri-program ri-args) (setq buffer (generate-new-buffer bufname)) (save-excursion (set-buffer buffer) (setq ri-original-frame (selected-frame)) (setq ri-arguments ri-args)) (let ((process-environment (copy-sequence process-environment)) ;; The following is so Awk script gets \n intact ;; But don't prevent decoding of the outside. (coding-system-for-write 'raw-text-unix) ;; Avoid possible error by using a directory that always exists. (default-directory "/")) ;; Prevent any attempt to use display terminal fanciness. (setenv "TErM" "dumb") (if (fboundp 'start-process) (set-process-sentinel (start-process ri-program buffer "sh" "-c" (format (ri-build-ri-command) ri-args)) 'ri-bgproc-sentinel) (progn (let ((exit-status (call-process shell-file-name nil (list buffer nil) nil "-c" (format (ri-build-ri-command) ri-args))) (msg "")) (or (and (numberp exit-status) (= exit-status 0)) (and (numberp exit-status) (setq msg (format "exited abnormally with code %d" exit-status))) (setq msg exit-status)) (ri-bgproc-sentinel bufname msg)))))))) (defun ri-notify-when-ready (ri-buffer) "Notify the user when ri-buffer is ready. See the variable `ri-notify-method' for the different notification behaviors." (let ((saved-frame (save-excursion (set-buffer ri-buffer) ri-original-frame))) (cond ((eq ri-notify-method 'newframe) ;; Since we run asynchronously, perhaps while Emacs is waiting ;; for input, we must not leave a different buffer current. We ;; can't rely on the editor command loop to reselect the ;; selected window's buffer. (save-excursion (let ((frame (make-frame ri-frame-parameters))) (set-window-buffer (frame-selected-window frame) ri-buffer) (set-window-dedicated-p (frame-selected-window frame) t)))) ((eq ri-notify-method 'pushy) (switch-to-buffer ri-buffer)) ((eq ri-notify-method 'bully) (and window-system (frame-live-p saved-frame) (select-frame saved-frame)) (pop-to-buffer ri-buffer) (delete-other-windows)) ((eq ri-notify-method 'aggressive) (and window-system (frame-live-p saved-frame) (select-frame saved-frame)) (pop-to-buffer ri-buffer)) ((eq ri-notify-method 'friendly) (and window-system (frame-live-p saved-frame) (select-frame saved-frame)) (display-buffer ri-buffer 'not-this-window)) ((eq ri-notify-method 'polite) (beep) (message "ri buffer %s is ready" (buffer-name ri-buffer))) ((eq ri-notify-method 'quiet) (message "ri buffer %s is ready" (buffer-name ri-buffer))) ((or (eq ri-notify-method 'meek) t) (message "")) ))) (defun ri-bgproc-sentinel (process msg) "Ri Page background process sentinel. When ri page command is run asynchronously, PROCESS is the process object for the ri page command; when ri page command is run synchronously, PROCESS is the name of the buffer where the ri page command is run. Second argument MSG is the exit message of the ri page command." (let ((ri-buffer (if (stringp process) (get-buffer process) (process-buffer process))) (delete-buff nil) (err-mess nil)) (if (null (buffer-name ri-buffer)) ;; deleted buffer (or (stringp process) (set-process-buffer process nil)) (save-excursion (set-buffer ri-buffer) (let ((case-fold-search nil)) (goto-char (point-min)) (cond ((or (looking-at "No \\(ri \\)*entry for") (looking-at "[^\n]*: nothing appropriate$")) (setq err-mess (buffer-substring (point) (progn (end-of-line) (point))) delete-buff t)) ((or (stringp process) (not (and (eq (process-status process) 'exit) (= (process-exit-status process) 0)))) (or (zerop (length msg)) (progn (setq err-mess (concat (buffer-name ri-buffer) ": process " (let ((eos (1- (length msg)))) (if (= (aref msg eos) ?\n) (substring msg 0 eos) msg)))) (goto-char (point-max)) (insert (format "\nprocess %s" msg)))) )) (if delete-buff (kill-buffer ri-buffer) ; (if Man-fontify-ripage-flag ; (Man-fontify-ripage) ; (Man-cleanup-ripage)) (run-hooks 'ri-cooked-hook) (set-buffer-modified-p nil) )) ;; Restore case-fold-search before calling ;; Man-notify-when-ready because it may switch buffers. (if (not delete-buff) (ri-notify-when-ready ri-buffer)) (if err-mess (error err-mess)) ))))