Class: Webgen::Utils::TagParser

Inherits:
Object
  • Object
show all
Defined in:
lib/webgen/utils/tag_parser.rb

Overview

This class is used to parse a string for webgen tags and replace them with dynamically generated content. See #replace_tags for more information.

Defined Under Namespace

Classes: Error, ProcessingStruct

Constant Summary collapse

BRACKETS_RE =

:nodoc:

/([{}])/

Instance Method Summary collapse

Constructor Details

#initialize(prefix = nil) ⇒ TagParser

Create a new TagParser object, optionally providing a tag prefix.



24
25
26
27
# File 'lib/webgen/utils/tag_parser.rb', line 24

def initialize(prefix = nil)
  @start_re = /(\\*)\{#{prefix}(\w+)(::?)/
  @end_re = /(\\*)\{#{prefix}(\w+)\}/
end

Instance Method Details

#parse_params(param_string, tag) ⇒ Object

Parse the parameter string and return the result.



115
116
117
118
119
# File 'lib/webgen/utils/tag_parser.rb', line 115

def parse_params(param_string, tag)
  YAML::load("--- #{param_string}")
rescue ArgumentError, SyntaxError, YAML::SyntaxError => e
  raise Error.new("Could not parse parameter string '#{param_string}' for tag '#{tag}': #{e.message}")
end

#replace_tags(content) ⇒ Object

Return the content with all webgen tags replaced.

When a webgen tag is encountered by the parser, the method yields all found information and substitutes the returned string for the tag.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/webgen/utils/tag_parser.rb', line 37

def replace_tags(content) #:yields: tag_name, params, body
  scanner = StringScanner.new(content)
  data = ProcessingStruct.new(:before_tag)
  while true
    case data.state
    when :before_tag
      if scanner.skip_until(@start_re)
        data.state = :in_start_tag
        data.backslashes = scanner[1].length
        data.brackets = 1
        data.tag = scanner[2]
        data.simple_tag = (scanner[3] == ':')
        data.params_start_pos = scanner.pos
        data.start_pos = scanner.pos - scanner.matched.length
      else
        data.state = :done
      end

    when :in_start_tag
      data.brackets += (scanner[1] == '{' ? 1 : -1) while data.brackets != 0 && scanner.skip_until(BRACKETS_RE)
      if data.brackets != 0
        raise Error.new("Unbalanced curly brackets found for tag '#{data.tag}'")
      else
        data.params_end_pos = data.body_end_pos = data.end_pos = scanner.pos - 1
        data.state = (data.simple_tag ? :process : :in_body)
      end

    when :process
      begin
        enc = scanner.string.encoding
        scanner.string.force_encoding('ASCII-8BIT')
        if data.backslashes % 2 == 0
          params = parse_params(scanner.string[data.params_start_pos...data.params_end_pos].force_encoding(enc),
                                data.tag)
          result = yield(data.tag, params,
                         scanner.string[(data.params_end_pos+1)...data.body_end_pos].to_s.force_encoding(enc))
          result = result.to_s.force_encoding('ASCII-8BIT')
          scanner.string[data.start_pos..data.end_pos] = "\\" * (data.backslashes / 2) + result
          scanner.pos = data.start_pos + result.length
        else
          scanner.string[data.start_pos, 1 + data.backslashes / 2] = ''
          scanner.pos -= 1 + data.backslashes / 2
        end
      ensure
        scanner.string.force_encoding(enc)
      end
      data.state = :before_tag

    when :in_body
      while (result = scanner.skip_until(@end_re))
        next unless scanner[2] == data.tag
        if scanner[1].length % 2 == 1
          scanner.string[(scanner.pos - scanner.matched.length), 1 + scanner[1].length / 2] = ''
          scanner.pos -= 1 + scanner[1].length / 2
        else
          break
        end
      end
      if result
        data.state = :process
        data.end_pos = scanner.pos - 1
        data.body_end_pos = scanner.pos - scanner.matched.length + scanner[1].length / 2
      else
        raise Error.new("Invalid body part - no end tag found for '#{data.tag}'")
      end

    when :done
      break
    end
  end
  scanner.string
rescue Error => e
  e.line = scanner.string[0...data.start_pos].scan("\n").size + 1
  e.column = data.start_pos - (scanner.string.rindex("\n", data.start_pos) || -1)
  raise
end