Search code examples
rubyruby-on-rails-3.1twitter-bootstrap

How to escape anchor link content when using gsub to replace words with links


I have an array of words I need to replace in a sentence with links. I am running into an error when there is a word that is contained in in my anchor link. I am using a loop to loop through all the words, so if the first link contains the current word to be replaced, it will be replaced with the new link again within the existing anchor tag.

Example:

I have the sentence: The quick brown fox jumps over the lazy dog.

I want to replace 'Fox' with <a href="#" data-content="A fox is not a dog">fox< /a> and 'Dog' with: <a href="#" data-content="A dog is a man's best friend">dog</a>

My code:

<% text = "The quick brown fox jumps over the lazy dog." %>

<% @definition.each do |d| % ><br/>
<% text = text.gsub(d.word, link_to(d.word, '# ', :class => "popover-definition", :rel => "popover", :title => "<strong>#{d.word}</strong>", :"data-content" => d.meaning)).html_safe %><br/>
<% end %>

** @definition contains both the word and the link to replace it with.

When the loop runs the second time the 'dog' in the <a> tag from 'fox' is replaced with a new link. How can I escape string replacing when the word is contained in an anchor?

Thanks!


Solution

  • In Ruby 1.9.2 and above, you can pass in a hash to gsub and it will match any keys in the hash to their values.

    From the documentation:

    If the second argument is a Hash, and the matched text is one of its keys, the corresponding value is the replacement string.

    So if you first create a hash from @definition:

    hash = @definition.inject({}) { |h, d| h[d.word] = d.meaning; h }
    #=> {"fox"=>"A fox is not a dog", "dog"=>"A dog is man's best friend"}
    

    Then you can do the replacement in just one line:

    text.gsub(%r[#{hash.keys.join('|')}], hash)
    #=> "The quick brown A fox is not a dog jumps over the lazy A dog is man's best friend."
    

    Just update hash to use link_to and this should work for your case:

    hash = @definition.inject({}) do |h, d|
      h[d.word] = link_to(d.word, '# ', :class => "popover-definition", :rel => "popover", :title => "<strong>#{d.word}</strong>", :"data-content" => d.meaning).html_safe
      h
    end
    text.gsub(%r[#{hash.keys.join('|')}], hash)