Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to match and replace templating tags in Ruby / Rails?

Trying to add a very rudimentary description template to one of my Rails models. What I want to do is take a template string like this:

template = "{{ name }} is the best {{ occupation }} in {{ city }}."

and a hash like this:

vals = {:name => "Joe Smith", :occupation => "birthday clown", :city => "Las Vegas"}

and get a description generated. I thought I could do this with a simple gsub but Ruby 1.8.7 doesn't accept hashes as the second argument. When I do a gsub as a block like this:

> template.gsub(/\{\{\s*(\w+)\s*\}\}/) {|m| vals[m]}
=> " is the best  in ." 

You can see it replaces it with the entire string (with curly braces), not the match captures.

How do I get it to replace "{{ something }}" with vals["something"] (or vals["something".to_sym])?

TIA

like image 415
Callmeed Avatar asked Jun 06 '11 21:06

Callmeed


2 Answers

Using Ruby 1.9.2

The string formatting operator % will format a string with a hash as the arg

>> template = "%{name} is the best %{occupation} in %{city}."
>> vals = {:name => "Joe Smith", :occupation => "birthday clown", :city => "Las Vegas"}
>> template % vals
=> "Joe Smith is the best birthday clown in Las Vegas."

Using Ruby 1.8.7

The string formatting operator in Ruby 1.8.7 doesn't support hashes. Instead, you can use the same arguments as the Ruby 1.9.2 solution and patch the String object so when you upgrade Ruby you won't have to edit your strings.

if RUBY_VERSION < '1.9.2'
  class String
    old_format = instance_method(:%)

    define_method(:%) do |arg|
      if arg.is_a?(Hash)
        self.gsub(/%\{(.*?)\}/) { arg[$1.to_sym] }
      else
        old_format.bind(self).call(arg)
      end
    end
  end
end

>> "%05d" % 123 
=> "00123"
>> "%-5s: %08x" % [ "ID", 123 ]
=> "ID   : 0000007b"
>> template = "%{name} is the best %{occupation} in %{city}."
>> vals = {:name => "Joe Smith", :occupation => "birthday clown", :city => "Las Vegas"}
>> template % vals
=> "Joe Smith is the best birthday clown in Las Vegas."

codepad example showing the default and extended behavior

like image 158
Christopher Manning Avatar answered Nov 15 '22 21:11

Christopher Manning


The easiest thing is probably to use $1.to_sym in your block:

>> template.gsub(/\{\{\s*(\w+)\s*\}\}/) { vals[$1.to_sym] }
=> "Joe Smith is the best birthday clown in Las Vegas."

From the fine manual:

In the block form, the current match string is passed in as a parameter, and variables such as $1, $2, $`, $&, and $’ will be set appropriately. The value returned by the block will be substituted for the match on each call.

like image 35
mu is too short Avatar answered Nov 15 '22 23:11

mu is too short