erbal 1.1 → 1.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -8,21 +8,25 @@ Erbal is a lightweight ERB parser that uses the Ragel State Machine Compiler (ht
8
8
 
9
9
  >> require 'erbal'
10
10
  >> src = Erbal.new("<% a=1 -%>a is: <%= a -%>").parse
11
- => "@output_buffer = ''; a=1 ;\n@output_buffer << %Q`a is: \#{ a }`;@output_buffer"
11
+ => "@output_buffer = ''; a=1 ;\n@output_buffer.concat(%Q`a is: \#{ a }`);@output_buffer"
12
12
  >> eval(src)
13
13
  => "a is: 1"
14
14
 
15
15
  Erbal.new takes an optional 2nd argument which is a hash of options, available options are:
16
16
 
17
- * :buffer The name of the output buffer, default is '@output_buffer'
18
- * :buffer_initial_value The initial value of the output buffer, defaults to a blank string.
17
+ * <b>:buffer</b> - The name of the output buffer. Default is '@output_buffer'.
18
+ * <b>:buffer_initial_value</b> - The initial value of the output buffer. Default is a blank string.
19
+ * <b>:safe_concat_method</b> - The method to call on the buffer when concatenating a string which doesn't need escaping, i.e text outside of ERB tags uses the safe concatenation method. Default is 'concat'.
20
+ * <b>:unsafe_concat_method</b> - The method to call on the buffer when concatenating a string which DOES need escaping, i.e <%= some_method %> will result in the use of unsafe_concat_method. Default is 'concat'.
21
+ * <b>:safe_concat_method</b> - Normally the '<%=' tag uses the unsafe_concat_method, with this option you can specify a keyword to signify that safe_concat_method should be used instead. For example if safe_concat_method is set to 'raw' then <%= raw some_method %> will result in a concatenation using the safe_concat_method. Default is blank, i.e no keyword is specified.
22
+
23
+ <b>NOTE: Erbal itself does NOT perform escaping, it is the responsibility of your unsafe_concat_method to escape the string passed to it.</b>
19
24
 
20
25
  == Rails 2.3
21
26
 
22
27
  Create the file 'config/initializers/erbal.rb' containing:
23
28
 
24
- require 'erbal/rails23'
25
- ActionView::Template.register_template_handler :erb, ErbalTemplateHandler
29
+ require 'erbal/rails'
26
30
 
27
31
  == Rails 3
28
32
 
@@ -33,44 +37,46 @@ I've not looked into yet.. patches are welcome! ;)
33
37
  These benchmarks were run on a Mac OS X 10.6.4, 2.66 Ghx Intel Core i5 with 8 GB of 1067 MHz DDR3 RAM.
34
38
 
35
39
  Ruby: 1.8.7 (2010-04-19 patchlevel 253) [i686-darwin10.4.0], MBARI 0x6770, Ruby Enterprise Edition 2010.02
36
- Erubis: 2.6.6
37
- Erbal: 1.1
38
-
39
- => Parsing Benchmark
40
-
41
- => Erb
42
- 0.851 0.853 0.848 0.847 0.849 0.847
43
- => Average: 0.849
44
-
45
- => Erubis (using FastEruby engine)
46
- 0.442 0.442 0.444 0.442 0.441 0.442
47
- => Average: 0.442
48
40
 
49
- => Erubis (using default Eruby engine)
50
- 0.451 0.424 0.446 0.425 0.445 0.446
51
- => Average: 0.439
52
-
53
- => Erbal
54
- 0.036 0.039 0.066 0.038 0.067 0.039
55
- => Average: 0.047
56
-
57
- => eval() Benchmark
58
-
59
- => Erb
60
- 0.203 0.192 0.180 0.180 0.179 0.191
61
- => Average: 0.187
62
-
63
- => Erubis (using FastEruby engine)
64
- 0.129 0.127 0.128 0.128 0.114 0.128
65
- => Average: 0.125
41
+ Erubis: 2.6.6
66
42
 
67
- => Erubis (using default Eruby engine)
68
- 0.178 0.176 0.165 0.176 0.175 0.164
69
- => Average: 0.172
43
+ Erbal: 1.1
70
44
 
71
- => Erbal
72
- 0.111 0.125 0.124 0.110 0.133 0.124
73
- => Average: 0.121
45
+ => Parsing Benchmark
46
+
47
+ => Erb
48
+ 0.851 0.853 0.848 0.847 0.849 0.847
49
+ => Average: 0.849
50
+
51
+ => Erubis (using FastEruby engine)
52
+ 0.442 0.442 0.444 0.442 0.441 0.442
53
+ => Average: 0.442
54
+
55
+ => Erubis (using default Eruby engine)
56
+ 0.451 0.424 0.446 0.425 0.445 0.446
57
+ => Average: 0.439
58
+
59
+ => Erbal
60
+ 0.036 0.039 0.066 0.038 0.067 0.039
61
+ => Average: 0.047
62
+
63
+ => eval() Benchmark
64
+
65
+ => Erb
66
+ 0.203 0.192 0.180 0.180 0.179 0.191
67
+ => Average: 0.187
68
+
69
+ => Erubis (using FastEruby engine)
70
+ 0.129 0.127 0.128 0.128 0.114 0.128
71
+ => Average: 0.125
72
+
73
+ => Erubis (using default Eruby engine)
74
+ 0.178 0.176 0.165 0.176 0.175 0.164
75
+ => Average: 0.172
76
+
77
+ => Erbal
78
+ 0.111 0.125 0.124 0.110 0.133 0.124
79
+ => Average: 0.121
74
80
 
75
81
  == Contributing
76
82
 
data/ext/erbal/erbal.c CHANGED
@@ -3,12 +3,33 @@
3
3
 
4
4
  static VALUE cErbal;
5
5
 
6
+ void rb_erbal_free(erbal_parser *parser) {
7
+ free(parser->state);
8
+ free(parser);
9
+ }
10
+
6
11
  VALUE rb_erbal_alloc(VALUE klass) {
7
12
  erbal_parser *parser = ALLOC(erbal_parser);
8
- VALUE obj = Data_Wrap_Struct(klass, 0, free, parser);
13
+ parser->state = ALLOC(parser_state);
14
+ VALUE obj = Data_Wrap_Struct(klass, 0, rb_erbal_free, parser);
9
15
  return obj;
10
16
  }
11
17
 
18
+ void rb_erbal_setup_option(VALUE self, erbal_parser* parser, VALUE* parser_option, const char* key, const char* default_value) {
19
+ VALUE value = rb_hash_aref(parser->options, ID2SYM(rb_intern(key)));
20
+
21
+ if (!NIL_P(value)) {
22
+ Check_Type(value, T_STRING);
23
+ *(parser_option) = value;
24
+ } else {
25
+ *(parser_option) = rb_str_new2(default_value);
26
+ }
27
+
28
+ VALUE ivar = rb_str_new2("@");
29
+ ivar = rb_str_cat2(ivar, key);
30
+ rb_iv_set(self, RSTRING(ivar)->ptr, *(parser_option));
31
+ }
32
+
12
33
  VALUE rb_erbal_initialize(int argc, VALUE *argv, VALUE self) {
13
34
  VALUE str, options;
14
35
 
@@ -35,16 +56,10 @@ VALUE rb_erbal_initialize(int argc, VALUE *argv, VALUE self) {
35
56
  parser->debug = 0;
36
57
  }
37
58
 
38
- VALUE buffer_name_val = rb_hash_aref(parser->options, ID2SYM(rb_intern("buffer")));
39
-
40
- if (!NIL_P(buffer_name_val)) {
41
- Check_Type(buffer_name_val, T_STRING);
42
- parser->buffer_name = buffer_name_val;
43
- } else {
44
- parser->buffer_name = rb_str_new2("@output_buffer");
45
- }
46
-
47
- rb_iv_set(self, "@buffer_name", parser->buffer_name);
59
+ rb_erbal_setup_option(self, parser, &parser->buffer_name, "buffer", "@output_buffer");
60
+ rb_erbal_setup_option(self, parser, &parser->safe_concat_method, "safe_concat_method", "concat");
61
+ rb_erbal_setup_option(self, parser, &parser->unsafe_concat_method, "unsafe_concat_method", "concat");
62
+ rb_erbal_setup_option(self, parser, &parser->safe_concat_keyword, "safe_concat_keyword", "");
48
63
 
49
64
  return self;
50
65
  }
data/ext/erbal/extconf.rb CHANGED
@@ -1,2 +1,2 @@
1
1
  require 'mkmf'
2
- create_makefile("erbal")
2
+ create_makefile("erbal")
data/ext/erbal/parser.c CHANGED
@@ -4,78 +4,99 @@
4
4
  #include "parser.h"
5
5
 
6
6
 
7
- #line 16 "parser.rl"
7
+ #line 31 "parser.rl"
8
8
 
9
9
 
10
10
 
11
11
  #line 12 "parser.c"
12
- static const int erbal_parser_start = 1;
13
- static const int erbal_parser_first_final = 1;
12
+ static const int erbal_parser_start = 3;
13
+ static const int erbal_parser_first_final = 3;
14
14
  static const int erbal_parser_error = -1;
15
15
 
16
- static const int erbal_parser_en_main = 1;
16
+ static const int erbal_parser_en_main = 3;
17
17
 
18
18
 
19
- #line 19 "parser.rl"
19
+ #line 34 "parser.rl"
20
20
 
21
21
  static char *ts, *te, *p, *pe, *eof;
22
22
  static int act, cs;
23
23
 
24
24
  inline void erbal_parser_tag_open_common(erbal_parser *parser, int shift) {
25
- if (parser->chars_seen != 0) {
26
- if (!parser->in_buffer_shift) {
27
- erbal_open_buffer_shift(parser);
25
+ if (parser->state->chars_seen != 0) {
26
+ if (!parser->state->in_concat) {
27
+ erbal_open_buffer_concat(parser, 1);
28
28
  }
29
29
 
30
30
  erbal_concat_chars_seen(parser, shift);
31
- parser->chars_seen = 0;
31
+ parser->state->chars_seen = 0;
32
32
  }
33
33
  }
34
34
 
35
35
  inline void erbal_parser_tag_open(erbal_parser *parser) {
36
36
  erbal_parser_tag_open_common(parser, -1);
37
- parser->state = TAG_OPEN;
37
+ parser->state->tag = TAG_OPEN;
38
38
  }
39
39
 
40
40
  inline void erbal_parser_tag_open_with_dash(erbal_parser *parser) {
41
41
  erbal_parser_tag_open_common(parser, -2);
42
- parser->state = TAG_OPEN;
42
+ parser->state->tag = TAG_OPEN;
43
43
  }
44
44
 
45
- inline void erbal_parser_tag_open_for_output(erbal_parser *parser) {
45
+ inline void erbal_parser_tag_open_choose_concat(erbal_parser *parser) {
46
+ if (strcmp(RSTRING(parser->keyword)->ptr, RSTRING(parser->safe_concat_keyword)->ptr) != 0) {
47
+ /* Keyword doesn't match, reset the buffer to the start of the expression match and act as if a keyword wasn't seen. */
48
+ p = parser->keyword_preceding_whitespace - 1;
49
+ erbal_parser_tag_open_for_unsafe_concat(parser);
50
+ } else {
51
+ /* Rewind the buffer to preserve whitespace following the keyword. */
52
+ p = p - (parser->keyword_trailing_whitespace - parser->keyword_end);
53
+ erbal_parser_tag_open_for_safe_concat(parser);
54
+ }
55
+ }
56
+
57
+ inline void erbal_parser_tag_open_for_unsafe_concat(erbal_parser *parser) {
46
58
  erbal_parser_tag_open_common(parser, -2);
47
- parser->state = TAG_OPEN_FOR_OUTPUT;
59
+ parser->state->tag = TAG_OPEN_FOR_UNSAFE_CONCAT;
60
+ }
61
+
62
+ inline void erbal_parser_tag_open_for_safe_concat(erbal_parser *parser) {
63
+ erbal_parser_tag_open_common(parser, -2);
64
+ parser->state->tag = TAG_OPEN_FOR_SAFE_CONCAT;
48
65
  }
49
66
 
50
67
  inline void erbal_parser_tag_open_for_comment(erbal_parser *parser) {
51
68
  erbal_parser_tag_open_common(parser, -2);
52
- parser->state = TAG_OPEN_FOR_COMMENT;
69
+ parser->state->tag = TAG_OPEN_FOR_COMMENT;
53
70
  }
54
71
 
55
72
  inline void erbal_parser_non_tag(erbal_parser *parser) {
56
- parser->chars_seen += 1;
73
+ parser->state->chars_seen += 1;
57
74
  }
58
75
 
59
76
  inline void erbal_parser_tag_close_common(erbal_parser *parser, int tag_size) {
60
- if (parser->state == TAG_OPEN_FOR_OUTPUT) {
61
- if (!parser->in_buffer_shift) {
62
- erbal_open_buffer_shift(parser);
77
+ if (parser->state->tag == TAG_OPEN_FOR_UNSAFE_CONCAT || parser->state->tag == TAG_OPEN_FOR_SAFE_CONCAT) {
78
+ if (!parser->state->in_concat) {
79
+ if (parser->state->tag == TAG_OPEN_FOR_SAFE_CONCAT) {
80
+ erbal_open_buffer_concat(parser, 1);
81
+ } else {
82
+ erbal_open_buffer_concat(parser, 0);
83
+ }
63
84
  }
64
85
 
65
86
  rb_str_buf_cat(parser->src, "#{", 2);
66
87
  erbal_concat_chars_seen(parser, -tag_size);
67
88
  rb_str_buf_cat(parser->src, "}", 1);
68
- } else if (parser->state == TAG_OPEN) {
69
- if (parser->in_buffer_shift) {
70
- erbal_close_buffer_shift(parser);
89
+ } else if (parser->state->tag == TAG_OPEN) {
90
+ if (parser->state->in_concat) {
91
+ erbal_close_buffer_concat(parser);
71
92
  }
72
93
 
73
94
  erbal_concat_chars_seen(parser, -tag_size);
74
95
  rb_str_buf_cat(parser->src, ";\n", 2);
75
96
  }
76
97
 
77
- parser->state = OUTSIDE_TAG;
78
- parser->chars_seen = 0;
98
+ parser->state->tag = OUTSIDE_TAG;
99
+ parser->state->chars_seen = 0;
79
100
  }
80
101
 
81
102
  inline void erbal_parser_tag_close_with_trim(erbal_parser *parser) {
@@ -95,8 +116,8 @@ inline VALUE erbal_escape_special_chars(erbal_parser *parser, int shift) {
95
116
  int i, n, slashes_seen = 0;
96
117
  char *current_char;
97
118
 
98
- for (i = 0; i < parser->chars_seen; i++) {
99
- current_char = (((p + shift) - parser->chars_seen) + i);
119
+ for (i = 0; i < parser->state->chars_seen; i++) {
120
+ current_char = (((p + shift) - parser->state->chars_seen) + i);
100
121
 
101
122
  if (*current_char == '#' || *current_char == '`') {
102
123
  if (slashes_seen == 0) {
@@ -122,39 +143,48 @@ inline VALUE erbal_escape_special_chars(erbal_parser *parser, int shift) {
122
143
  }
123
144
 
124
145
  inline void erbal_concat_chars_seen(erbal_parser *parser, int shift) {
125
- if (parser->chars_seen != 0) {
126
- if (parser->in_buffer_shift && parser->state == OUTSIDE_TAG) {
146
+ if (parser->state->chars_seen != 0) {
147
+ if (parser->state->in_concat && parser->state->tag == OUTSIDE_TAG) {
127
148
  rb_str_concat(parser->src, erbal_escape_special_chars(parser, shift));
128
149
  } else {
129
- rb_str_buf_cat(parser->src, ((p + shift) - parser->chars_seen), parser->chars_seen);
150
+ rb_str_buf_cat(parser->src, ((p + shift) - parser->state->chars_seen), parser->state->chars_seen);
130
151
  }
131
152
  }
132
153
 
133
- parser->chars_seen = 0;
154
+ parser->state->chars_seen = 0;
134
155
  }
135
156
 
136
- inline void erbal_open_buffer_shift(erbal_parser *parser) {
157
+ inline void erbal_open_buffer_concat(erbal_parser *parser, int safe_concat) {
137
158
  rb_str_concat(parser->src, parser->buffer_name);
138
- rb_str_buf_cat(parser->src, " << %Q`", 7);
139
- parser->in_buffer_shift = 1;
159
+ rb_str_buf_cat(parser->src, ".", 1);
160
+
161
+ if (safe_concat) {
162
+ rb_str_concat(parser->src, parser->safe_concat_method);
163
+ } else {
164
+ rb_str_concat(parser->src, parser->unsafe_concat_method);
165
+ }
166
+
167
+ rb_str_buf_cat(parser->src, "(", 1);
168
+ rb_str_buf_cat(parser->src, "%Q`", 3);
169
+ parser->state->in_concat = 1;
140
170
  }
141
171
 
142
- inline void erbal_close_buffer_shift(erbal_parser *parser) {
143
- rb_str_buf_cat(parser->src, "`;", 2);
144
- parser->in_buffer_shift = 0;
172
+ inline void erbal_close_buffer_concat(erbal_parser *parser) {
173
+ rb_str_buf_cat(parser->src, "`);", 3);
174
+ parser->state->in_concat = 0;
145
175
  }
146
176
 
147
177
  inline void erbal_parser_finish(erbal_parser *parser) {
148
- if (parser->chars_seen != 0) {
149
- if (!parser->in_buffer_shift) {
150
- erbal_open_buffer_shift(parser);
178
+ if (parser->state->chars_seen != 0) {
179
+ if (!parser->state->in_concat) {
180
+ erbal_open_buffer_concat(parser, 1);
151
181
  }
152
182
 
153
183
  erbal_concat_chars_seen(parser, 0);
154
184
  }
155
185
 
156
- if (parser->in_buffer_shift) {
157
- erbal_close_buffer_shift(parser);
186
+ if (parser->state->in_concat) {
187
+ erbal_close_buffer_concat(parser);
158
188
  }
159
189
 
160
190
  rb_str_concat(parser->src, parser->buffer_name);
@@ -165,9 +195,9 @@ inline void erbal_parser_finish(erbal_parser *parser) {
165
195
  }
166
196
 
167
197
  void erbal_parser_init(VALUE self, erbal_parser *parser) {
168
- parser->chars_seen = 0;
169
- parser->in_buffer_shift = 0;
170
- parser->state = OUTSIDE_TAG;
198
+ parser->state->chars_seen = 0;
199
+ parser->state->in_concat = 0;
200
+ parser->state->tag = OUTSIDE_TAG;
171
201
  parser->src = rb_str_dup(parser->buffer_name);
172
202
 
173
203
  rb_iv_set(self, "@src", parser->src);
@@ -184,7 +214,7 @@ void erbal_parser_init(VALUE self, erbal_parser *parser) {
184
214
  }
185
215
 
186
216
 
187
- #line 188 "parser.c"
217
+ #line 218 "parser.c"
188
218
  {
189
219
  cs = erbal_parser_start;
190
220
  ts = 0;
@@ -192,89 +222,99 @@ void erbal_parser_init(VALUE self, erbal_parser *parser) {
192
222
  act = 0;
193
223
  }
194
224
 
195
- #line 186 "parser.rl"
225
+ #line 231 "parser.rl"
196
226
  }
197
227
 
198
228
  void erbal_parser_exec(erbal_parser *parser) {
199
229
  p = RSTRING(parser->str)->ptr;
200
230
  pe = p + strlen(p);
201
231
 
202
- #line 203 "parser.c"
232
+ #line 233 "parser.c"
203
233
  {
204
234
  if ( p == pe )
205
235
  goto _test_eof;
206
236
  switch ( cs )
207
237
  {
208
238
  tr0:
209
- #line 14 "parser.rl"
239
+ #line 23 "parser.rl"
210
240
  {{p = ((te))-1;}{ erbal_parser_non_tag(parser); }}
211
- goto st1;
241
+ goto st3;
212
242
  tr1:
213
- #line 12 "parser.rl"
243
+ #line 21 "parser.rl"
214
244
  {te = p+1;{ erbal_parser_tag_close_with_trim(parser); }}
215
- goto st1;
245
+ goto st3;
216
246
  tr2:
217
- #line 14 "parser.rl"
247
+ #line 20 "parser.rl"
248
+ {{p = ((te))-1;}{ erbal_parser_tag_open_for_unsafe_concat(parser); }}
249
+ goto st3;
250
+ tr7:
251
+ #line 23 "parser.rl"
218
252
  {te = p+1;{ erbal_parser_non_tag(parser); }}
219
- goto st1;
220
- tr6:
221
- #line 14 "parser.rl"
253
+ goto st3;
254
+ tr11:
255
+ #line 23 "parser.rl"
222
256
  {te = p;p--;{ erbal_parser_non_tag(parser); }}
223
- goto st1;
224
- tr7:
225
- #line 13 "parser.rl"
257
+ goto st3;
258
+ tr12:
259
+ #line 22 "parser.rl"
226
260
  {te = p+1;{ erbal_parser_tag_close(parser); }}
227
- goto st1;
228
- tr10:
229
- #line 8 "parser.rl"
261
+ goto st3;
262
+ tr15:
263
+ #line 17 "parser.rl"
230
264
  {te = p;p--;{ erbal_parser_tag_open(parser); }}
231
- goto st1;
232
- tr11:
233
- #line 10 "parser.rl"
265
+ goto st3;
266
+ tr16:
267
+ #line 19 "parser.rl"
234
268
  {te = p+1;{ erbal_parser_tag_open_for_comment(parser); }}
235
- goto st1;
236
- tr12:
237
- #line 9 "parser.rl"
269
+ goto st3;
270
+ tr17:
271
+ #line 18 "parser.rl"
238
272
  {te = p+1;{ erbal_parser_tag_open_with_dash(parser); }}
239
- goto st1;
240
- tr13:
241
- #line 11 "parser.rl"
242
- {te = p+1;{ erbal_parser_tag_open_for_output(parser); }}
243
- goto st1;
244
- st1:
273
+ goto st3;
274
+ tr19:
275
+ #line 20 "parser.rl"
276
+ {te = p;p--;{ erbal_parser_tag_open_for_unsafe_concat(parser); }}
277
+ goto st3;
278
+ tr22:
279
+ #line 9 "parser.rl"
280
+ { parser->keyword_trailing_whitespace = p; }
281
+ #line 29 "parser.rl"
282
+ {te = p;p--;{ erbal_parser_tag_open_choose_concat(parser); }}
283
+ goto st3;
284
+ st3:
245
285
  #line 1 "NONE"
246
286
  {ts = 0;}
247
287
  if ( ++p == pe )
248
- goto _test_eof1;
249
- case 1:
288
+ goto _test_eof3;
289
+ case 3:
250
290
  #line 1 "NONE"
251
291
  {ts = p;}
252
- #line 253 "parser.c"
292
+ #line 293 "parser.c"
253
293
  switch( (*p) ) {
254
- case 37: goto st2;
255
- case 45: goto tr4;
256
- case 60: goto st4;
294
+ case 37: goto st4;
295
+ case 45: goto tr9;
296
+ case 60: goto st6;
257
297
  }
258
- goto tr2;
259
- st2:
298
+ goto tr7;
299
+ st4:
260
300
  if ( ++p == pe )
261
- goto _test_eof2;
262
- case 2:
301
+ goto _test_eof4;
302
+ case 4:
263
303
  if ( (*p) == 62 )
264
- goto tr7;
265
- goto tr6;
266
- tr4:
304
+ goto tr12;
305
+ goto tr11;
306
+ tr9:
267
307
  #line 1 "NONE"
268
308
  {te = p+1;}
269
- goto st3;
270
- st3:
309
+ goto st5;
310
+ st5:
271
311
  if ( ++p == pe )
272
- goto _test_eof3;
273
- case 3:
274
- #line 275 "parser.c"
312
+ goto _test_eof5;
313
+ case 5:
314
+ #line 315 "parser.c"
275
315
  if ( (*p) == 37 )
276
316
  goto st0;
277
- goto tr6;
317
+ goto tr11;
278
318
  st0:
279
319
  if ( ++p == pe )
280
320
  goto _test_eof0;
@@ -282,45 +322,116 @@ case 0:
282
322
  if ( (*p) == 62 )
283
323
  goto tr1;
284
324
  goto tr0;
285
- st4:
325
+ st6:
286
326
  if ( ++p == pe )
287
- goto _test_eof4;
288
- case 4:
327
+ goto _test_eof6;
328
+ case 6:
289
329
  if ( (*p) == 37 )
290
- goto st5;
291
- goto tr6;
292
- st5:
330
+ goto st7;
331
+ goto tr11;
332
+ st7:
293
333
  if ( ++p == pe )
294
- goto _test_eof5;
295
- case 5:
334
+ goto _test_eof7;
335
+ case 7:
296
336
  switch( (*p) ) {
297
- case 35: goto tr11;
298
- case 45: goto tr12;
299
- case 61: goto tr13;
337
+ case 35: goto tr16;
338
+ case 45: goto tr17;
339
+ case 61: goto tr18;
300
340
  }
301
- goto tr10;
341
+ goto tr15;
342
+ tr18:
343
+ #line 1 "NONE"
344
+ {te = p+1;}
345
+ goto st8;
346
+ st8:
347
+ if ( ++p == pe )
348
+ goto _test_eof8;
349
+ case 8:
350
+ #line 351 "parser.c"
351
+ if ( (*p) == 32 )
352
+ goto tr20;
353
+ if ( 97 <= (*p) && (*p) <= 122 )
354
+ goto tr21;
355
+ goto tr19;
356
+ tr20:
357
+ #line 8 "parser.rl"
358
+ { parser->keyword_preceding_whitespace = p; }
359
+ goto st1;
360
+ st1:
361
+ if ( ++p == pe )
362
+ goto _test_eof1;
363
+ case 1:
364
+ #line 365 "parser.c"
365
+ if ( (*p) == 32 )
366
+ goto st1;
367
+ if ( 97 <= (*p) && (*p) <= 122 )
368
+ goto tr4;
369
+ goto tr2;
370
+ tr4:
371
+ #line 7 "parser.rl"
372
+ { parser->keyword_start = p; }
373
+ goto st2;
374
+ tr21:
375
+ #line 8 "parser.rl"
376
+ { parser->keyword_preceding_whitespace = p; }
377
+ #line 7 "parser.rl"
378
+ { parser->keyword_start = p; }
379
+ goto st2;
380
+ st2:
381
+ if ( ++p == pe )
382
+ goto _test_eof2;
383
+ case 2:
384
+ #line 385 "parser.c"
385
+ if ( (*p) == 32 )
386
+ goto tr5;
387
+ if ( 97 <= (*p) && (*p) <= 122 )
388
+ goto st2;
389
+ goto tr2;
390
+ tr5:
391
+ #line 11 "parser.rl"
392
+ {
393
+ parser->keyword_end = p;
394
+ parser->keyword = rb_str_new(parser->keyword_start, (p - parser->keyword_start));
395
+ }
396
+ goto st9;
397
+ st9:
398
+ if ( ++p == pe )
399
+ goto _test_eof9;
400
+ case 9:
401
+ #line 402 "parser.c"
402
+ if ( (*p) == 32 )
403
+ goto st9;
404
+ goto tr22;
302
405
  }
303
- _test_eof1: cs = 1; goto _test_eof;
304
- _test_eof2: cs = 2; goto _test_eof;
305
406
  _test_eof3: cs = 3; goto _test_eof;
306
- _test_eof0: cs = 0; goto _test_eof;
307
407
  _test_eof4: cs = 4; goto _test_eof;
308
408
  _test_eof5: cs = 5; goto _test_eof;
409
+ _test_eof0: cs = 0; goto _test_eof;
410
+ _test_eof6: cs = 6; goto _test_eof;
411
+ _test_eof7: cs = 7; goto _test_eof;
412
+ _test_eof8: cs = 8; goto _test_eof;
413
+ _test_eof1: cs = 1; goto _test_eof;
414
+ _test_eof2: cs = 2; goto _test_eof;
415
+ _test_eof9: cs = 9; goto _test_eof;
309
416
 
310
417
  _test_eof: {}
311
418
  if ( p == eof )
312
419
  {
313
420
  switch ( cs ) {
314
- case 2: goto tr6;
315
- case 3: goto tr6;
421
+ case 4: goto tr11;
422
+ case 5: goto tr11;
316
423
  case 0: goto tr0;
317
- case 4: goto tr6;
318
- case 5: goto tr10;
424
+ case 6: goto tr11;
425
+ case 7: goto tr15;
426
+ case 8: goto tr19;
427
+ case 1: goto tr2;
428
+ case 2: goto tr2;
429
+ case 9: goto tr22;
319
430
  }
320
431
  }
321
432
 
322
433
  }
323
434
 
324
- #line 192 "parser.rl"
435
+ #line 237 "parser.rl"
325
436
  erbal_parser_finish(parser);
326
437
  }
data/ext/erbal/parser.h CHANGED
@@ -3,29 +3,40 @@
3
3
 
4
4
  #include "ruby.h"
5
5
 
6
+ typedef struct parser_state {
7
+ unsigned int tag;
8
+ unsigned int chars_seen;
9
+ unsigned int in_concat;
10
+ } parser_state;
11
+
6
12
  typedef struct erbal_parser {
7
- unsigned int state, chars_seen, in_buffer_shift, debug;
8
- VALUE str, src, buffer_name, options;
13
+ parser_state *state;
14
+ unsigned int debug;
15
+ VALUE str, src, buffer_name, options, safe_concat_method, unsafe_concat_method, keyword, safe_concat_keyword;
16
+ char *keyword_start, *keyword_end, *keyword_trailing_whitespace, *keyword_preceding_whitespace;
9
17
  } erbal_parser;
10
18
 
11
19
  inline void erbal_parser_tag_open(erbal_parser*);
12
20
  inline void erbal_parser_tag_open_with_dash(erbal_parser*);
13
21
  inline void erbal_parser_tag_open_for_comment(erbal_parser*);
14
- inline void erbal_parser_tag_open_for_output(erbal_parser*);
22
+ inline void erbal_parser_tag_open_choose_concat(erbal_parser*);
23
+ inline void erbal_parser_tag_open_for_unsafe_concat(erbal_parser*);
24
+ inline void erbal_parser_tag_open_for_safe_concat(erbal_parser*);
15
25
  inline void erbal_parser_non_tag(erbal_parser*);
16
26
  inline void erbal_parser_tag_close(erbal_parser*);
17
27
  inline void erbal_parser_tag_close_with_trim(erbal_parser*);
18
- inline void erbal_parser_tag_close_common(erbal_parser*, int shift);
28
+ inline void erbal_parser_tag_close_common(erbal_parser*, int);
19
29
  inline void erbal_parser_finish(erbal_parser*);
20
- inline void erbal_concat_chars_seen(erbal_parser*, int shift);
21
- inline void erbal_parser_tag_open_common(erbal_parser*, int shift);
22
- inline void erbal_open_buffer_shift(erbal_parser*);
23
- inline void erbal_close_buffer_shift(erbal_parser*);
24
- inline VALUE erbal_escape_special_chars(erbal_parser*, int shift);
30
+ inline void erbal_concat_chars_seen(erbal_parser*, int);
31
+ inline void erbal_parser_tag_open_common(erbal_parser*, int);
32
+ inline void erbal_open_buffer_concat(erbal_parser*, int);
33
+ inline void erbal_close_buffer_concat(erbal_parser*);
34
+ inline VALUE erbal_escape_special_chars(erbal_parser*, int);
25
35
 
26
- #define TAG_OPEN 1
27
- #define TAG_OPEN_FOR_COMMENT 2
28
- #define TAG_OPEN_FOR_OUTPUT 3
29
- #define OUTSIDE_TAG 4
36
+ #define TAG_OPEN 1
37
+ #define TAG_OPEN_FOR_COMMENT 2
38
+ #define TAG_OPEN_FOR_SAFE_CONCAT 3
39
+ #define TAG_OPEN_FOR_UNSAFE_CONCAT 4
40
+ #define OUTSIDE_TAG 5
30
41
 
31
42
  #endif
data/ext/erbal/parser.rl CHANGED
@@ -4,14 +4,29 @@
4
4
  %%{
5
5
  machine erbal_parser;
6
6
 
7
+ action keyword_start { parser->keyword_start = fpc; }
8
+ action keyword_preceding_whitespace { parser->keyword_preceding_whitespace = fpc; }
9
+ action keyword_trailing_whitespace { parser->keyword_trailing_whitespace = fpc; }
10
+
11
+ action keyword_end {
12
+ parser->keyword_end = fpc;
13
+ parser->keyword = rb_str_new(parser->keyword_start, (fpc - parser->keyword_start));
14
+ }
15
+
7
16
  main := |*
8
17
  '<%' => { erbal_parser_tag_open(parser); };
9
18
  '<%-' => { erbal_parser_tag_open_with_dash(parser); };
10
19
  '<%#' => { erbal_parser_tag_open_for_comment(parser); };
11
- '<%=' => { erbal_parser_tag_open_for_output(parser); };
20
+ '<%=' => { erbal_parser_tag_open_for_unsafe_concat(parser); };
12
21
  '-%>' => { erbal_parser_tag_close_with_trim(parser); };
13
22
  '%>' => { erbal_parser_tag_close(parser); };
14
23
  any => { erbal_parser_non_tag(parser); };
24
+
25
+ '<%=' (
26
+ [ ]* >keyword_preceding_whitespace
27
+ [a-z]+ >keyword_start %keyword_end
28
+ [ ]+ %keyword_trailing_whitespace
29
+ ) => { erbal_parser_tag_open_choose_concat(parser); };
15
30
  *|;
16
31
  }%%
17
32
 
@@ -21,60 +36,81 @@ static char *ts, *te, *p, *pe, *eof;
21
36
  static int act, cs;
22
37
 
23
38
  inline void erbal_parser_tag_open_common(erbal_parser *parser, int shift) {
24
- if (parser->chars_seen != 0) {
25
- if (!parser->in_buffer_shift) {
26
- erbal_open_buffer_shift(parser);
39
+ if (parser->state->chars_seen != 0) {
40
+ if (!parser->state->in_concat) {
41
+ erbal_open_buffer_concat(parser, 1);
27
42
  }
28
43
 
29
44
  erbal_concat_chars_seen(parser, shift);
30
- parser->chars_seen = 0;
45
+ parser->state->chars_seen = 0;
31
46
  }
32
47
  }
33
48
 
34
49
  inline void erbal_parser_tag_open(erbal_parser *parser) {
35
50
  erbal_parser_tag_open_common(parser, -1);
36
- parser->state = TAG_OPEN;
51
+ parser->state->tag = TAG_OPEN;
37
52
  }
38
53
 
39
54
  inline void erbal_parser_tag_open_with_dash(erbal_parser *parser) {
40
55
  erbal_parser_tag_open_common(parser, -2);
41
- parser->state = TAG_OPEN;
56
+ parser->state->tag = TAG_OPEN;
42
57
  }
43
58
 
44
- inline void erbal_parser_tag_open_for_output(erbal_parser *parser) {
59
+ inline void erbal_parser_tag_open_choose_concat(erbal_parser *parser) {
60
+ if (strcmp(RSTRING(parser->keyword)->ptr, RSTRING(parser->safe_concat_keyword)->ptr) != 0) {
61
+ /* Keyword doesn't match, reset the buffer to the start of the expression match and act as if a keyword wasn't seen. */
62
+ p = parser->keyword_preceding_whitespace - 1;
63
+ erbal_parser_tag_open_for_unsafe_concat(parser);
64
+ } else {
65
+ /* Rewind the buffer to preserve whitespace following the keyword. */
66
+ p = p - (parser->keyword_trailing_whitespace - parser->keyword_end);
67
+ erbal_parser_tag_open_for_safe_concat(parser);
68
+ }
69
+ }
70
+
71
+ inline void erbal_parser_tag_open_for_unsafe_concat(erbal_parser *parser) {
45
72
  erbal_parser_tag_open_common(parser, -2);
46
- parser->state = TAG_OPEN_FOR_OUTPUT;
73
+ parser->state->tag = TAG_OPEN_FOR_UNSAFE_CONCAT;
74
+ }
75
+
76
+ inline void erbal_parser_tag_open_for_safe_concat(erbal_parser *parser) {
77
+ erbal_parser_tag_open_common(parser, -2);
78
+ parser->state->tag = TAG_OPEN_FOR_SAFE_CONCAT;
47
79
  }
48
80
 
49
81
  inline void erbal_parser_tag_open_for_comment(erbal_parser *parser) {
50
82
  erbal_parser_tag_open_common(parser, -2);
51
- parser->state = TAG_OPEN_FOR_COMMENT;
83
+ parser->state->tag = TAG_OPEN_FOR_COMMENT;
52
84
  }
53
85
 
54
86
  inline void erbal_parser_non_tag(erbal_parser *parser) {
55
- parser->chars_seen += 1;
87
+ parser->state->chars_seen += 1;
56
88
  }
57
89
 
58
90
  inline void erbal_parser_tag_close_common(erbal_parser *parser, int tag_size) {
59
- if (parser->state == TAG_OPEN_FOR_OUTPUT) {
60
- if (!parser->in_buffer_shift) {
61
- erbal_open_buffer_shift(parser);
91
+ if (parser->state->tag == TAG_OPEN_FOR_UNSAFE_CONCAT || parser->state->tag == TAG_OPEN_FOR_SAFE_CONCAT) {
92
+ if (!parser->state->in_concat) {
93
+ if (parser->state->tag == TAG_OPEN_FOR_SAFE_CONCAT) {
94
+ erbal_open_buffer_concat(parser, 1);
95
+ } else {
96
+ erbal_open_buffer_concat(parser, 0);
97
+ }
62
98
  }
63
99
 
64
100
  rb_str_buf_cat(parser->src, "#{", 2);
65
101
  erbal_concat_chars_seen(parser, -tag_size);
66
102
  rb_str_buf_cat(parser->src, "}", 1);
67
- } else if (parser->state == TAG_OPEN) {
68
- if (parser->in_buffer_shift) {
69
- erbal_close_buffer_shift(parser);
103
+ } else if (parser->state->tag == TAG_OPEN) {
104
+ if (parser->state->in_concat) {
105
+ erbal_close_buffer_concat(parser);
70
106
  }
71
107
 
72
108
  erbal_concat_chars_seen(parser, -tag_size);
73
109
  rb_str_buf_cat(parser->src, ";\n", 2);
74
110
  }
75
111
 
76
- parser->state = OUTSIDE_TAG;
77
- parser->chars_seen = 0;
112
+ parser->state->tag = OUTSIDE_TAG;
113
+ parser->state->chars_seen = 0;
78
114
  }
79
115
 
80
116
  inline void erbal_parser_tag_close_with_trim(erbal_parser *parser) {
@@ -94,8 +130,8 @@ inline VALUE erbal_escape_special_chars(erbal_parser *parser, int shift) {
94
130
  int i, n, slashes_seen = 0;
95
131
  char *current_char;
96
132
 
97
- for (i = 0; i < parser->chars_seen; i++) {
98
- current_char = (((p + shift) - parser->chars_seen) + i);
133
+ for (i = 0; i < parser->state->chars_seen; i++) {
134
+ current_char = (((p + shift) - parser->state->chars_seen) + i);
99
135
 
100
136
  if (*current_char == '#' || *current_char == '`') {
101
137
  if (slashes_seen == 0) {
@@ -121,39 +157,48 @@ inline VALUE erbal_escape_special_chars(erbal_parser *parser, int shift) {
121
157
  }
122
158
 
123
159
  inline void erbal_concat_chars_seen(erbal_parser *parser, int shift) {
124
- if (parser->chars_seen != 0) {
125
- if (parser->in_buffer_shift && parser->state == OUTSIDE_TAG) {
160
+ if (parser->state->chars_seen != 0) {
161
+ if (parser->state->in_concat && parser->state->tag == OUTSIDE_TAG) {
126
162
  rb_str_concat(parser->src, erbal_escape_special_chars(parser, shift));
127
163
  } else {
128
- rb_str_buf_cat(parser->src, ((p + shift) - parser->chars_seen), parser->chars_seen);
164
+ rb_str_buf_cat(parser->src, ((p + shift) - parser->state->chars_seen), parser->state->chars_seen);
129
165
  }
130
166
  }
131
167
 
132
- parser->chars_seen = 0;
168
+ parser->state->chars_seen = 0;
133
169
  }
134
170
 
135
- inline void erbal_open_buffer_shift(erbal_parser *parser) {
171
+ inline void erbal_open_buffer_concat(erbal_parser *parser, int safe_concat) {
136
172
  rb_str_concat(parser->src, parser->buffer_name);
137
- rb_str_buf_cat(parser->src, " << %Q`", 7);
138
- parser->in_buffer_shift = 1;
173
+ rb_str_buf_cat(parser->src, ".", 1);
174
+
175
+ if (safe_concat) {
176
+ rb_str_concat(parser->src, parser->safe_concat_method);
177
+ } else {
178
+ rb_str_concat(parser->src, parser->unsafe_concat_method);
179
+ }
180
+
181
+ rb_str_buf_cat(parser->src, "(", 1);
182
+ rb_str_buf_cat(parser->src, "%Q`", 3);
183
+ parser->state->in_concat = 1;
139
184
  }
140
185
 
141
- inline void erbal_close_buffer_shift(erbal_parser *parser) {
142
- rb_str_buf_cat(parser->src, "`;", 2);
143
- parser->in_buffer_shift = 0;
186
+ inline void erbal_close_buffer_concat(erbal_parser *parser) {
187
+ rb_str_buf_cat(parser->src, "`);", 3);
188
+ parser->state->in_concat = 0;
144
189
  }
145
190
 
146
191
  inline void erbal_parser_finish(erbal_parser *parser) {
147
- if (parser->chars_seen != 0) {
148
- if (!parser->in_buffer_shift) {
149
- erbal_open_buffer_shift(parser);
192
+ if (parser->state->chars_seen != 0) {
193
+ if (!parser->state->in_concat) {
194
+ erbal_open_buffer_concat(parser, 1);
150
195
  }
151
196
 
152
197
  erbal_concat_chars_seen(parser, 0);
153
198
  }
154
199
 
155
- if (parser->in_buffer_shift) {
156
- erbal_close_buffer_shift(parser);
200
+ if (parser->state->in_concat) {
201
+ erbal_close_buffer_concat(parser);
157
202
  }
158
203
 
159
204
  rb_str_concat(parser->src, parser->buffer_name);
@@ -164,9 +209,9 @@ inline void erbal_parser_finish(erbal_parser *parser) {
164
209
  }
165
210
 
166
211
  void erbal_parser_init(VALUE self, erbal_parser *parser) {
167
- parser->chars_seen = 0;
168
- parser->in_buffer_shift = 0;
169
- parser->state = OUTSIDE_TAG;
212
+ parser->state->chars_seen = 0;
213
+ parser->state->in_concat = 0;
214
+ parser->state->tag = OUTSIDE_TAG;
170
215
  parser->src = rb_str_dup(parser->buffer_name);
171
216
 
172
217
  rb_iv_set(self, "@src", parser->src);
@@ -0,0 +1,42 @@
1
+ require 'erbal'
2
+
3
+ class Erbal
4
+ module Rails
5
+ class Rails_2_3_10_TemplateHandler < ActionView::TemplateHandler
6
+ include ActionView::TemplateHandlers::Compilable
7
+ def compile(template)
8
+ ::Erbal.new("<% __in_erb_template=true %>#{template.source}", {:buffer => '@output_buffer', :buffer_initial_value => 'ActiveSupport::SafeBuffer.new',
9
+ :safe_concat_method => 'safe_concat', :unsafe_concat_method => 'concat', :safe_concat_keyword => 'raw'}).parse
10
+ end
11
+ end
12
+
13
+ def self.version_unsupported
14
+ raise "Sorry, this version of Erbal doesn't support Rails #{ActionPack::VERSION::MAJOR}.#{ActionPack::VERSION::MINOR}.#{ActionPack::VERSION::TINY}."
15
+ end
16
+
17
+ def self.register_template_handler(handler_class)
18
+ ActionView::Template.register_template_handler :erb, handler_class
19
+ end
20
+
21
+ def self.register_template_handler_for_2_3_10
22
+ register_template_handler(Rails_2_3_10_TemplateHandler)
23
+ end
24
+ end
25
+ end
26
+
27
+ case ActionPack::VERSION::MAJOR
28
+ when 2
29
+ case ActionPack::VERSION::MINOR
30
+ when 3
31
+ case ActionPack::VERSION::TINY
32
+ when 10
33
+ Erbal::Rails.register_template_handler_for_2_3_10
34
+ else
35
+ Erbal::Rails.version_unsupported
36
+ end
37
+ else
38
+ Erbal::Rails.version_unsupported
39
+ end
40
+ else
41
+ Erbal::Rails.version_unsupported
42
+ end
data/spec/erbal_spec.rb CHANGED
@@ -40,7 +40,7 @@ describe Erbal do
40
40
  end
41
41
 
42
42
  it "should parse content without any tags" do
43
- erbal_parse("I love unicorns!").should == "@out = '';@out << %Q`I love unicorns!`;@out"
43
+ erbal_parse("I love unicorns!").should == "@out = '';@out.concat(%Q`I love unicorns!`);@out"
44
44
  end
45
45
 
46
46
  it "should parse the <% tag" do
@@ -48,68 +48,112 @@ describe Erbal do
48
48
  end
49
49
 
50
50
  it "should parse the <%- tag" do
51
- erbal_parse("1 + 1 is <%- 1 + 1 %>").should == "@out = '';@out << %Q`1 + 1 is `; 1 + 1 ;\n@out"
51
+ erbal_parse("1 + 1 is <%- 1 + 1 %>").should == "@out = '';@out.concat(%Q`1 + 1 is `); 1 + 1 ;\n@out"
52
52
  end
53
53
 
54
54
  it "should add a line break after <% %> tags incase the tags ended with a comment" do
55
- erbal_parse("<% 1 + 1 # eeek comment! %> hi mom").should == "@out = ''; 1 + 1 # eeek comment! ;\n@out << %Q` hi mom`;@out"
55
+ erbal_parse("<% 1 + 1 # eeek comment! %> hi mom").should == "@out = ''; 1 + 1 # eeek comment! ;\n@out.concat(%Q` hi mom`);@out"
56
56
  end
57
57
 
58
58
  it "should parse the <%= tag" do
59
- erbal_parse("<%= 1 + 1 %>").should == "@out = '';@out << %Q`\#{ 1 + 1 }`;@out"
59
+ erbal_parse("<%= 1 + 1 %>").should == "@out = '';@out.concat(%Q`\#{ 1 + 1 }`);@out"
60
60
  end
61
61
 
62
62
  it "should parse the comment tag <%#" do
63
- erbal_parse("Something:<br />\n<%# I'm a comment %>").should == "@out = '';@out << %Q`Something:<br />\n`;@out"
63
+ erbal_parse("Something:<br />\n<%# I'm a comment %>").should == "@out = '';@out.concat(%Q`Something:<br />\n`);@out"
64
64
  end
65
65
 
66
66
  it "should swallow the following newline if the -%> tag is used" do
67
- erbal_parse("<%= 1 + 1 -%>\n\n").should == "@out = '';@out << %Q`\#{ 1 + 1 }\n`;@out"
67
+ erbal_parse("<%= 1 + 1 -%>\n\n").should == "@out = '';@out.concat(%Q`\#{ 1 + 1 }\n`);@out"
68
68
  end
69
69
 
70
70
  it "should not swallow the following character if the -%> tag is used and the following character is not a newline" do
71
- erbal_parse("<%= 1 + 1 -%>Z").should == "@out = '';@out << %Q`\#{ 1 + 1 }Z`;@out"
71
+ erbal_parse("<%= 1 + 1 -%>Z").should == "@out = '';@out.concat(%Q`\#{ 1 + 1 }Z`);@out"
72
72
  end
73
73
 
74
74
  it "should concat text surrounding the tags when the opening tag is <%" do
75
- erbal_parse("1 + 1 is <% 1 + 1 %>. Easy!").should == "@out = '';@out << %Q`1 + 1 is `; 1 + 1 ;\n@out << %Q`. Easy!`;@out"
75
+ erbal_parse("1 + 1 is <% 1 + 1 %>. Easy!").should == "@out = '';@out.concat(%Q`1 + 1 is `); 1 + 1 ;\n@out.concat(%Q`. Easy!`);@out"
76
76
  end
77
77
 
78
78
  it "should concat text surrounding the tags when the opening tag is <%=" do
79
- erbal_parse("1 + 1 is <%= 1 + 1 %>. Easy!").should == "@out = '';@out << %Q`1 + 1 is \#{ 1 + 1 }. Easy!`;@out"
79
+ erbal_parse("1 + 1 is <%= 1 + 1 %>. Easy!").should == "@out = '';@out.concat(%Q`1 + 1 is \#{ 1 + 1 }. Easy!`);@out"
80
80
  end
81
81
 
82
82
  it "should not open a new buffer shift when there is more than one consecutive <%= tag" do
83
- erbal_parse("1 + 1 is <%= 1 + 1 %>, and 2 + 2 is <%= 2 + 2 %>. Easy!").should == "@out = '';@out << %Q`1 + 1 is \#{ 1 + 1 }, and 2 + 2 is \#{ 2 + 2 }. Easy!`;@out"
83
+ erbal_parse("1 + 1 is <%= 1 + 1 %>, and 2 + 2 is <%= 2 + 2 %>. Easy!").should == "@out = '';@out.concat(%Q`1 + 1 is \#{ 1 + 1 }, and 2 + 2 is \#{ 2 + 2 }. Easy!`);@out"
84
84
  end
85
85
 
86
- it "should escape a hash character that signifies the start of a string interpolation when outside tags" do
87
- erbal_parse("<%= 1 + 1 -%> wee \#{1 + 3}").should == "@out = '';@out << %Q`\#{ 1 + 1 } wee \\\#{1 + 3}`;@out"
88
- eval(erbal_parse("<%= 1 + 1 -%> wee \#{1 + 3}")).should == eval(erubis_parse("<%= 1 + 1 -%> wee \#{1 + 3}"))
86
+ describe "when escaping special characters" do
87
+ it "should escape a hash character that signifies the start of a string interpolation when outside tags" do
88
+ erbal_parse("<%= 1 + 1 -%> wee \#{1 + 3}").should == "@out = '';@out.concat(%Q`\#{ 1 + 1 } wee \\\#{1 + 3}`);@out"
89
+ eval(erbal_parse("<%= 1 + 1 -%> wee \#{1 + 3}")).should == eval(erubis_parse("<%= 1 + 1 -%> wee \#{1 + 3}"))
89
90
 
90
- erbal_parse("<%= 1 + 1 -%> wee \\\#{1 + 3}").should == "@out = '';@out << %Q`\#{ 1 + 1 } wee \\\\\\\#{1 + 3}`;@out"
91
- eval(erbal_parse("<%= 1 + 1 -%> wee \\\#{1 + 3}")).should == eval(erubis_parse("<%= 1 + 1 -%> wee \\\#{1 + 3}"))
91
+ erbal_parse("<%= 1 + 1 -%> wee \\\#{1 + 3}").should == "@out = '';@out.concat(%Q`\#{ 1 + 1 } wee \\\\\\\#{1 + 3}`);@out"
92
+ eval(erbal_parse("<%= 1 + 1 -%> wee \\\#{1 + 3}")).should == eval(erubis_parse("<%= 1 + 1 -%> wee \\\#{1 + 3}"))
92
93
 
93
- erbal_parse("<%= 1 + 1 -%> wee \\\\\\\#{1 + 3}").should == "@out = '';@out << %Q`\#{ 1 + 1 } wee \\\\\\\\\\\\\\\#{1 + 3}`;@out"
94
- eval(erbal_parse("<%= 1 + 1 -%> wee \\\\\\\#{1 + 3}")).should == eval(erubis_parse("<%= 1 + 1 -%> wee \\\\\\\#{1 + 3}"))
94
+ erbal_parse("<%= 1 + 1 -%> wee \\\\\\\#{1 + 3}").should == "@out = '';@out.concat(%Q`\#{ 1 + 1 } wee \\\\\\\\\\\\\\\#{1 + 3}`);@out"
95
+ eval(erbal_parse("<%= 1 + 1 -%> wee \\\\\\\#{1 + 3}")).should == eval(erubis_parse("<%= 1 + 1 -%> wee \\\\\\\#{1 + 3}"))
95
96
 
96
- erbal_parse('<%= 1 + 1 -%> wee #{1 + 3}').should == "@out = '';@out << %Q`\#{ 1 + 1 } wee \\\#{1 + 3}`;@out"
97
- eval(erbal_parse('<%= 1 + 1 -%> wee #{1 + 3}')).should == eval(erubis_parse('<%= 1 + 1 -%> wee #{1 + 3}'))
98
- end
97
+ erbal_parse('<%= 1 + 1 -%> wee #{1 + 3}').should == "@out = '';@out.concat(%Q`\#{ 1 + 1 } wee \\\#{1 + 3}`);@out"
98
+ eval(erbal_parse('<%= 1 + 1 -%> wee #{1 + 3}')).should == eval(erubis_parse('<%= 1 + 1 -%> wee #{1 + 3}'))
99
+ end
100
+
101
+ it "should not escape a hash character that signifies the start of a string interpolation when inside tags" do
102
+ erbal_parse('<%= "#{1 + 1}" -%>').should == "@out = '';@out.concat(%Q`\#{ \"\#{1 + 1}\" }`);@out"
103
+ eval(erbal_parse('<%= "#{1 + 1}" -%>')).should == eval(erubis_parse('<%= "#{1 + 1}" -%>'))
104
+ end
105
+
106
+ it "should escape a backtick character that signifies the end off a buffer shift" do
107
+ erbal_parse("weeee `").should == "@out = '';@out.concat(%Q`weeee \\``);@out"
108
+ eval(erbal_parse("weeee `")).should == eval(erubis_parse("weeee `"));
99
109
 
100
- it "should not escape a hash character that signifies the start of a string interpolation when inside tags" do
101
- erbal_parse('<%= "#{1 + 1}" -%>').should == "@out = '';@out << %Q`\#{ \"\#{1 + 1}\" }`;@out"
102
- eval(erbal_parse('<%= "#{1 + 1}" -%>')).should == eval(erubis_parse('<%= "#{1 + 1}" -%>'))
110
+ erbal_parse("weeee \\`").should == "@out = '';@out.concat(%Q`weeee \\\\\\``);@out"
111
+ eval(erbal_parse("weeee \\`")).should == eval(erubis_parse("weeee \\`"))
112
+
113
+ erbal_parse("weeee \\\\\\\`").should == "@out = '';@out.concat(%Q`weeee \\\\\\\\\\\\\\``);@out"
114
+ eval(erbal_parse("weeee \\\\\\\`")).should == eval(erubis_parse("weeee \\\\\\\`"))
115
+ end
103
116
  end
104
117
 
105
- it "should escape a backtick character that signifies the end off a buffer shift" do
106
- erbal_parse("weeee `").should == "@out = '';@out << %Q`weeee \\``;@out"
107
- eval(erbal_parse("weeee `")).should == eval(erubis_parse("weeee `"));
118
+ describe "when using the safe_concat_method, unsafe_concat_method and safe_concat_keyword options" do
119
+ it "should default to using 'concat' if the :safe_concat_method option is not specified" do
120
+ erbal_parse("hello").should == "@out = '';@out.concat(%Q`hello`);@out"
121
+ end
122
+
123
+ it "should default to using 'concat' if the :unsafe_concat_method option is not specified" do
124
+ erbal_parse("<%= 1 + 1 %>").should == "@out = '';@out.concat(%Q`\#{ 1 + 1 }`);@out"
125
+ end
126
+
127
+ it "should use the safe concat method for text outside of tags" do
128
+ erbal_parse("hello", :safe_concat_method => 'safe_concat').should == "@output_buffer = '';@output_buffer.safe_concat(%Q`hello`);@output_buffer"
129
+ end
130
+
131
+ it "should use the unsafe concat method for the <%= tag" do
132
+ erbal_parse("<%= 1 + 1 %>", :unsafe_concat_method => 'unsafe_concat').should == "@output_buffer = '';@output_buffer.unsafe_concat(%Q`\#{ 1 + 1 }`);@output_buffer"
133
+ end
134
+
135
+ it "should use the safe concat method for the <%= tag if the safe_concat_keyword option is also used" do
136
+ erbal_parse("<%= raw 1 + 1 %>", :safe_concat_method => 'safe_concat', :safe_concat_keyword => 'raw').should == "@output_buffer = '';@output_buffer.safe_concat(%Q`\#{ 1 + 1 }`);@output_buffer"
137
+ end
138
+
139
+ it "should not use the safe concat method for the <%= tag if the safe_concat_keyword option is also used but not applicable" do
140
+ erbal_parse("<%= blah 1 + 1 %>", :unsafe_concat_method => 'unsafe_concat').should == "@output_buffer = '';@output_buffer.unsafe_concat(%Q`\#{ blah 1 + 1 }`);@output_buffer"
141
+ end
142
+
143
+ it "should not set a default safe_concat_keyword option" do
144
+ erbal_parse("<%= raw 1 + 1 %>", :unsafe_concat_method => 'unsafe_concat', :safe_concat_keyword => nil).should == "@output_buffer = '';@output_buffer.unsafe_concat(%Q`\#{ raw 1 + 1 }`);@output_buffer"
145
+ end
146
+
147
+ it "should preserve the whitespace following the safe_concat_keyword" do
148
+ erbal_parse("<%= raw 1 + 1 %>", :safe_concat_method => 'safe_concat', :safe_concat_keyword => 'raw').should == "@output_buffer = '';@output_buffer.safe_concat(%Q`\#{ 1 + 1 }`);@output_buffer"
149
+ end
108
150
 
109
- erbal_parse("weeee \\`").should == "@out = '';@out << %Q`weeee \\\\\\``;@out"
110
- eval(erbal_parse("weeee \\`")).should == eval(erubis_parse("weeee \\`"))
151
+ it "should use the safe concat method for the <%= tag if the safe_concat_keyword option is also used and the keyword has no preceding whitespace" do
152
+ erbal_parse("<%=raw 1 + 1 %>", :safe_concat_method => 'safe_concat', :safe_concat_keyword => 'raw').should == "@output_buffer = '';@output_buffer.safe_concat(%Q`\#{ 1 + 1 }`);@output_buffer"
153
+ end
111
154
 
112
- erbal_parse("weeee \\\\\\\`").should == "@out = '';@out << %Q`weeee \\\\\\\\\\\\\\``;@out"
113
- eval(erbal_parse("weeee \\\\\\\`")).should == eval(erubis_parse("weeee \\\\\\\`"))
155
+ it "should not use the safe concat method for the <%= tag if the safe_concat_keyword option is also used but not applicable and the keyword has no preceding whitespace" do
156
+ erbal_parse("<%=blah 1 + 1 %>", :unsafe_concat_method => 'unsafe_concat').should == "@output_buffer = '';@output_buffer.unsafe_concat(%Q`\#{blah 1 + 1 }`);@output_buffer"
157
+ end
114
158
  end
115
159
  end
data/tasks/gem.rake CHANGED
@@ -2,7 +2,7 @@ require 'rake/gempackagetask'
2
2
  require 'yaml'
3
3
 
4
4
  WIN_SUFFIX = ENV['WIN_SUFFIX'] || 'i386-mswin32'
5
- ERBAL_VERSION = '1.1'
5
+ ERBAL_VERSION = '1.2.rc1'
6
6
 
7
7
  task :clean => :clobber_package
8
8
 
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erbal
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
5
- prerelease: false
4
+ hash: 977940510
5
+ prerelease: true
6
6
  segments:
7
7
  - 1
8
- - 1
9
- version: "1.1"
8
+ - 2
9
+ - rc1
10
+ version: 1.2.rc1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Ian Leitch
@@ -14,7 +15,7 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-12-30 00:00:00 +11:00
18
+ date: 2011-01-05 00:00:00 +11:00
18
19
  default_executable:
19
20
  dependencies: []
20
21
 
@@ -31,7 +32,7 @@ files:
31
32
  - CHANGELOG
32
33
  - README.rdoc
33
34
  - Rakefile
34
- - lib/erbal/rails23.rb
35
+ - lib/erbal/rails.rb
35
36
  - spec/erbal_spec.rb
36
37
  - spec/spec_helper.rb
37
38
  - tasks/ext.rake
@@ -66,12 +67,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
67
  required_rubygems_version: !ruby/object:Gem::Requirement
67
68
  none: false
68
69
  requirements:
69
- - - ">="
70
+ - - ">"
70
71
  - !ruby/object:Gem::Version
71
- hash: 3
72
+ hash: 25
72
73
  segments:
73
- - 0
74
- version: "0"
74
+ - 1
75
+ - 3
76
+ - 1
77
+ version: 1.3.1
75
78
  requirements: []
76
79
 
77
80
  rubyforge_project:
data/lib/erbal/rails23.rb DELETED
@@ -1,21 +0,0 @@
1
- require 'erbal'
2
-
3
- if ActiveSupport.const_defined?('SafeBuffer')
4
-
5
- class ErbalTemplateHandler < ActionView::TemplateHandler
6
- include ActionView::TemplateHandlers::Compilable
7
- def compile(template)
8
- ::Erbal.new("<% __in_erb_template=true %>#{template.source}", {:buffer => '@output_buffer', :buffer_initial_value => 'ActiveSupport::SafeBuffer.new'}).parse
9
- end
10
- end
11
-
12
- else
13
-
14
- class ErbalTemplateHandler < ActionView::TemplateHandler
15
- include ActionView::TemplateHandlers::Compilable
16
- def compile(template)
17
- ::Erbal.new("<% __in_erb_template=true %>#{template.source}", {:buffer => '@output_buffer'}).parse
18
- end
19
- end
20
-
21
- end