Permalink
Browse files

Adds named argument support to the tag parser: any tag can include ar…

…guments/attributes, which will be passed to the method as a Ruby hash (with Symbol keys)
  • Loading branch information...
1 parent 8349597 commit 50ef20ea89ed15e22be57aad4eac7e9c23619c55 @ddemaree committed Apr 12, 2011
Showing with 39 additions and 8 deletions.
  1. +27 −4 lib/mustache/parser.rb
  2. +12 −4 test/parser_test.rb
View
@@ -50,11 +50,15 @@ def to_s
SKIP_WHITESPACE = [ '#', '^', '/', '<', '>', '=', '!' ]
# The content allowed in a tag name.
- ALLOWED_CONTENT = /(\w|[?!\/.-])*/
+ # OLD_ALLOWED_CONTENT = /(\w|[?!\/.-])*/
+ ALLOWED_CONTENT = /(\w|[?!\/.-])*(?: (\w|[?!\/.-])+\:"[^"]+")*/
# These types of tags allow any content,
# the rest only allow ALLOWED_CONTENT.
- ANY_CONTENT = [ '!', '=' ]
+ ANY_CONTENT = [ '!', '=', '%' ]
+
+ # Regexp for tags containing arguments
+ ARGUMENT_FORMAT = /(?: (\w|[?!\/.-])+\:"[^"]+")+/
attr_reader :scanner, :result
attr_writer :otag, :ctag
@@ -124,7 +128,7 @@ def scan_tags
# Since {{= rewrites ctag, we store the ctag which should be used
# when parsing this specific tag.
current_ctag = self.ctag
- type = @scanner.scan(/#|\^|\/|=|!|<|>|&|\{/)
+ type = @scanner.scan(/#|\^|\/|=|!|\%|<|>|&|\{/)
@scanner.skip(/\s*/)
# ANY_CONTENT tags allow any character inside of them, while
@@ -139,7 +143,26 @@ def scan_tags
# We found {{ but we can't figure out what's going on inside.
error "Illegal content in tag" if content.empty?
- fetch = [:mustache, :fetch, content.split('.')]
+ # Detect a tag with arguments
+ # error content.to_s if content =~ /tag_with_args/
+ tag_arguments = {}
+
+ if content =~ ARGUMENT_FORMAT
+ # Strip out and store each named argument
+ content.gsub!(ARGUMENT_FORMAT) { |t|
+ key, value = t.strip.split(":") # Strip leading spaces and separate keys from values
+ value.gsub!(/\"/,"") # Strip quoted values
+ tag_arguments[key.to_sym] = value; # Store in the arguments hash
+ "" # Return an empty string, to remove this argument from the tag content
+ }
+ end
+
+ # The handler is an array of chained method names and (if present) the arguments hash
+ # E.g. {{post.date format:"%a %b %e"}} => ["post", "date", {:format => "%a %b %e"}]
+ handler = content.split('.')
+ handler << tag_arguments unless tag_arguments.empty?
+
+ fetch = [:mustache, :fetch, handler]
prev = @result
# Based on the sigil, do what needs to be done.
View
@@ -1,14 +1,19 @@
$LOAD_PATH.unshift File.dirname(__FILE__)
+$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
+
require 'helper'
+require 'mustache'
+# require '../lib/mustache/parser'
class ParserTest < Test::Unit::TestCase
def test_parser
lexer = Mustache::Parser.new
tokens = lexer.compile(<<-EOF)
<h1>{{header}}</h1>
+{{tag_with_args arg1:"hi there"}}
{{#items}}
{{#first}}
- <li><strong>{{name}}</strong></li>
+ <li><strong>{{name format:"short"}}</strong></li>
{{/first}}
{{#link}}
<li><a href="{{url}}">{{name}}</a></li>
@@ -24,6 +29,9 @@ def test_parser
[:static, "<h1>"],
[:mustache, :etag, [:mustache, :fetch, ["header"]]],
[:static, "</h1>\n"],
+ [:mustache, :etag,
+ [:mustache, :fetch, ["tag_with_args", {:arg1=>"hi there"}]]],
+ [:static, "\n"],
[:mustache,
:section,
[:mustache, :fetch, ["items"]],
@@ -33,9 +41,9 @@ def test_parser
[:mustache, :fetch, ["first"]],
[:multi,
[:static, " <li><strong>"],
- [:mustache, :etag, [:mustache, :fetch, ["name"]]],
+ [:mustache, :etag, [:mustache, :fetch, ["name", {:format=>"short"}]]],
[:static, "</strong></li>\n"]],
- %Q' <li><strong>{{name}}</strong></li>\n',
+ %Q' <li><strong>{{name format:"short"}}</strong></li>\n',
%w[{{ }}]],
[:mustache,
:section,
@@ -48,7 +56,7 @@ def test_parser
[:static, "</a></li>\n"]],
%Q' <li><a href="{{url}}">{{name}}</a></li>\n',
%w[{{ }}]]],
- %Q'{{#first}}\n <li><strong>{{name}}</strong></li>\n{{/first}}\n{{#link}}\n <li><a href="{{url}}">{{name}}</a></li>\n{{/link}}\n',
+ %Q'{{#first}}\n <li><strong>{{name format:"short"}}</strong></li>\n{{/first}}\n{{#link}}\n <li><a href="{{url}}">{{name}}</a></li>\n{{/link}}\n',
%w[{{ }}]],
[:static, "\n"],
[:mustache,

0 comments on commit 50ef20e

Please sign in to comment.