Comments
-
Greg Hurrell
Thanks for filing the request, ennen.
Off the top of my head I can't envisage any major technical obstacles to implementing that feature (although I'll be honest with you and admit that I'm unlikely to be able to implement it in the near future because I have a fair bit of other stuff on my plate right now).
Might I suggest that an excellent first step would be for you to look at the spec suite and write some specs describing the kind of behaviour you expect to see?
Remember that the links can also take optional link text (eg. [[article|the foo article]]) so you'd want to specify how it should behave for that kind of link (perhaps the block should take two parameters?) as well as the standard [[foo]] kind of link.
I think it's important for this to be fairly well thought-out before proceeding because once you start allowing this kind of callback then it's quite possible that people might want to use it for things you didn't envisage in the first place. (Should the block be returning
false
or should it be returning a string containing the CSS class name?)I'd be happy to bounce some ideas around with you if you want to tell me a little bit more about your usage scenario and exactly how and why you'd want to use this kind of feature. (Although I'm guessing that you're wanting to colorize links to articles that don't exist yet...)
-
ennen
There's no rush, and I'll be glad to help however I can.
I'm not familiar with those spec files, however. Is it a DSL? If you could point me at some documentation - I'm always ready to learn something new. Then I'll take a swing at writing some myself.
I was thinking that the block would contain some test like
Page.find_by_title link_text
and if that were false (or nil, I suppose) the resulting link would be generated withclass="redlink"
- the specific class might be set in a similar fashion tointernal_link_prefix
, et. al.However, I agree that whatever aproach we would take should be as robust as possible - but I also imagine that would entail more signifigant alterations to the parser. I'm also not familiar enough with the internals to really suggest very signifigant changes.
One idea that does come to mind is passing in an options hash to the parser upon initialization to achieve similar effect - which could be passed in again when calling the
parse
method, to allow for simple runtime modifcation of the same options. Again, however, I'm not sure if this is the best approach - or even how feasible it would be.I'm using the gem in a Rails-based wiki I'm developing, geared towards blogging and personal information management. Really very similar to MediaWiki, but on a smaller scale, plus a few small conceptual tweaks.
At a minimum I'm just looking to colorize links to non-existant pages - but I'd also like to be able to do things like suppress link generation under certain conditions. That way, for example, an unpriveledged user could be prevented from encountering to links to non-existant pages at all. The same would be achieved by simply preventing access to the new page action, but I feel that the former would be more elegant.
Anyway, that's probably plenty of ideas from me for now - let me know what you think, and sorry for the late reply; I've been pretty engrossed in this little project for the past couple weeks. Thanks for your time.
-
ennen
Nevermind - the spec files are RSpec, correct? I'll go ahead and look into it, and see if I can produce some for the behaviour I'm looking for.
-
Greg Hurrell
Yep, they're RSpec.
However, I agree that whatever aproach we would take should be as robust as possible - but I also imagine that would entail more signifigant alterations to the parser.
If it really is just executing a chunk of code passed in as a Proc or a block, then it probably won't be too invasive. I think it all depends on just how much flexibility and control you want to delegate out like that.
The very simplest case ("execute this block and if it returns
true
or a class string then use that as the link class") would be fairly straightforward, I think. Other more complex behaviour starts to look nasty though; probably to the point that I would suggest moving it out into either a pre or a post-processing phase (which you could write in Ruby). Post-processing is fairly easy because by the time the wikitext markup has gone through the translator you know that you're guaranteed to get a valid HTML snippet out of it, and you don't have to worry about dodgy markup. The appeal of the separate phase is that it keeps the wikitext codebase simple, clean and fast; it really just concerns itself with the task of transforming wikitext markup into HTML.I'm also not familiar enough with the internals to really suggest very signifigant changes.
I wouldn't worry about the internals at all. The important thing is to have a good specification of the desired behaviour.
One idea that does come to mind is passing in an options hash to the parser upon initialization to achieve similar effect - which could be passed in again when calling the parse method, to allow for simple runtime modifcation of the same options. Again, however, I'm not sure if this is the best approach - or even how feasible it would be.
Sounds reasonable. I know that in my own use of the module (this site), where I use it for the wiki, my blog, the forums, this issue tracker and basically everything on the site, I tend to call the
.w
method all over the place in my views and helpers. It would be pretty clumsy to pass a block or Proc in at every one of those sites; I'd be much more likely to want to configure it once and only once when the parser was instantiated.And obviously, seeing as wiki articles can be very long (thousands of words) and contain many, many links, you wouldn't want to do a database query for every single link that you saw. You'd probably want to do one query which pulled all the titles into a (memcached) cache and then check against the cache.
I'm using the gem in a Rails-based wiki I'm developing, geared towards blogging and personal information management. Really very similar to MediaWiki, but on a smaller scale, plus a few small conceptual tweaks.
If you ever put it up anywhere public please do let me know as I like to see how people are using the module.
At a minimum I'm just looking to colorize links to non-existant pages - but I'd also like to be able to do things like suppress link generation under certain conditions.
That's a little bit more complicated then, I think. Seeing as you're no longer just talking about adding or modifying a CSS class, but actually suppressing an entire element. What do you want to do? Turn "see the [[foo bar]] page" into "see the foo bar page"? And what would the block or Proc (or method) have to return to make this happen?
That way, for example, an unpriveledged user could be prevented from encountering to links to non-existant pages at all. The same would be achieved by simply preventing access to the new page action, but I feel that the former would be more elegant.
My personal take on that one, and the one I use on this site, is that a link to a non-existent page like this one will take you to the "new page action", and if you're not logged in as an admin user you should be invited to authenticate as one. As an admin user that's pretty convenient. For non-admin users I think it could possibly be confusing, but your proposed "red links" feature would largely address that I think.
Another big incentive to not introduce that kind of conditional behaviour is that if you start displaying different content for admin and non-admin users then you can no longer cache your wiki pages, because you no longer have unchanging content for all users.
-
Greg Hurrell
Bah, I'm silly. I don't even know the behaviour of my own website! Non-admin users clicking on non-existent page links like this one don't get asked to authenticate, they just see a "Requested article not found" flash. The "red links" feature would be a welcome addition to make the site more navegable in any case.
-
Greg Hurrell
Summary changed:
- From: requesting support for redlinks in wikitext
- To: Support for "redlinks" in wikitext
-
Greg Hurrell
Just thought of another use case. In this issue tracker, for example, text like "ticket #1268" turns into hyperlinks (eg. ticket #1268). If the block had the means of returning not only a class but also a "title" attribute, then I could make a tool-tip showing the issue summary appear when hovering the mouse over such a link.
-
Greg Hurrell
Just wanted to add one comment here because I git a private message from someone via GitHub who also wants to see this.
He said he'd tried pre-processing the links before feeding the markup into the wikitext extension, but that obviously didn't work because everything got escaped.
The post-processing approach would work and it's probably worth noting that it wouldn't be too nasty to implement seeing as any HTML that comes out of the extension is guaranteed to be well-formed so you don't have to worry about unclosed
<a href="foo">
tags or anything else. In fact, if you look at the output all such links will always be exactly the same, with the attributes in the same order, and so quite easy to process with simple regular expressions.I myself still haven't decided whether a post-processing pass or an during-translation callback is the way to go here.
-
anonymous
Hi, I'm "someone via GitHub" :) I'm thinking this would be a sensible implementation: http://pastie.org/545134
When it encounters
foo
, "foo" is passed to the proc. When it encountersfoo
, "foo" is also passed to the proc. The custom title is irrelevant when you want to determine if a page link is a redlink, since the title can be anything. You'll always be looking it up via the permalink/slug/pagename/whatever you name it. -
anonymous
Oh, and regarding prost processing: On this particular wiki, the url prefix is nil, so I wouldn't know for sure if a <a>-tag is a page-wiki-ting-link or just a regular <a>.
-
Greg Hurrell
Good points, "someone from GitHub" ;-) I think you're right on both counts.
-
Greg Hurrell
Just in case that pastie ever goes away:
Wikitext::Parser.new.parse("foo", { :base_heading_level => 1, :redlink_proc => proc {|link| Page.count_by_permalink(link) == 0 } })
I think it's basically the right idea, although I wonder if rather than hard-coding the word "red" in the option name it should instead be called something like
link_class
, and the proc could optionally return a string with the class for the link (which might be"redlink"
or anything else).I just don't know whether I want to commit to a yes/no dichotomy here when it might actually be more flexible to allow an arbitrary class based on a string. In other words it would look like:
Wikitext::Parser.new.parse("foo", { :base_heading_level => 1, :link_class => proc { |target| Page.count_by_permalink(target) == 0 ? 'redlink' : nil } })
Evidently you'd probably not actually use a
count_by_permalink
method as you'd be generating a database query for every link in the text, which could be lots of queries if the text is long. I think you'd want some kind of custom method with caching built-in. -
anonymous
Created
,
edited
In this particular case, having a few hundred queries probably isn't that much of a problem, since the output will be cached anyway. It'll only run these queries once after any given page has changed.
However, here's a few alternative implementations I could think of.
## Parse time parser = Wikitext::Parser.new("foo") linked_pages = Page.find(:all, :conditions => {:permalink => parser.pages}) parser.parse({ :page_proc => proc {|link| linked_pages.any? {|lp| lp.permalink == link } && {:class => "red"} } }) ## Post-processing parser = Wikitext::Parser.new("foo") parser.parsed? # => false html = parser.parse parser.parsed? # => true parser.page_links # => ["a page", "another page", "more pages"] page_records = Page.find(:all, :conditions => {:permalink => parser.page_links}) # The block returns a hash that will be mapped to the <a> tags attributes parser.post_process(:page_links) {|link| {:style => "whatever"} } parser.post_process(:page_links) {|link| page_records.any? {|r| r.permalink == link } && {:class => "red"} }
PS: It would be useful if there was a "name" text field for these comments, so that I could tag them without registering an account. Also, I <3 preview buttons :)
-
anonymous
Forgot to mention, the first example would have to pre-parse somehow, so that
parser.pages
is available before it parses the rest of the stuff. So indeed, it seems like post processing is the way to go, so that you can get a list of all the pages in the document before redlinking. -
Greg Hurrell
Thanks for the ongoing input, anon.
It would be useful if there was a "name" text field for these comments, so that I could tag them without registering an account.
I've often wondered about that and in principle I think it's a good idea. See ticket #1348.
Also, I <3 preview buttons :)
Yep, aware of that one. See ticket #1267. I actually have AJAX previews in place for a several other things on the site (wiki articles, blog posts etc) but I just need to decide what the best UI for comment previews is.
So indeed, it seems like post processing is the way to go, so that you can get a list of all the pages in the document before redlinking.
On my own site, where there are just over 1,500 wiki pages, I was thinking of doing one big query for all page titles and just keeping that array around in memory forever. Something like this (not tested, typed in browser, usual disclaimers apply):
class Article < ActiveRecord::Base def self.page_titles @@page_titles ||= Article.find_by_sql('SELECT title FROM articles').map(&:title) end end
Which you'd then use like this:
Wikitext::Parser.new.parse "foo", :link_class => proc { |target| Article.page_titles.include?(target) ? nil : 'redlink' }
Obviously you'd need to invalidate that cache appropriately after saving or destroying articles. With that number of articles, no need to do it on a per-article basis, nor use memcached or anything else. Just keep it as simple as possible. As the number of articles goes up though you'd need more complex solutions, I think.
-
August Lilleaas
It's anon here, finally registered and logged in ; )
The :link_class proc definitely makes sense, +1! It will then be up to you if you n+1 and cache it (which is OK some times), or do as you say and maintain an array of all the titles in the wiki and use that.
-
Greg Hurrell
Ok, specs are now done. Implementation to follow.
-
Greg Hurrell
Ok, initial implementation is done. Will let it bake for a while to see if any issues crop up, but it looks like this:
diff --git a/doc/RELEASE-NOTES b/doc/RELEASE-NOTES index 2c29c10..cfa2598 100644 --- a/doc/RELEASE-NOTES +++ b/doc/RELEASE-NOTES @@ -9,6 +9,10 @@ the source code repository at: git.wincent.dev. = Changes in 1.9 * NilClass#w method now accepts and optional parameters hash +* new +link_proc+ option to Wikitext::Parser#parse allows you to + dynamically apply a custom CSS class based on the link target; + this can be used, for example, to provide "red links" for + articles which do not exist yet = Changes in 1.8 diff --git a/ext/parser.c b/ext/parser.c index fd28270..4728ad8 100644 --- a/ext/parser.c +++ b/ext/parser.c @@ -1042,6 +1042,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self) // process options hash int base_indent = 0; int base_heading_level = NUM2INT(rb_iv_get(self, "@base_heading_level")); + VALUE link_proc = Qnil; if (!NIL_P(options) && TYPE(options) == T_HASH) { // :indent => 0 (or more) @@ -1061,6 +1062,11 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self) // :base_heading_level => 0/1/2/3/4/5/6 if (rb_funcall(options, rb_intern("has_key?"), 1, ID2SYM(rb_intern("base_heading_level"))) == Qtrue) base_heading_level = NUM2INT(rb_hash_aref(options, ID2SYM(rb_intern("base_heading_level")))); + + // :link_proc => lambda { |link_target| ... } + // TODO: refactor to avoid some repeated calls to ID2SYM and rb_intern + if (rb_funcall(options, rb_intern("has_key?"), 1, ID2SYM(rb_intern("link_proc"))) == Qtrue) + link_proc = rb_hash_aref(options, ID2SYM(rb_intern("link_proc"))); } // normalize, regardless of whether this came from instance variable or override @@ -2144,10 +2150,20 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self) } else wiki_trim_link_text(parser); + + // perform "redlink" check before manipulating link_target + if (NIL_P(link_proc)) + j = Qnil; + else + { + j = rb_funcall(link_proc, rb_intern("call"), 1, string_from_str(parser->link_target)); + if (!NIL_P(j)) + j = StringValue(j); + } wiki_encode_link_target(parser); wiki_pop_from_stack_up_to(parser, output, LINK_START, true); parser->capture = NULL; - wiki_append_hyperlink(parser, prefix, parser->link_target, parser->link_text, Qnil, false); + wiki_append_hyperlink(parser, prefix, parser->link_target, parser->link_text, j, false); str_clear(parser->link_target); str_clear(parser->link_text); } diff --git a/spec/internal_link_spec.rb b/spec/internal_link_spec.rb index 5e4d588..feff8bf 100755 --- a/spec/internal_link_spec.rb +++ b/spec/internal_link_spec.rb @@ -114,6 +114,107 @@ describe Wikitext::Parser, 'internal links (space to underscore off)' do @parser.parse('foo [[bar]] baz').should == expected # was a bug end + describe '"red link" support' do + it 'should accept a Proc object via the optional "link_proc" parameter' do + @parser.parse('foo', :link_proc => Proc.new { }).should == %Q{<p>foo</p>\n} + end + + it 'should accept a lambda via the optional "link_proc" parameter' do + @parser.parse('foo', :link_proc => lambda { }).should == %Q{<p>foo</p>\n} + end + + it 'should apply custom link CSS when supplied (Proc object version)' do + link_proc = Proc.new { |target| target == 'bar' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo">foo</a> <a href="/wiki/bar" class="redlink">bar</a></p>\n} + @parser.parse('[[foo]] [[bar]]', :link_proc => link_proc).should == expected + end + + it 'should apply custom link CSS when supplied (lambda version)' do + link_proc = lambda { |target| target == 'bar' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo">foo</a> <a href="/wiki/bar" class="redlink">bar</a></p>\n} + @parser.parse('[[foo]] [[bar]]', :link_proc => link_proc).should == expected + end + + it 'should apply no custom link CSS when supplied nil (Proc object version)' do + expected = %Q{<p><a href="/wiki/foo">foo</a></p>\n} + @parser.parse('[[foo]]', :link_proc => Proc.new { |target| nil }).should == expected + end + + it 'should apply no custom link CSS when supplied nil (lambda version)' do + expected = %Q{<p><a href="/wiki/foo">foo</a></p>\n} + @parser.parse('[[foo]]', :link_proc => lambda { |target| nil }).should == expected + end + + it 'should let exceptions bubble up from the link proc (Proc object version)' do + lambda { @parser.parse('[[foo]]', :link_proc => Proc.new { |target| raise 'bar' }) }.should raise_error(RuntimeError, /bar/) + end + + it 'should let exceptions bubble up from the link proc (lambda version)' do + lambda { @parser.parse('[[foo]]', :link_proc => lambda { |target| raise 'bar' }) }.should raise_error(RuntimeError, /bar/) + end + + it 'should complain if the link proc returns a non-stringy object (Proc object version)' do + lambda { + @parser.parse '[[foo]]', :link_proc => Proc.new { 1 } + }.should raise_error(TypeError, /can't convert/) + end + + it 'should complain if the link proc returns a non-stringy object (lambda version)' do + lambda { + @parser.parse '[[foo]]', :link_proc => lambda { 1 } + }.should raise_error(TypeError, /can't convert/) + end + + # a couple of Ruby's idiosynchrasies: different behaviour of lambdas and Procs + it 'should not complain if the Proc object accepts too many arguments' do + lambda { + @parser.parse '[[foo]]', :link_proc => Proc.new { |a,b| } + }.should_not raise_error(ArgumentError, /wrong number/) + end + + it 'should complain if the lambda accepts too many arguments' do + lambda { + @parser.parse '[[foo]]', :link_proc => lambda { |a,b| } + }.should raise_error(ArgumentError, /wrong number/) + end + + it 'should complain when "return" is used inside a "Proc.new" block' do + lambda { + @parser.parse '[[foo]]', :link_proc => Proc.new { return 'bar' } + }.should raise_error(LocalJumpError) + end + + it 'should not complain when "return" is used inside a lambda' do + lambda { + @parser.parse '[[foo]]', :link_proc => lambda { return 'bar' } + }.should_not raise_error(LocalJumpError) + end + + it 'should interact correctly with spaces in link targets (Proc object version)' do + link_proc = Proc.new { |target| target == 'bar b' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo%20a">foo a</a> <a href="/wiki/bar%20b" class="redlink">bar b</a></p>\n} + @parser.parse('[[foo a]] [[bar b]]', :link_proc => link_proc).should == expected + end + + it 'should interact correctly with spaces in link targets (lambda version)' do + link_proc = lambda { |target| target == 'bar b' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo%20a">foo a</a> <a href="/wiki/bar%20b" class="redlink">bar b</a></p>\n} + @parser.parse('[[foo a]] [[bar b]]', :link_proc => link_proc).should == expected + end + + it 'should interact correctly with explicit link text (Proc object version)' do + link_proc = Proc.new { |target| target == 'bar b' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo%20a">hello</a> <a href="/wiki/bar%20b" class="redlink">world</a></p>\n} + @parser.parse('[[foo a|hello]] [[bar b|world]]', :link_proc => link_proc).should == expected + end + + it 'should interact correctly with explicit link text (lambda version)' do + link_proc = lambda { |target| target == 'bar b' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo%20a">hello</a> <a href="/wiki/bar%20b" class="redlink">world</a></p>\n} + @parser.parse('[[foo a|hello]] [[bar b|world]]', :link_proc => link_proc).should == expected + end + end + describe 'custom link text' do it 'should recognize link text placed after the separator' do @parser.parse('[[foo|bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n} @@ -541,6 +642,107 @@ describe Wikitext::Parser, 'internal links (space to underscore on)' do @parser.parse('foo [[bar]] baz').should == expected # was a bug end + describe '"red link" support' do + it 'should accept a Proc object via the optional "link_proc" parameter' do + @parser.parse('foo', :link_proc => Proc.new { }).should == %Q{<p>foo</p>\n} + end + + it 'should accept a lambda via the optional "link_proc" parameter' do + @parser.parse('foo', :link_proc => lambda { }).should == %Q{<p>foo</p>\n} + end + + it 'should apply custom link CSS when supplied (Proc object version)' do + link_proc = Proc.new { |target| target == 'bar' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo">foo</a> <a href="/wiki/bar" class="redlink">bar</a></p>\n} + @parser.parse('[[foo]] [[bar]]', :link_proc => link_proc).should == expected + end + + it 'should apply custom link CSS when supplied (lambda version)' do + link_proc = lambda { |target| target == 'bar' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo">foo</a> <a href="/wiki/bar" class="redlink">bar</a></p>\n} + @parser.parse('[[foo]] [[bar]]', :link_proc => link_proc).should == expected + end + + it 'should apply no custom link CSS when supplied nil (Proc object version)' do + expected = %Q{<p><a href="/wiki/foo">foo</a></p>\n} + @parser.parse('[[foo]]', :link_proc => Proc.new { |target| nil }).should == expected + end + + it 'should apply no custom link CSS when supplied nil (lambda version)' do + expected = %Q{<p><a href="/wiki/foo">foo</a></p>\n} + @parser.parse('[[foo]]', :link_proc => lambda { |target| nil }).should == expected + end + + it 'should let exceptions bubble up from the link proc (Proc object version)' do + lambda { @parser.parse('[[foo]]', :link_proc => Proc.new { |target| raise 'bar' }) }.should raise_error(RuntimeError, /bar/) + end + + it 'should let exceptions bubble up from the link proc (lambda version)' do + lambda { @parser.parse('[[foo]]', :link_proc => lambda { |target| raise 'bar' }) }.should raise_error(RuntimeError, /bar/) + end + + it 'should complain if the link proc returns a non-stringy object (Proc object version)' do + lambda { + @parser.parse '[[foo]]', :link_proc => Proc.new { 1 } + }.should raise_error(TypeError, /can't convert/) + end + + it 'should complain if the link proc returns a non-stringy object (lambda version)' do + lambda { + @parser.parse '[[foo]]', :link_proc => lambda { 1 } + }.should raise_error(TypeError, /can't convert/) + end + + # a couple of Ruby's idiosynchrasies: different behaviour of lambdas and Procs + it 'should not complain if the Proc object accepts too many arguments' do + lambda { + @parser.parse '[[foo]]', :link_proc => Proc.new { |a,b| } + }.should_not raise_error(ArgumentError, /wrong number/) + end + + it 'should complain if the lambda accepts too many arguments' do + lambda { + @parser.parse '[[foo]]', :link_proc => lambda { |a,b| } + }.should raise_error(ArgumentError, /wrong number/) + end + + it 'should complain when "return" is used inside a "Proc.new" block' do + lambda { + @parser.parse '[[foo]]', :link_proc => Proc.new { return 'bar' } + }.should raise_error(LocalJumpError) + end + + it 'should not complain when "return" is used inside a lambda' do + lambda { + @parser.parse '[[foo]]', :link_proc => lambda { return 'bar' } + }.should_not raise_error(LocalJumpError) + end + + it 'should interact correctly with spaces in link targets (Proc object version)' do + link_proc = Proc.new { |target| target == 'bar b' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo_a">foo a</a> <a href="/wiki/bar_b" class="redlink">bar b</a></p>\n} + @parser.parse('[[foo a]] [[bar b]]', :link_proc => link_proc).should == expected + end + + it 'should interact correctly with spaces in link targets (lambda version)' do + link_proc = lambda { |target| target == 'bar b' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo_a">foo a</a> <a href="/wiki/bar_b" class="redlink">bar b</a></p>\n} + @parser.parse('[[foo a]] [[bar b]]', :link_proc => link_proc).should == expected + end + + it 'should interact correctly with explicit link text (Proc object version)' do + link_proc = Proc.new { |target| target == 'bar b' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo_a">hello</a> <a href="/wiki/bar_b" class="redlink">world</a></p>\n} + @parser.parse('[[foo a|hello]] [[bar b|world]]', :link_proc => link_proc).should == expected + end + + it 'should interact correctly with explicit link text (lambda version)' do + link_proc = lambda { |target| target == 'bar b' ? 'redlink' : nil } + expected = %Q{<p><a href="/wiki/foo_a">hello</a> <a href="/wiki/bar_b" class="redlink">world</a></p>\n} + @parser.parse('[[foo a|hello]] [[bar b|world]]', :link_proc => link_proc).should == expected + end + end + describe 'custom link text' do it 'should recognize link text placed after the separator' do @parser.parse('[[foo|bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
-
Greg Hurrell
Wikitext 1.9 (with this new feature) was released on 6 August, so going to mark this one as closed.
-
Greg Hurrell
Status changed:
- From: new
- To: closed
Add a comment
Comments are now closed for this issue.