angry_mob_common_targets 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. data/LICENSE +21 -0
  2. data/README.md +38 -0
  3. data/lib/common_mob.rb +9 -0
  4. data/lib/common_mob/digest.rb +43 -0
  5. data/lib/common_mob/erb.rb +72 -0
  6. data/lib/common_mob/file.rb +55 -0
  7. data/lib/common_mob/patch.rb +51 -0
  8. data/lib/common_mob/resource_locator.rb +9 -0
  9. data/lib/common_mob/shell.rb +323 -0
  10. data/lib/common_mob/template.rb +23 -0
  11. data/lib/common_mob/version.rb +3 -0
  12. data/targets/crontab_patch.rb +37 -0
  13. data/targets/extract.rb +40 -0
  14. data/targets/fetch.rb +40 -0
  15. data/targets/files.rb +244 -0
  16. data/targets/git.rb +84 -0
  17. data/targets/group.rb +33 -0
  18. data/targets/packages.rb +94 -0
  19. data/targets/ruby.rb +13 -0
  20. data/targets/services.rb +184 -0
  21. data/targets/shell.rb +43 -0
  22. data/targets/user.rb +108 -0
  23. data/vendor/mustache/CONTRIBUTORS +9 -0
  24. data/vendor/mustache/HISTORY.md +135 -0
  25. data/vendor/mustache/LICENSE +20 -0
  26. data/vendor/mustache/README.md +405 -0
  27. data/vendor/mustache/Rakefile +103 -0
  28. data/vendor/mustache/benchmarks/complex.erb +15 -0
  29. data/vendor/mustache/benchmarks/complex.haml +12 -0
  30. data/vendor/mustache/benchmarks/helper.rb +20 -0
  31. data/vendor/mustache/benchmarks/simple.erb +5 -0
  32. data/vendor/mustache/benchmarks/speed.rb +78 -0
  33. data/vendor/mustache/bin/mustache +90 -0
  34. data/vendor/mustache/contrib/mustache-mode.el +278 -0
  35. data/vendor/mustache/contrib/mustache.vim +69 -0
  36. data/vendor/mustache/examples/hash.rb +16 -0
  37. data/vendor/mustache/examples/hash.yml +5 -0
  38. data/vendor/mustache/examples/projects.mustache +26 -0
  39. data/vendor/mustache/examples/projects.yml +28 -0
  40. data/vendor/mustache/examples/self.mustache +4 -0
  41. data/vendor/mustache/examples/self.yml +3 -0
  42. data/vendor/mustache/examples/simple.mustache +10 -0
  43. data/vendor/mustache/examples/simple.rb +24 -0
  44. data/vendor/mustache/lib/mustache.rb +358 -0
  45. data/vendor/mustache/lib/mustache/context.rb +108 -0
  46. data/vendor/mustache/lib/mustache/generator.rb +160 -0
  47. data/vendor/mustache/lib/mustache/parser.rb +230 -0
  48. data/vendor/mustache/lib/mustache/sinatra.rb +180 -0
  49. data/vendor/mustache/lib/mustache/template.rb +59 -0
  50. data/vendor/mustache/lib/mustache/version.rb +3 -0
  51. data/vendor/mustache/lib/rack/bug/panels/mustache_panel.rb +81 -0
  52. data/vendor/mustache/lib/rack/bug/panels/mustache_panel/mustache_extension.rb +27 -0
  53. data/vendor/mustache/lib/rack/bug/panels/mustache_panel/view.mustache +46 -0
  54. data/vendor/mustache/man/mustache.1 +180 -0
  55. data/vendor/mustache/man/mustache.1.html +204 -0
  56. data/vendor/mustache/man/mustache.1.ron +127 -0
  57. data/vendor/mustache/man/mustache.5 +576 -0
  58. data/vendor/mustache/man/mustache.5.html +415 -0
  59. data/vendor/mustache/man/mustache.5.ron +324 -0
  60. data/vendor/mustache/mustache.gemspec +32 -0
  61. data/vendor/mustache/test/autoloading_test.rb +52 -0
  62. data/vendor/mustache/test/fixtures/comments.mustache +1 -0
  63. data/vendor/mustache/test/fixtures/comments.rb +14 -0
  64. data/vendor/mustache/test/fixtures/complex_view.mustache +17 -0
  65. data/vendor/mustache/test/fixtures/complex_view.rb +34 -0
  66. data/vendor/mustache/test/fixtures/crazy_recursive.mustache +9 -0
  67. data/vendor/mustache/test/fixtures/crazy_recursive.rb +31 -0
  68. data/vendor/mustache/test/fixtures/delimiters.mustache +8 -0
  69. data/vendor/mustache/test/fixtures/delimiters.rb +23 -0
  70. data/vendor/mustache/test/fixtures/double_section.mustache +7 -0
  71. data/vendor/mustache/test/fixtures/double_section.rb +14 -0
  72. data/vendor/mustache/test/fixtures/escaped.mustache +1 -0
  73. data/vendor/mustache/test/fixtures/escaped.rb +14 -0
  74. data/vendor/mustache/test/fixtures/inner_partial.mustache +1 -0
  75. data/vendor/mustache/test/fixtures/inner_partial.txt +1 -0
  76. data/vendor/mustache/test/fixtures/inverted_section.mustache +7 -0
  77. data/vendor/mustache/test/fixtures/inverted_section.rb +14 -0
  78. data/vendor/mustache/test/fixtures/lambda.mustache +7 -0
  79. data/vendor/mustache/test/fixtures/lambda.rb +31 -0
  80. data/vendor/mustache/test/fixtures/namespaced.mustache +1 -0
  81. data/vendor/mustache/test/fixtures/namespaced.rb +25 -0
  82. data/vendor/mustache/test/fixtures/nested_objects.mustache +17 -0
  83. data/vendor/mustache/test/fixtures/nested_objects.rb +35 -0
  84. data/vendor/mustache/test/fixtures/node.mustache +8 -0
  85. data/vendor/mustache/test/fixtures/partial_with_module.mustache +3 -0
  86. data/vendor/mustache/test/fixtures/partial_with_module.rb +37 -0
  87. data/vendor/mustache/test/fixtures/passenger.conf +5 -0
  88. data/vendor/mustache/test/fixtures/passenger.rb +27 -0
  89. data/vendor/mustache/test/fixtures/recursive.mustache +4 -0
  90. data/vendor/mustache/test/fixtures/recursive.rb +14 -0
  91. data/vendor/mustache/test/fixtures/simple.mustache +5 -0
  92. data/vendor/mustache/test/fixtures/simple.rb +26 -0
  93. data/vendor/mustache/test/fixtures/template_partial.mustache +2 -0
  94. data/vendor/mustache/test/fixtures/template_partial.rb +18 -0
  95. data/vendor/mustache/test/fixtures/template_partial.txt +4 -0
  96. data/vendor/mustache/test/fixtures/unescaped.mustache +1 -0
  97. data/vendor/mustache/test/fixtures/unescaped.rb +14 -0
  98. data/vendor/mustache/test/fixtures/utf8.mustache +3 -0
  99. data/vendor/mustache/test/fixtures/utf8_partial.mustache +1 -0
  100. data/vendor/mustache/test/helper.rb +7 -0
  101. data/vendor/mustache/test/mustache_test.rb +536 -0
  102. data/vendor/mustache/test/parser_test.rb +54 -0
  103. data/vendor/mustache/test/partial_test.rb +168 -0
  104. metadata +167 -0
