CodeTag

class
Superclass
Inversion::Template::Tag
Included Modules
Inversion::AbstractClass

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 )
    end

end

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.

Attributes

body[R]

the body of the tag

identifiers[R]

the identifiers in the code contained in the tag

Public Class Methods

anchor
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 96
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 ]
end
anchor
tag_patterns()

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 86
def self::tag_patterns
        return @tag_patterns if defined?( @tag_patterns )
        return self.superclass.tag_patterns
end

Protected Instance Methods

anchor
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 109
def initialize( body, linenum=nil, colnum=nil ) # :notnew:
        super

        @body = body.strip
        @identifiers = []
        @matched_pattern = self.match_tag_pattern( body )
end
anchor
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 141
def match_tag_pattern( body )

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

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

        valid_patterns = self.class.tag_patterns.map( &: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 ]
end