CodeTag class

The base class for Inversion tags that parse the body section of the tag using a Ruby parser.

It provides a ‘tag_pattern` declarative method that is used to specify a pattern of tokens to match, and a block for handling tag instances that match the pattern.

class Inversion::Template::MyTag < Inversion::Template::CodeTag

    # Match a tag that looks like: <?my "string of stuff" ?>
    tag_pattern 'tstring_beg $(tstring_content) tstring_end' do |tag, match|
        tag.string = match.string( 1 )


The tokens in the ‘tag_pattern` are Ruby token names used by the parser. If you’re creating your own tag, you can dump the tokens for a particular snippet using the ‘inversion’ command-line tool that comes with the gem:

$ inversion tagtokens 'attr.dump! {|thing| thing.length }'
ident<"attr"> period<"."> ident<"dump!"> sp<" "> lbrace<"{"> op<"|"> \
  ident<"thing"> op<"|"> sp<" "> ident<"thing"> period<".">          \
  ident<"length"> sp<" "> rbrace<"}">

:todo: Finish the tag_pattern docs: placeholders, regex limitations, etc.


body R

the body of the tag

identifiers R

the identifiers in the code contained in the tag

Public Class Methods


Declarative that forces a tag to inherit existing patterns from its parent, rather than replacing them. Afterwards, you can use ‘tag_pattern` regularly, appending to the list.

# File lib/inversion/template/codetag.rb, line 115
def self::inherit_tag_patterns
        raise ScriptError, "Patterns already exist for this tag." if defined?( @tag_patterns )
        @tag_patterns = self.superclass.tag_patterns
tag_pattern( token_pattern ) { |tag, match_data| ... }

Declare a ‘token_pattern` for tag bodies along with a `callback` that will be called when a tag matching the pattern is instantiated. The `callback` will be called with the tag instance, and the MatchData object that resulted from matching the input, and should set up the yielded `tag` object appropriately.

# File lib/inversion/template/codetag.rb, line 105
def self::tag_pattern( token_pattern, &callback ) #:yield: tag, match_data
        pattern = TokenPattern.compile( token_pattern )
        @tag_patterns = [] unless defined?( @tag_patterns )
        @tag_patterns << [ pattern, callback ]

Return the tag patterns for this class, or those of its superclass if it doesn’t override them.

# File lib/inversion/template/codetag.rb, line 95
def self::tag_patterns
        return @tag_patterns if defined?( @tag_patterns )
        return self.superclass.tag_patterns

Protected Instance Methods

initialize( body, linenum=nil, colnum=nil )

Initialize a new tag that expects Ruby code in its ‘body`. Calls the tag’s parse_pi_body method with the specified ‘body`.

# File lib/inversion/template/codetag.rb, line 127
def initialize( body, linenum=nil, colnum=nil ) # :notnew:

        @body = body.strip
        @identifiers = []
        @matched_pattern = self.match_tag_pattern( body )
match_tag_pattern( body )

Match the given ‘body` against one of the tag’s tag patterns, calling the block associated with the first one that matches and returning the matching pattern.

# File lib/inversion/template/codetag.rb, line 159
def match_tag_pattern( body )

        self.class.tag_patterns.each do |tp, callback|
                self.log.debug "Testing for a match against %p" % [ tp ]
                if match = tp.match( body.strip )
                        self.log.debug "Matched tag pattern: %p, match is: %p" % [ tp, match ]
               self, match )
                        return tp

        self.log.error "Failed to match %p with %d patterns." %
                [ body, self.class.tag_patterns.length ]

        valid_patterns = &:first ).map( &:source ).join( "\n  ")
        tokenized_src = Ripper.lex( body ).collect do |tok|
                self.log.debug "  lexed token: #{tok.inspect}"
                "%s<%s>" % [ tok[1].to_s[3..-1], tok[2] ]
        end.join(' ')

        raise Inversion::ParseError, "malformed %s: expected one of:\n  %s\ngot:\n  %s" %
                [ self.tagname, valid_patterns, tokenized_src ]