Autolink URLs like http://localhost:8000 (anything that has a schema like http:// and looks like a URL), in addition to internet URLs.

This commit is contained in:
Alexey Verkhovsky 2005-01-20 21:34:27 +00:00
parent b74244ee3f
commit dce2af06aa
4 changed files with 91 additions and 42 deletions

View file

@ -19,8 +19,7 @@ class URIChunk < Chunk::Abstract
include URI::REGEXP::PATTERN include URI::REGEXP::PATTERN
# this condition is to get rid of pesky warnings in tests # this condition is to get rid of pesky warnings in tests
unless defined? URI_CHUNK_CONSTANTS_DEFINED unless defined? URIChunk::INTERNET_URI_REGEXP
URI_CHUNK_CONSTANTS_DEFINED = true
GENERIC = '(?:aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org)' GENERIC = '(?:aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org)'
COUNTRY = '(?:au|at|be|ca|ch|de|dk|fr|hk|in|ir|it|jp|nl|no|pt|ru|se|sw|tv|tw|uk|us)' COUNTRY = '(?:au|at|be|ca|ch|de|dk|fr|hk|in|ir|it|jp|nl|no|pt|ru|se|sw|tv|tw|uk|us)'
@ -45,27 +44,28 @@ class URIChunk < Chunk::Abstract
FRAGMENT = "#{URIC_NO_ENDING}*" FRAGMENT = "#{URIC_NO_ENDING}*"
# DOMLABEL is defined in the ruby uri library, TLDS is defined above # DOMLABEL is defined in the ruby uri library, TLDS is defined above
FULL_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}" INTERNET_HOSTNAME = "(?:#{DOMLABEL}\\.)+#{TLDS}"
# Correct a typo bug in ruby 1.8.x lib/uri/common.rb # Correct a typo bug in ruby 1.8.x lib/uri/common.rb
PORT = '\\d*' PORT = '\\d*'
URI_PATTERN = INTERNET_URI =
"(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1) "(?:(#{SCHEME}):/{0,2})?" + # Optional scheme: (\1)
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2) "(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
"(#{FULL_HOSTNAME})" + # Mandatory hostname (\3) "(#{INTERNET_HOSTNAME})" + # Mandatory hostname (\3)
"(?::(#{PORT}))?" + # Optional :port (\4) "(?::(#{PORT}))?" + # Optional :port (\4)
"(#{ABS_PATH})?" + # Optional absolute path (\5) "(#{ABS_PATH})?" + # Optional absolute path (\5)
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6) "(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7) "(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
end
TEXTILE_SYNTAX_PREFIX = '(!)?' TEXTILE_SYNTAX_PREFIX = '(!)?'
URI_PATTERN_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + URI_PATTERN, Regexp::EXTENDED, 'N') INTERNET_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + INTERNET_URI, Regexp::EXTENDED, 'N')
end
def self.pattern def self.pattern
URI_PATTERN_REGEXP INTERNET_URI_REGEXP
end end
attr_reader :uri, :scheme, :user, :host, :port, :path, :query, :fragment, :link_text attr_reader :uri, :scheme, :user, :host, :port, :path, :query, :fragment, :link_text
@ -149,3 +149,31 @@ class URIChunk < Chunk::Abstract
end end
end end
# uri with mandatory scheme but less restrictive hostname, like
# http://localhost:2500/blah.html
class LocalURIChunk < URIChunk
unless defined? LocalURIChunk::LOCAL_URI_REGEXP
# hostname can be just a simple word like 'localhost'
ANY_HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
# The basic URI expression as a string
# Scheme and hostname are mandatory
LOCAL_URI =
"(?:(#{SCHEME})://)+" + # Mandatory scheme:// (\1)
"(?:(#{USERINFO})@)?" + # Optional userinfo@ (\2)
"(#{ANY_HOSTNAME})" + # Mandatory hostname (\3)
"(?::(#{PORT}))?" + # Optional :port (\4)
"(#{ABS_PATH})?" + # Optional absolute path (\5)
"(?:\\?(#{QUERY}))?" + # Optional ?query (\6)
"(?:\\#(#{FRAGMENT}))?" # Optional #fragment (\7)
LOCAL_URI_REGEXP = Regexp.new(TEXTILE_SYNTAX_PREFIX + LOCAL_URI, Regexp::EXTENDED, 'N')
end
def self.pattern
LOCAL_URI_REGEXP
end
end

View file

