erbal 1.1 → 1.2.rc1

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.
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