wikilink-converter 0.1.0
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/.document +5 -0
- data/.rspec +1 -0
- data/.travis.yml +1 -0
- data/.yardopts +1 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +57 -0
- data/Guardfile +21 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +14 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/lib/wikilink/converter/namespace.rb +58 -0
- data/lib/wikilink/converter/site.rb +99 -0
- data/lib/wikilink/converter/sites/ruby_china.rb +43 -0
- data/lib/wikilink/converter/sites/wikipedia.rb +20 -0
- data/lib/wikilink/converter/utils.rb +37 -0
- data/lib/wikilink/converter.rb +173 -0
- data/lib/wikilink-converter.rb +1 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/matchers.rb +43 -0
- data/spec/wikilink/converter/namespace_spec.rb +113 -0
- data/spec/wikilink/converter/site_spec.rb +334 -0
- data/spec/wikilink/converter/sites/ruby_china_spec.rb +31 -0
- data/spec/wikilink/converter/sites/wikipedia_spec.rb +25 -0
- data/spec/wikilink/converter_spec.rb +177 -0
- data/wikilink-converter.gemspec +98 -0
- metadata +208 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm: 1.9.2
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-m markdown
|
data/Gemfile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
require 'rbconfig'
|
3
|
+
HOST_OS = RbConfig::CONFIG['host_os']
|
4
|
+
|
5
|
+
# Add dependencies to develop your gem here.
|
6
|
+
# Include everything needed to run rake, tests, features, etc.
|
7
|
+
group :development do
|
8
|
+
gem 'rspec'
|
9
|
+
gem 'yard'
|
10
|
+
gem 'bundler'
|
11
|
+
gem 'jeweler'
|
12
|
+
gem 'simplecov'
|
13
|
+
gem 'guard'
|
14
|
+
gem 'guard-rspec'
|
15
|
+
gem 'guard-bundler'
|
16
|
+
gem 'guard-yard'
|
17
|
+
gem 'redcarpet'
|
18
|
+
|
19
|
+
if HOST_OS =~ /linux/i
|
20
|
+
gem 'rb-inotify', require: false
|
21
|
+
gem 'libnotify', require: false
|
22
|
+
end
|
23
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
ffi (1.0.11)
|
6
|
+
git (1.2.5)
|
7
|
+
guard (0.8.8)
|
8
|
+
thor (~> 0.14.6)
|
9
|
+
guard-bundler (0.1.3)
|
10
|
+
bundler (>= 1.0.0)
|
11
|
+
guard (>= 0.2.2)
|
12
|
+
guard-rspec (0.5.8)
|
13
|
+
guard (>= 0.8.4)
|
14
|
+
guard-yard (1.0.2)
|
15
|
+
guard (>= 0.2.2)
|
16
|
+
yard (>= 0.7.0)
|
17
|
+
jeweler (1.6.4)
|
18
|
+
bundler (~> 1.0)
|
19
|
+
git (>= 1.2.5)
|
20
|
+
rake
|
21
|
+
libnotify (0.5.9)
|
22
|
+
multi_json (1.0.4)
|
23
|
+
rake (0.9.2.2)
|
24
|
+
rb-inotify (0.8.8)
|
25
|
+
ffi (>= 0.5.0)
|
26
|
+
redcarpet (1.17.2)
|
27
|
+
rspec (2.7.0)
|
28
|
+
rspec-core (~> 2.7.0)
|
29
|
+
rspec-expectations (~> 2.7.0)
|
30
|
+
rspec-mocks (~> 2.7.0)
|
31
|
+
rspec-core (2.7.1)
|
32
|
+
rspec-expectations (2.7.0)
|
33
|
+
diff-lcs (~> 1.1.2)
|
34
|
+
rspec-mocks (2.7.0)
|
35
|
+
simplecov (0.5.4)
|
36
|
+
multi_json (~> 1.0.3)
|
37
|
+
simplecov-html (~> 0.5.3)
|
38
|
+
simplecov-html (0.5.3)
|
39
|
+
thor (0.14.6)
|
40
|
+
yard (0.7.4)
|
41
|
+
|
42
|
+
PLATFORMS
|
43
|
+
ruby
|
44
|
+
|
45
|
+
DEPENDENCIES
|
46
|
+
bundler
|
47
|
+
guard
|
48
|
+
guard-bundler
|
49
|
+
guard-rspec
|
50
|
+
guard-yard
|
51
|
+
jeweler
|
52
|
+
libnotify
|
53
|
+
rb-inotify
|
54
|
+
redcarpet
|
55
|
+
rspec
|
56
|
+
simplecov
|
57
|
+
yard
|
data/Guardfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec', :version => 2, :cli => '--color --format doc' do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
guard 'bundler' do
|
13
|
+
watch('Gemfile')
|
14
|
+
# Uncomment next line if Gemfile contain `gemspec' command
|
15
|
+
# watch(/^.+\.gemspec/)
|
16
|
+
end
|
17
|
+
|
18
|
+
guard 'yard' do
|
19
|
+
watch(%r{lib/.+\.rb})
|
20
|
+
watch('README.markdown')
|
21
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Ian Yang
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
wikilink-converter
|
2
|
+
==================
|
3
|
+
|
4
|
+
Description goes here.
|
5
|
+
|
6
|
+
[](http://travis-ci.org/doitian/wikilink-converter)
|
7
|
+
|
8
|
+
|
9
|
+
Copyright
|
10
|
+
---------
|
11
|
+
|
12
|
+
Copyright (c) 2011 Ian Yang. See LICENSE.txt for
|
13
|
+
further details.
|
14
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "wikilink-converter"
|
18
|
+
gem.homepage = "http://github.com/doitian/wikilink-converter"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{convert [[WikiLink]] to <a>}
|
21
|
+
gem.description = %Q{convert [[WikiLink]] to <a>}
|
22
|
+
gem.email = "me@iany.me"
|
23
|
+
gem.authors = ["Ian Yang"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
task :cov do
|
35
|
+
system 'bundle exec rake COVERAGE=1 spec'
|
36
|
+
end
|
37
|
+
|
38
|
+
task :default => :spec
|
39
|
+
|
40
|
+
require 'yard'
|
41
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'wikilink/converter/utils'
|
2
|
+
|
3
|
+
module Wikilink
|
4
|
+
class Converter
|
5
|
+
# Namespace converter
|
6
|
+
class Namespace
|
7
|
+
include LinkHelper
|
8
|
+
|
9
|
+
DEFAULT_NAME = ''
|
10
|
+
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def config(&block)
|
18
|
+
@block = block
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(colon, path, name, current_page)
|
23
|
+
if @block
|
24
|
+
instance_exec(colon, path, name, current_page, &@block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Default < Namespace
|
29
|
+
def run(colon, path, name, current_page)
|
30
|
+
return super if @block
|
31
|
+
|
32
|
+
path, fragment = path.split('#', 2)
|
33
|
+
path, query = path.split('?', 2)
|
34
|
+
|
35
|
+
fragment = '#' + fragment if fragment
|
36
|
+
query = '?' + query if query
|
37
|
+
|
38
|
+
url = to_url(path, fragment, query)
|
39
|
+
|
40
|
+
link_to(name, url, :class => html_class)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_url(path, fragment, query)
|
44
|
+
if path.nil? || path.empty?
|
45
|
+
[query, fragment].join
|
46
|
+
else
|
47
|
+
[options[:prefix], path, options[:suffix], query, fragment].join
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
def html_class
|
54
|
+
[options[:class], ('external' if options[:external])]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'wikilink/converter/namespace'
|
2
|
+
require 'wikilink/converter/utils'
|
3
|
+
|
4
|
+
module Wikilink
|
5
|
+
class Converter
|
6
|
+
# Site converter
|
7
|
+
class Site
|
8
|
+
include ArgumentExtractor
|
9
|
+
CURRENT_SITE_NAME = ''
|
10
|
+
DEFAULT_NAMESPACE = ::Wikilink::Converter::Namespace::DEFAULT_NAME
|
11
|
+
|
12
|
+
attr_reader :options
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@options = options
|
16
|
+
is_current_site = @options[:name].to_s == CURRENT_SITE_NAME
|
17
|
+
@options[:external] = !is_current_site unless @options.key?(:external)
|
18
|
+
@options[:prefix] ||= '/' if is_current_site
|
19
|
+
@namespace_converters = {}
|
20
|
+
|
21
|
+
on_namespace(DEFAULT_NAMESPACE)
|
22
|
+
|
23
|
+
yield self if block_given?
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_namespace(*args, &block)
|
27
|
+
namespace, converter, options = extract_arguments(*args)
|
28
|
+
namespace = DEFAULT_NAMESPACE if namespace.to_s.empty?
|
29
|
+
|
30
|
+
converter ||= namespace_converter(namespace)
|
31
|
+
if converter.nil?
|
32
|
+
# do not add new handler if there's a instance method can handle it,
|
33
|
+
# except that user passes a block to override it
|
34
|
+
if block || instance_method_converter(namespace).nil?
|
35
|
+
converter = ::Wikilink::Converter::Namespace
|
36
|
+
converter = ::Wikilink::Converter::Namespace::Default if !block && namespace == DEFAULT_NAMESPACE
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
if converter.is_a?(Class)
|
41
|
+
options = default_options_for_namespace.merge(options)
|
42
|
+
options[:name] ||= namespace
|
43
|
+
converter = converter.new(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
if converter.respond_to?(:config) && block
|
47
|
+
converter.config(&block)
|
48
|
+
end
|
49
|
+
|
50
|
+
set_namespace_converter namespace, converter if converter
|
51
|
+
self
|
52
|
+
end
|
53
|
+
alias_method :on, :on_namespace
|
54
|
+
alias_method :namespace, :on_namespace
|
55
|
+
|
56
|
+
def on_default_namespace(*args, &block)
|
57
|
+
on_namespace(DEFAULT_NAMESPACE, *args, &block)
|
58
|
+
end
|
59
|
+
alias_method :default_namespace, :on_default_namespace
|
60
|
+
|
61
|
+
def run(colon, namespace, path, name, current_page)
|
62
|
+
if converter = namespace_converter(namespace)
|
63
|
+
converter.run(colon, path, name, current_page)
|
64
|
+
elsif converter = instance_method_converter(namespace)
|
65
|
+
converter.call(colon, path, name, current_page)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
def html_class
|
71
|
+
[options[:class], ('external' if options[:external])]
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def namespace_converter(namespace)
|
76
|
+
namespace = namespace.to_s.downcase
|
77
|
+
@namespace_converters[namespace]
|
78
|
+
end
|
79
|
+
|
80
|
+
def instance_method_converter(namespace)
|
81
|
+
namespace = namespace.to_s.downcase
|
82
|
+
try_message = "on_namespace_#{namespace}".to_sym
|
83
|
+
method(try_message) if respond_to?(try_message)
|
84
|
+
end
|
85
|
+
|
86
|
+
def set_namespace_converter(namespace, converter)
|
87
|
+
@namespace_converters[namespace] = converter
|
88
|
+
end
|
89
|
+
|
90
|
+
def default_options_for_namespace
|
91
|
+
opts = @options.dup
|
92
|
+
opts.delete :name
|
93
|
+
opts[:site_name] = @options[:name]
|
94
|
+
|
95
|
+
opts
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'wikilink/converter/site'
|
2
|
+
require 'wikilink/converter/utils'
|
3
|
+
|
4
|
+
module Wikilink
|
5
|
+
class Converter
|
6
|
+
module Sites
|
7
|
+
class RubyChina < Wikilink::Converter::Site
|
8
|
+
include Wikilink::Converter::LinkHelper
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
if options[:name] == CURRENT_SITE
|
12
|
+
options[:domain] ||= '/'
|
13
|
+
else
|
14
|
+
options[:domain] ||= 'http://ruby-china.org/'
|
15
|
+
end
|
16
|
+
options[:prefix] = "#{options[:domain]}wiki/"
|
17
|
+
|
18
|
+
super(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_namespace_topic(colon, path, name, current_page)
|
22
|
+
path = "#{options[:domain]}topics/#{path}"
|
23
|
+
link_to name, path, :class => html_class
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_namespace_node(colon, path, name, current_page)
|
27
|
+
path = "#{options[:domain]}topics/node#{path}"
|
28
|
+
link_to name, path, :class => html_class
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class RubyTaiwan < RubyChina
|
33
|
+
def initialize(options = {})
|
34
|
+
if options[:name] != CURRENT_SITE
|
35
|
+
options[:domain] ||= 'http://ruby-taiwan.org/'
|
36
|
+
end
|
37
|
+
|
38
|
+
super(options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'wikilink/converter/site'
|
2
|
+
|
3
|
+
module Wikilink
|
4
|
+
class Converter
|
5
|
+
module Sites
|
6
|
+
class Wikipedia < Wikilink::Converter::Site
|
7
|
+
def initialize(options = {})
|
8
|
+
options[:lang] ||= 'en'
|
9
|
+
if options[:name] == CURRENT_SITE
|
10
|
+
options[:prefix] ||= '/wiki/'
|
11
|
+
else
|
12
|
+
options[:prefix] ||= "http://#{options[:lang]}.wikipedia.org/wiki/"
|
13
|
+
end
|
14
|
+
|
15
|
+
super(options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Wikilink
|
4
|
+
class Converter
|
5
|
+
module ArgumentExtractor
|
6
|
+
def extract_arguments(*args)
|
7
|
+
options = {}
|
8
|
+
options = args.pop if args.last.is_a?(Hash)
|
9
|
+
name = args.shift
|
10
|
+
|
11
|
+
if name.nil? || name.is_a?(String) || name.is_a?(Symbol)
|
12
|
+
name = name.to_s
|
13
|
+
converter = args.shift
|
14
|
+
else
|
15
|
+
converter = name
|
16
|
+
name = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
throw ArgumentError, "too many arguments" unless args.empty?
|
20
|
+
|
21
|
+
[name, converter, options]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module LinkHelper
|
26
|
+
def link_to(name, url, attributes = {})
|
27
|
+
attributes[:class] = Array(attributes[:class]).flatten.join(' ').split.uniq.join(' ')
|
28
|
+
attributes.delete(:class) if attributes[:class].empty?
|
29
|
+
attributes = attributes.inject('') do |memo, (key, value)|
|
30
|
+
memo + key.to_s + '="' + CGI.escape_html(value) + '" '
|
31
|
+
end
|
32
|
+
|
33
|
+
"<a #{attributes}href=\"#{CGI.escape_html url}\">#{CGI.escape_html name}</a>"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'wikilink/converter/site'
|
3
|
+
require 'wikilink/converter/utils'
|
4
|
+
|
5
|
+
module Wikilink
|
6
|
+
# Convert `[[Wikilink]]` to HTML
|
7
|
+
#
|
8
|
+
# The real work is handed over to registered handlers through {#on}.
|
9
|
+
#
|
10
|
+
# The parsing rules
|
11
|
+
# -----------------
|
12
|
+
#
|
13
|
+
# - `[[Wikilink]]` should be in one line, otherwise it is ignored.
|
14
|
+
# - `\[[Wikilink]]` is escaped and converted to `[[Wikilink]]`.
|
15
|
+
# - `[[action:arg]]` triggers handler registered on **action**. **arg** is passed as
|
16
|
+
# first argument to the handler. **arg** can contains colons.
|
17
|
+
# - `[[:action:arg]]` is the same with `[[action:arg]]`, except that `true` is
|
18
|
+
# passed as the second argument to handler to indicate a prefix colon
|
19
|
+
# exists. It is useful for resources like image, where no colon version
|
20
|
+
# inserts the image and colon version inserts the link.
|
21
|
+
# - `[[Wikilink]]` is identical with `[[page:Wikilink]]`, i.e., the default
|
22
|
+
# action is **page**.
|
23
|
+
class Converter
|
24
|
+
extend Forwardable
|
25
|
+
include ArgumentExtractor
|
26
|
+
CURRENT_SITE = ::Wikilink::Converter::Site::CURRENT_SITE_NAME
|
27
|
+
|
28
|
+
# Setup a converter. Handlers can be registered in block directly. If no
|
29
|
+
# handler is registered on **page**, a default handler
|
30
|
+
# Wikilink::Converter::Page is created with the given `options`.
|
31
|
+
#
|
32
|
+
# @param [Hash] options options for Wikilink::Converter::Page
|
33
|
+
def initialize(options = {})
|
34
|
+
@site_converts = {}
|
35
|
+
@action_handlers = {}
|
36
|
+
@options = options
|
37
|
+
|
38
|
+
on_site(CURRENT_SITE, @options)
|
39
|
+
yield self if block_given?
|
40
|
+
end
|
41
|
+
|
42
|
+
def run(text, current_page = nil)
|
43
|
+
text.gsub(/(^|.)\[\[(.*?[^:])\]\]/) do |match|
|
44
|
+
prefix, inner = $1, $2.strip
|
45
|
+
if prefix == '\\'
|
46
|
+
match[1..-1]
|
47
|
+
else
|
48
|
+
if inner.start_with?(':')
|
49
|
+
colon = ':'
|
50
|
+
inner = inner[1..-1]
|
51
|
+
end
|
52
|
+
link, name = inner.split('|', 2)
|
53
|
+
path, namespace, site = link.split(':', 3).reverse
|
54
|
+
|
55
|
+
if site.to_s.empty? && !namespace.to_s.empty?
|
56
|
+
# if namespace is a valid site name, use it as site
|
57
|
+
if site_converter(namespace)
|
58
|
+
site = namespace
|
59
|
+
namespace = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if name.to_s.empty?
|
64
|
+
name = resolve_name(inner, current_page)
|
65
|
+
end
|
66
|
+
|
67
|
+
# ignore malformed wikilink
|
68
|
+
if valid?(site, namespace, path)
|
69
|
+
result = convert_link(colon, site, namespace, path, name, current_page)
|
70
|
+
result ? ($1 + result) : match
|
71
|
+
else
|
72
|
+
match
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def execute(text)
|
79
|
+
text.gsub(/(^|.)\{\{(.*?[^:])\}\}/) do |match|
|
80
|
+
prefix, inner = $1, $2.strip
|
81
|
+
if prefix == '\\'
|
82
|
+
match[1..-1]
|
83
|
+
else
|
84
|
+
action, arg = inner.split(':', 2)
|
85
|
+
result = convert_action(action, arg)
|
86
|
+
result ? ($1 + result) : match
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def_delegator :@current_site_converter, :on_namespace
|
92
|
+
alias_method :on, :on_namespace
|
93
|
+
alias_method :namespace, :on_namespace
|
94
|
+
|
95
|
+
def_delegator :@current_site_converter, :on_default_namespace
|
96
|
+
alias_method :default_namespace, :on_default_namespace
|
97
|
+
|
98
|
+
def on_site(*args)
|
99
|
+
site, converter, options = extract_arguments(*args)
|
100
|
+
options = @options.merge(options)
|
101
|
+
site = CURRENT_SITE if site.to_s.empty?
|
102
|
+
|
103
|
+
converter ||= site_converter(site) || Wikilink::Converter::Site
|
104
|
+
if converter.is_a?(Class)
|
105
|
+
options[:name] ||= site
|
106
|
+
converter = converter.new(options)
|
107
|
+
end
|
108
|
+
|
109
|
+
yield converter if block_given?
|
110
|
+
|
111
|
+
set_site_converter site, converter
|
112
|
+
self
|
113
|
+
end
|
114
|
+
alias_method :site, :on_site
|
115
|
+
|
116
|
+
def on_current_site(*args, &block)
|
117
|
+
on_site(CURRENT_SITE, *args, &block)
|
118
|
+
end
|
119
|
+
alias_method :current_site, :on_current_site
|
120
|
+
|
121
|
+
def on_action(action, &block)
|
122
|
+
@action_handlers[action.to_s.downcase] = block
|
123
|
+
self
|
124
|
+
end
|
125
|
+
alias_method :action, :on_action
|
126
|
+
|
127
|
+
|
128
|
+
private
|
129
|
+
def site_converter(site)
|
130
|
+
site = site.to_s.downcase
|
131
|
+
site == CURRENT_SITE ? @current_site_converter : @site_converts[site]
|
132
|
+
end
|
133
|
+
|
134
|
+
def set_site_converter(site, converter)
|
135
|
+
site = site.to_s.downcase
|
136
|
+
if site == CURRENT_SITE
|
137
|
+
@current_site_converter = converter
|
138
|
+
else
|
139
|
+
@site_converts[site] = converter
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def convert_action(action, argument)
|
144
|
+
handler = @action_handlers[action.to_s.downcase]
|
145
|
+
handler.call(argument) if handler
|
146
|
+
end
|
147
|
+
|
148
|
+
def convert_link(colon, site, namespace, path, name, current_page)
|
149
|
+
converter = site_converter(site)
|
150
|
+
converter.run(colon, namespace, path, name, current_page) if converter
|
151
|
+
end
|
152
|
+
|
153
|
+
# TODO: relative
|
154
|
+
# TODO: ruby (computer) -> ruby
|
155
|
+
# TODO: Shanghai, China -> Shanghai
|
156
|
+
def resolve_name(inner_text, current_path)
|
157
|
+
if inner_text.end_with?('|')
|
158
|
+
inner_text.chop.chomp('/').split(%r{[:/]}, 2).last
|
159
|
+
else
|
160
|
+
inner_text
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
INVALID_NAME_REGEXP = /[^[[:alnum:]][[:blank]]_-]/
|
165
|
+
INVALID_PATH_REGEXP = /^[\[\]]/
|
166
|
+
def valid?(site, namespace, path)
|
167
|
+
return false if site =~ INVALID_NAME_REGEXP
|
168
|
+
return false if namespace =~ INVALID_NAME_REGEXP
|
169
|
+
return false if path =~ INVALID_PATH_REGEXP
|
170
|
+
return true
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'wikilink/converter'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
if ENV['COVERAGE']
|
4
|
+
require 'simplecov'
|
5
|
+
SimpleCov.start do
|
6
|
+
add_filter "/spec/"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rspec'
|
11
|
+
require 'wikilink-converter'
|
12
|
+
|
13
|
+
# Requires supporting files with custom matchers and macros, etc,
|
14
|
+
# in ./support/ and its subdirectories.
|
15
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
16
|
+
|
17
|
+
RSpec.configure do |config|
|
18
|
+
|
19
|
+
end
|