青木です。

「URIっぽい文字列にマッチする正規表現」を返す機能が URI に
欲しいのですが、どうでしょうか。現在でも URI.extract で
それっぽい文字列を取り出すことはできますが、sub したくなった
のです。以下に使いたくなったコードの例を引用します。

  WikiName = /\b(?:[A-Z][a-z0-9]+){2,}\b/n
  ExplicitLink = /\[\[.*?\]\]/e   # FIXME: `e' does not work
  schemes = %w( http ftp )
     # ↓これをなくしたい
  SeemsURL = /\b(?=#{Regexp.union(*schemes)}:)#{URI::PATTERN::X_ABS_URI}/xn
  NeedESC = /[&"<>]/

  def convert(str)
    esctable = TextUtils::ESC
    cgi_href = escape_html(@config.cgi_url)
    str.gsub(/(#{NeedESC})|(#{WikiName})|(#{ExplicitLink})|(#{SeemsURL})/on) {
      # ブロックは省略
    }
  end

[考えられる問題点]

  * 現在は内部で正規表現を使っているからよいが、
    将来に実装が変わったら提供するのが難しくなる
  * 括弧の数で (ユーザが) はまるかもしれない

試みにパッチを書いたので添付しておきます。インターフェイスは
extract に合わせて URI.regexp(schemes = nil) としました。
-------------------------------------------------------------------
青木峰郎


Index: lib/uri/common.rb
===================================================================
RCS file: /src/ruby/lib/uri/common.rb,v
retrieving revision 1.10
diff -u -p -r1.10 common.rb
--- lib/uri/common.rb	29 Mar 2003 06:39:50 -0000	1.10
+++ lib/uri/common.rb	2 Dec 2003 18:12:48 -0000
@@ -396,28 +396,42 @@ module URI
 --- URI::extract(str[, schemes])
 
 =end
-  def self.extract(str, schemes = [])
-    urls = []
-    regexp = ABS_URI_REF
-    unless schemes.empty?
-      regexp = Regexp.new('(?=' + schemes.collect{|s| 
-			    Regexp.quote(s + ':')
-			  }.join('|') + ')' + PATTERN::X_ABS_URI, 
-			  Regexp::EXTENDED, 'N')
+  def self.extract(str, schemes = nil, &block)
+    if block_given?
+      str.scan(regexp(schemes)) { yield $& }
+      nil
+    else
+      result = []
+      str.scan(regexp(schemes)) { result.push $& }
+      result
     end
+  end
+
+=begin
+
+--- URI::regexp([match_schemes])
 
-    str.scan(regexp) {
-      if block_given?
-	yield($&)
-      else
-	urls << $&
+    Returns a Regexp object which matches to URI-like strings.
+    If MATCH_SCHEMES given, resulting regexp matches to URIs
+    whose scheme is one of the MATCH_SCHEMES.
+
+      # extract first URI from html_string
+      html_string.slice(URI.regexp)
+
+      # remove ftp URIs
+      html_string.sub(URI.regexp(['ftp'])
+
+      # You do NOT rely on the number of parentheses
+      html_string.scan(URI.regexp) do |*matches|
+        p $&
       end
-    }
 
-    if block_given?
-      return nil
+=end
+  def self.regexp(schemes = nil)
+    unless schemes
+      ABS_URI_REF
     else
-      return urls
+      /(?=#{Regexp.union(*schemes)}:)#{PATTERN::X_ABS_URI}/xn
     end
   end
 
Index: test/uri/test_common.rb
===================================================================
RCS file: /ruby/ruby/test/uri/test_common.rb,v
retrieving revision 1.2
diff -u -p -r1.2 test_common.rb
--- test/uri/test_common.rb	5 Oct 2003 06:09:26 -0000	1.2
+++ test/uri/test_common.rb	2 Dec 2003 18:12:49 -0000
@@ -29,6 +29,20 @@ class TestCommon < Test::Unit::TestCase
     assert_equal(['From:', 'mailto:xxx / xxx.xxx.xxx]'].sort, 
 		 URI.extract('From: XXX [mailto:xxx / xxx.xxx.xxx]').sort)
   end
+
+  def test_regexp
+    assert_instance_of Regexp, URI.regexp
+    assert_instance_of Regexp, URI.regexp(['http'])
+    assert_equal URI.regexp, URI.regexp
+    assert_equal 'http://', 'x http:// x'.slice(URI.regexp)
+    assert_equal 'http://', 'x http:// x'.slice(URI.regexp(['http']))
+    assert_equal 'http://', 'x http:// x ftp://'.slice(URI.regexp(['http']))
+    assert_equal nil, 'http://'.slice(URI.regexp([]))
+    assert_equal nil, ''.slice(URI.regexp)
+    assert_equal nil, 'xxxx'.slice(URI.regexp)
+    assert_equal nil, ':'.slice(URI.regexp)
+    assert_equal 'From:', 'From:'.slice(URI.regexp)
+  end
 end