@@ -0,0 +1,108 @@
1
+ class Mustache
2
+ # A ContextMiss is raised whenever a tag's target can not be found
3
+ # in the current context if `Mustache#raise_on_context_miss?` is
4
+ # set to true.
5
+ #
6
+ # For example, if your View class does not respond to `music` but
7
+ # your template contains a `{{music}}` tag this exception will be raised.
8
+ #
9
+ # By default it is not raised. See Mustache.raise_on_context_miss.
10
+ class ContextMiss < RuntimeError; end
11
+
12
+ # A Context represents the context which a Mustache template is
13
+ # executed within. All Mustache tags reference keys in the Context.
14
+ class Context
15
+ # Expect to be passed an instance of `Mustache`.
16
+ def initialize(mustache)
17
+ @stack = [mustache]
18
+ end
19
+
20
+ # A {{>partial}} tag translates into a call to the context's
21
+ # `partial` method, which would be this sucker right here.
22
+ #
23
+ # If the Mustache view handling the rendering (e.g. the view
24
+ # representing your profile page or some other template) responds
25
+ # to `partial`, we call it and render the result.
26
+ def partial(name)
27
+ # Look for the first Mustache in the stack.
28
+ mustache = mustache_in_stack
29
+
30
+ # Call its `partial` method and render the result.
31
+ mustache.render(mustache.partial(name), self)
32
+ end
33
+
34
+ # Find the first Mustache in the stack. If we're being rendered
35
+ # inside a Mustache object as a context, we'll use that one.
36
+ def mustache_in_stack
37
+ @stack.detect { |frame| frame.is_a?(Mustache) }
38
+ end
39
+
40
+ # Adds a new object to the context's internal stack.
41
+ #
42
+ # Returns the Context.
43
+ def push(new)
44
+ @stack.unshift(new)
45
+ self
46
+ end
47
+ alias_method :update, :push
48
+
49
+ # Removes the most recently added object from the context's
50
+ # internal stack.
51
+ #
52
+ # Returns the Context.
53
+ def pop
54
+ @stack.shift
55
+ self
56
+ end
57
+
58
+ # Can be used to add a value to the context in a hash-like way.
59
+ #
60
+ # context[:name] = "Chris"
61
+ def []=(name, value)
62
+ push(name => value)
63
+ end
64
+
65
+ # Alias for `fetch`.
66
+ def [](name)
67
+ fetch(name, nil)
68
+ end
69
+
70
+ # Do we know about a particular key? In other words, will calling
71
+ # `context[key]` give us a result that was set. Basically.
72
+ def has_key?(key)
73
+ !!fetch(key)
74
+ rescue ContextMiss
75
+ false
76
+ end
77
+
78
+ # Similar to Hash#fetch, finds a value by `name` in the context's
79
+ # stack. You may specify the default return value by passing a
80
+ # second parameter.
81
+ #
82
+ # If no second parameter is passed (or raise_on_context_miss is
83
+ # set to true), will raise a ContextMiss exception on miss.
84
+ def fetch(name, default = :__raise)
85
+ @stack.each do |frame|
86
+ # Prevent infinite recursion.
87
+ next if frame == self
88
+
89
+ # Is this frame a hash?
90
+ hash = frame.respond_to?(:has_key?)
91
+
92
+ if hash && frame.has_key?(name)
93
+ return frame[name]
94
+ elsif hash && frame.has_key?(name.to_s)
95
+ return frame[name.to_s]
96
+ elsif !hash && frame.respond_to?(name)
97
+ return frame.__send__(name)
98
+ end
99
+ end
100
+
101
+ if default == :__raise || mustache_in_stack.raise_on_context_miss?
102
+ raise ContextMiss.new("Can't find #{name} in #{@stack.inspect}")
103
+ else
104
+ default
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,160 @@
1
+ class Mustache
2
+ # The Generator is in charge of taking an array of Mustache tokens,
3
+ # usually assembled by the Parser, and generating an interpolatable
4
+ # Ruby string. This string is considered the "compiled" template
5
+ # because at that point we're relying on Ruby to do the parsing and
6
+ # run our code.
7
+ #
8
+ # For example, let's take this template:
9
+ #
10
+ # Hi {{thing}}!
11
+ #
12
+ # If we run this through the Parser we'll get these tokens:
13
+ #
14
+ # [:multi,
15
+ # [:static, "Hi "],
16
+ # [:mustache, :etag, "thing"],
17
+ # [:static, "!\n"]]
18
+ #
19
+ # Now let's hand that to the Generator:
20
+ #
21
+ # >> puts Mustache::Generator.new.compile(tokens)
22
+ # "Hi #{CGI.escapeHTML(ctx[:thing].to_s)}!\n"
23
+ #
24
+ # You can see the generated Ruby string for any template with the
25
+ # mustache(1) command line tool:
26
+ #
27
+ # $ mustache --compile test.mustache
28
+ # "Hi #{CGI.escapeHTML(ctx[:thing].to_s)}!\n"
29
+ class Generator
30
+ # Options are unused for now but may become useful in the future.
31
+ def initialize(options = {})
32
+ @options = options
33
+ end
34
+
35
+ # Given an array of tokens, returns an interpolatable Ruby string.
36
+ def compile(exp)
37
+ "\"#{compile!(exp)}\""
38
+ end
39
+
40
+ # Given an array of tokens, converts them into Ruby code. In
41
+ # particular there are three types of expressions we are concerned
42
+ # with:
43
+ #
44
+ # :multi
45
+ # Mixed bag of :static, :mustache, and whatever.
46
+ #
47
+ # :static
48
+ # Normal HTML, the stuff outside of {{mustaches}}.
49
+ #
50
+ # :mustache
51
+ # Any Mustache tag, from sections to partials.
52
+ #
53
+ # To give you an idea of what you'll be dealing with take this
54
+ # template:
55
+ #
56
+ # Hello {{name}}
57
+ # You have just won ${{value}}!
58
+ # {{#in_ca}}
59
+ # Well, ${{taxed_value}}, after taxes.
60
+ # {{/in_ca}}
61
+ #
62
+ # If we run this through the Parser, we'll get back this array of
63
+ # tokens:
64
+ #
65
+ # [:multi,
66
+ # [:static, "Hello "],
67
+ # [:mustache, :etag, "name"],
68
+ # [:static, "\nYou have just won $"],
69
+ # [:mustache, :etag, "value"],
70
+ # [:static, "!\n"],
71
+ # [:mustache,
72
+ # :section,
73
+ # "in_ca",
74
+ # [:multi,
75
+ # [:static, "Well, $"],
76
+ # [:mustache, :etag, "taxed_value"],
77
+ # [:static, ", after taxes.\n"]]]]
78
+ def compile!(exp)
79
+ case exp.first
80
+ when :multi
81
+ exp[1..-1].map { |e| compile!(e) }.join
82
+ when :static
83
+ str(exp[1])
84
+ when :mustache
85
+ send("on_#{exp[1]}", *exp[2..-1])
86
+ else
87
+ raise "Unhandled exp: #{exp.first}"
88
+ end
89
+ end
90
+
91
+ # Callback fired when the compiler finds a section token. We're
92
+ # passed the section name and the array of tokens.
93
+ def on_section(name, content)
94
+ # Convert the tokenized content of this section into a Ruby
95
+ # string we can use.
96
+ code = compile(content)
97
+
98
+ # Compile the Ruby for this section now that we know what's
99
+ # inside the section.
100
+ ev(<<-compiled)
101
+ if v = ctx[#{name.to_sym.inspect}]
102
+ if v == true
103
+ #{code}
104
+ elsif v.is_a?(Proc)
105
+ v.call(#{code})
106
+ else
107
+ # Shortcut when passed non-array
108
+ v = [v] if v.respond_to?(:has_key?) || !v.respond_to?(:map)
109
+
110
+ v.map { |h| ctx.push(h); r = #{code}; ctx.pop; r }.join
111
+ end
112
+ end
113
+ compiled
114
+ end
115
+
116
+ # Fired when we find an inverted section. Just like `on_section`,
117
+ # we're passed the inverted section name and the array of tokens.
118
+ def on_inverted_section(name, content)
119
+ # Convert the tokenized content of this section into a Ruby
120
+ # string we can use.
121
+ code = compile(content)
122
+
123
+ # Compile the Ruby for this inverted section now that we know
124
+ # what's inside.
125
+ ev(<<-compiled)
126
+ v = ctx[#{name.to_sym.inspect}]
127
+ if v.nil? || v == false || v.respond_to?(:empty?) && v.empty?
128
+ #{code}
129
+ end
130
+ compiled
131
+ end
132
+
133
+ # Fired when the compiler finds a partial. We want to return code
134
+ # which calls a partial at runtime instead of expanding and
135
+ # including the partial's body to allow for recursive partials.
136
+ def on_partial(name)
137
+ ev("ctx.partial(#{name.to_sym.inspect})")
138
+ end
139
+
140
+ # An unescaped tag.
141
+ def on_utag(name)
142
+ ev("ctx[#{name.to_sym.inspect}]")
143
+ end
144
+
145
+ # An escaped tag.
146
+ def on_etag(name)
147
+ ev("CGI.escapeHTML(ctx[#{name.to_sym.inspect}].to_s)")
148
+ end
149
+
150
+ # An interpolation-friendly version of a string, for use within a
151
+ # Ruby string.
152
+ def ev(s)
153
+ "#\{#{s}}"
154
+ end
155
+
156
+ def str(s)
157
+ s.inspect[1..-2]
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,230 @@
1
+ require 'strscan'
2
+
3
+ class Mustache
4
+ # The Parser is responsible for taking a string template and
5
+ # converting it into an array of tokens and, really, expressions. It
6
+ # raises SyntaxError if there is anything it doesn't understand and
7
+ # knows which sigil corresponds to which tag type.
8
+ #
9
+ # For example, given this template:
10
+ #
11
+ # Hi {{thing}}!
12
+ #
13
+ # Run through the Parser we'll get these tokens:
14
+ #
15
+ # [:multi,
16
+ # [:static, "Hi "],
17
+ # [:mustache, :etag, "thing"],
18
+ # [:static, "!\n"]]
19
+ #
20
+ # You can see the array of tokens for any template with the
21
+ # mustache(1) command line tool:
22
+ #
23
+ # $ mustache --tokens test.mustache
24
+ # [:multi, [:static, "Hi "], [:mustache, :etag, "thing"], [:static, "!\n"]]
25
+ class Parser
26
+ # A SyntaxError is raised when the Parser comes across unclosed
27
+ # tags, sections, illegal content in tags, or anything of that
28
+ # sort.
29
+ class SyntaxError < StandardError
30
+ def initialize(message, position)
31
+ @message = message
32
+ @lineno, @column, @line = position
33
+ @stripped_line = @line.strip
34
+ @stripped_column = @column - (@line.size - @line.lstrip.size)
35
+ end
36
+
37
+ def to_s
38
+ <<-EOF
39
+ #{@message}
40
+ Line #{@lineno}
41
+ #{@stripped_line}
42
+ #{' ' * @stripped_column}^
43
+ EOF
44
+ end
45
+ end
46
+
47
+ # After these types of tags, all whitespace will be skipped.
48
+ SKIP_WHITESPACE = [ '#', '^', '/' ]
49
+
50
+ # The content allowed in a tag name.
51
+ ALLOWED_CONTENT = /(\w|[?!\/-])*/
52
+
53
+ # These types of tags allow any content,
54
+ # the rest only allow ALLOWED_CONTENT.
55
+ ANY_CONTENT = [ '!', '=' ]
56
+
57
+ attr_reader :scanner, :result
58
+ attr_writer :otag, :ctag
59
+
60
+ # Accepts an options hash which does nothing but may be used in
61
+ # the future.
62
+ def initialize(options = {})
63
+ @options = {}
64
+ end
65
+
66
+ # The opening tag delimiter. This may be changed at runtime.
67
+ def otag
68
+ @otag ||= '{{'
69
+ end
70
+
71
+ # The closing tag delimiter. This too may be changed at runtime.
72
+ def ctag
73
+ @ctag ||= '}}'
74
+ end
75
+
76
+ # Given a string template, returns an array of tokens.
77
+ def compile(template)
78
+ if template.respond_to?(:encoding)
79
+ @encoding = template.encoding
80
+ template = template.dup.force_encoding("BINARY")
81
+ else
82
+ @encoding = nil
83
+ end
84
+
85
+ # Keeps information about opened sections.
86
+ @sections = []
87
+ @result = [:multi]
88
+ @scanner = StringScanner.new(template)
89
+
90
+ # Scan until the end of the template.
91
+ until @scanner.eos?
92
+ scan_tags || scan_text
93
+ end
94
+
95
+ if !@sections.empty?
96
+ # We have parsed the whole file, but there's still opened sections.
97
+ type, pos, result = @sections.pop
98
+ error "Unclosed section #{type.inspect}", pos
99
+ end
100
+
101
+ @result
102
+ end
103
+
104
+ # Find {{mustaches}} and add them to the @result array.
105
+ def scan_tags
106
+ # Scan until we hit an opening delimiter.
107
+ return unless @scanner.scan(regexp(otag))
108
+
109
+ # Since {{= rewrites ctag, we store the ctag which should be used
110
+ # when parsing this specific tag.
111
+ current_ctag = self.ctag
112
+ type = @scanner.scan(/#|\^|\/|=|!|<|>|&|\{/)
113
+ @scanner.skip(/\s*/)
114
+
115
+ # ANY_CONTENT tags allow any character inside of them, while
116
+ # other tags (such as variables) are more strict.
117
+ if ANY_CONTENT.include?(type)
118
+ r = /\s*#{regexp(type)}?#{regexp(current_ctag)}/
119
+ content = scan_until_exclusive(r)
120
+ else
121
+ content = @scanner.scan(ALLOWED_CONTENT)
122
+ end
123
+
124
+ # We found {{ but we can't figure out what's going on inside.
125
+ error "Illegal content in tag" if content.empty?
126
+
127
+ # Based on the sigil, do what needs to be done.
128
+ case type
129
+ when '#'
130
+ block = [:multi]
131
+ @result << [:mustache, :section, content, block]
132
+ @sections << [content, position, @result]
133
+ @result = block
134
+ when '^'
135
+ block = [:multi]
136
+ @result << [:mustache, :inverted_section, content, block]
137
+ @sections << [content, position, @result]
138
+ @result = block
139
+ when '/'
140
+ section, pos, result = @sections.pop
141
+ @result = result
142
+
143
+ if section.nil?
144
+ error "Closing unopened #{content.inspect}"
145
+ elsif section != content
146
+ error "Unclosed section #{section.inspect}", pos
147
+ end
148
+ when '!'
149
+ # ignore comments
150
+ when '='
151
+ self.otag, self.ctag = content.split(' ', 2)
152
+ when '>', '<'
153
+ @result << [:mustache, :partial, content]
154
+ when '{', '&'
155
+ # The closing } in unescaped tags is just a hack for
156
+ # aesthetics.
157
+ type = "}" if type == "{"
158
+ @result << [:mustache, :utag, content]
159
+ else
160
+ @result << [:mustache, :etag, content]
161
+ end
162
+
163
+ # Skip whitespace and any balancing sigils after the content
164
+ # inside this tag.
165
+ @scanner.skip(/\s+/)
166
+ @scanner.skip(regexp(type)) if type
167
+
168
+ # Try to find the closing tag.
169
+ unless close = @scanner.scan(regexp(current_ctag))
170
+ error "Unclosed tag"
171
+ end
172
+
173
+ # Skip whitespace following this tag if we need to.
174
+ @scanner.skip(/\s+/) if SKIP_WHITESPACE.include?(type)
175
+ end
176
+
177
+ # Try to find static text, e.g. raw HTML with no {{mustaches}}.
178
+ def scan_text
179
+ text = scan_until_exclusive(regexp(otag))
180
+
181
+ if text.nil?
182
+ # Couldn't find any otag, which means the rest is just static text.
183
+ text = @scanner.rest
184
+ # Mark as done.
185
+ @scanner.clear
186
+ end
187
+
188
+ text.force_encoding(@encoding) if @encoding
189
+
190
+ @result << [:static, text]
191
+ end
192
+
193
+ # Scans the string until the pattern is matched. Returns the substring
194
+ # *excluding* the end of the match, advancing the scan pointer to that
195
+ # location. If there is no match, nil is returned.
196
+ def scan_until_exclusive(regexp)
197
+ pos = @scanner.pos
198
+ if @scanner.scan_until(regexp)
199
+ @scanner.pos -= @scanner.matched.size
200
+ @scanner.pre_match[pos..-1]
201
+ end
202
+ end
203
+
204
+ # Returns [lineno, column, line]
205
+ def position
206
+ # The rest of the current line
207
+ rest = @scanner.check_until(/\n|\Z/).to_s.chomp
208
+
209
+ # What we have parsed so far
210
+ parsed = @scanner.string[0...@scanner.pos]
211
+
212
+ lines = parsed.split("\n")
213
+
214
+ [ lines.size, lines.last.size - 1, lines.last + rest ]
215
+ end
216
+
217
+ # Used to quickly convert a string into a regular expression
218
+ # usable by the string scanner.
219
+ def regexp(thing)
220
+ /#{Regexp.escape(thing)}/
221
+ end
222
+
223
+ # Raises a SyntaxError. The message should be the name of the
224
+ # error - other details such as line number and position are
225
+ # handled for you.
226
+ def error(message, pos = position)
227
+ raise SyntaxError.new(message, pos)
228
+ end
229
+ end
230
+ end