@ -39,7 +39,9 @@ require 'chunks/nowiki'
# UPDATED: 22nd May 2004 # UPDATED: 22nd May 2004
class WikiContent < String class WikiContent < String
PRE_ENGINE_ACTIONS = [ NoWiki, Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word ] PRE_ENGINE_ACTIONS = [ NoWiki, Category, Include,
URIChunk, LocalURIChunk,
WikiChunk::Link, WikiChunk::Word ]
POST_ENGINE_ACTIONS = [ Literal::Pre, Literal::Tags ] POST_ENGINE_ACTIONS = [ Literal::Pre, Literal::Tags ]
DEFAULT_OPTS = { DEFAULT_OPTS = {
:pre_engine_actions => PRE_ENGINE_ACTIONS, :pre_engine_actions => PRE_ENGINE_ACTIONS,

View file

@ -36,37 +36,32 @@ end
# This module is to be included in unit tests that involve matching chunks. # This module is to be included in unit tests that involve matching chunks.
# It provides a easy way to test whether a chunk matches a particular string # It provides a easy way to test whether a chunk matches a particular string
# and any the values of any fields that should be set after a match. # and any the values of any fields that should be set after a match.
class ContentStub < String
attr_reader :chunks, :content
def initialize(str)
super
@chunks = []
end
end
module ChunkMatch module ChunkMatch
# Asserts a number of tests for the given type and text. # Asserts a number of tests for the given type and text.
def match(type, test_text, expected) def match(chunk_type, test_text, expected_chunk_state)
pattern = type.pattern if chunk_type.respond_to? :pattern
assert_match(pattern, test_text) assert_match(chunk_type.pattern, test_text)
pattern =~ test_text # Previous assertion guarantees match end
chunk = type.new($~)
content = ContentStub.new(test_text)
chunk_type.apply_to(content)
# Test if requested parts are correct. # Test if requested parts are correct.
for method_sym, value in expected do expected_chunk_state.each_pair do |a_method, expected_value|
assert_respond_to(chunk, method_sym) assert content.chunks.last.kind_of?(chunk_type)
assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'") assert_respond_to(content.chunks.last, a_method)
assert_equal(expected_value, content.chunks.last.send(a_method.to_sym),
"Wrong #{a_method} value")
end end
end end
end end
module ActionController
class TestResponse
def binary_content
sio = StringIO.new
begin
$stdout = sio
body.call
ensure
$stdout = STDOUT
end
sio.rewind
sio.read
end
end
end

View file

@ -7,8 +7,9 @@ class URITest < Test::Unit::TestCase
include ChunkMatch include ChunkMatch
def test_non_matches def test_non_matches
assert_no_match(URIChunk.pattern, 'There is no URI here') assert_conversion_does_not_apply(URIChunk, 'There is no URI here')
assert_no_match(URIChunk.pattern, 'One gemstone is the garnet:reddish in colour, like ruby') assert_conversion_does_not_apply(URIChunk,
'One gemstone is the garnet:reddish in colour, like ruby')
end end
def test_simple_uri def test_simple_uri
@ -110,9 +111,9 @@ class URITest < Test::Unit::TestCase
end end
def test_non_uri def test_non_uri
assert_no_match(URIChunk.pattern, 'httpd.conf') assert_conversion_does_not_apply URIChunk, 'httpd.conf'
assert_no_match(URIChunk.pattern, 'libproxy.so') assert_conversion_does_not_apply URIChunk, 'libproxy.so'
assert_no_match(URIChunk.pattern, 'ld.so.conf') assert_conversion_does_not_apply URIChunk, 'ld.so.conf'
end end
def test_uri_in_text def test_uri_in_text
@ -152,4 +153,27 @@ class URITest < Test::Unit::TestCase
:query => 'arg=val,') :query => 'arg=val,')
end end
def test_local_urls
# normal
match(LocalURIChunk, 'http://perforce:8001/toto.html',
:scheme => 'http', :host => 'perforce',
:port => '8001', :link_text => 'http://perforce:8001/toto.html')
# in parentheses
match(LocalURIChunk, 'URI (http://localhost:2500) in brackets',
:host => 'localhost', :port => '2500')
match(LocalURIChunk, 'because (as shown at http://perforce:8001) the results',
:host => 'perforce', :port => '8001')
match(LocalURIChunk,
'A wiki (http://localhost:2500/wiki.cgi?WhatIsWiki) page',
:scheme => 'http', :host => 'localhost', :path => '/wiki.cgi',
:port => '2500', :query => 'WhatIsWiki')
end
def assert_conversion_does_not_apply(chunk_type, str)
processed_str = str.dup
URIChunk.apply_to(processed_str)
assert_equal(str, processed_str)
end
end end