erbal 1.0 → 1.1
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 +46 -2
- data/benchmark/bench.rb +54 -13
- data/benchmark/mem_leak_detect.rb +22 -0
- data/benchmark/sample.erb +20 -24
- data/ext/erbal/erbal.c +24 -11
- data/ext/erbal/parser.c +9 -23
- data/ext/erbal/parser.rl +3 -17
- data/tasks/gem.rake +1 -1
- metadata +4 -3
data/README.rdoc
CHANGED
@@ -14,8 +14,8 @@ Erbal is a lightweight ERB parser that uses the Ragel State Machine Compiler (ht
|
|
14
14
|
|
15
15
|
Erbal.new takes an optional 2nd argument which is a hash of options, available options are:
|
16
16
|
|
17
|
-
|
18
|
-
|
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.
|
19
19
|
|
20
20
|
== Rails 2.3
|
21
21
|
|
@@ -28,6 +28,50 @@ Create the file 'config/initializers/erbal.rb' containing:
|
|
28
28
|
|
29
29
|
I've not looked into yet.. patches are welcome! ;)
|
30
30
|
|
31
|
+
== Benchmarks
|
32
|
+
|
33
|
+
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
|
+
|
35
|
+
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
|
+
|
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
|
66
|
+
|
67
|
+
=> Erubis (using default Eruby engine)
|
68
|
+
0.178 0.176 0.165 0.176 0.175 0.164
|
69
|
+
=> Average: 0.172
|
70
|
+
|
71
|
+
=> Erbal
|
72
|
+
0.111 0.125 0.124 0.110 0.133 0.124
|
73
|
+
=> Average: 0.121
|
74
|
+
|
31
75
|
== Contributing
|
32
76
|
|
33
77
|
* Fork the project.
|
data/benchmark/bench.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
1
3
|
require 'erb'
|
2
4
|
require 'rubygems'
|
3
5
|
require 'erubis'
|
@@ -5,31 +7,46 @@ require File.expand_path(File.dirname(__FILE__)) + '/../lib/erbal'
|
|
5
7
|
|
6
8
|
RUNS = 3000
|
7
9
|
REPEAT = 6
|
8
|
-
SRC = File.read('sample.erb')
|
10
|
+
SRC = File.read(File.expand_path(File.dirname(__FILE__)) + '/sample.erb')
|
9
11
|
|
10
12
|
class Benchmark
|
11
|
-
def self.run(runs, repeat, warmup=false)
|
12
|
-
puts "\n=> #{
|
13
|
+
def self.run(what, runs, repeat, warmup=false)
|
14
|
+
puts "\n=> #{canonical_name}" unless warmup
|
13
15
|
times = []
|
14
16
|
repeat.times do |i|
|
15
17
|
total = 0
|
16
|
-
runs.times do
|
18
|
+
runs.times do |n|
|
17
19
|
start = Time.now
|
18
|
-
|
20
|
+
send(what)
|
19
21
|
total += Time.now-start
|
20
22
|
end
|
21
23
|
times << total
|
22
|
-
|
24
|
+
unless warmup
|
25
|
+
$stdout.write(sprintf("%.3f ", total))
|
26
|
+
$stdout.flush
|
27
|
+
end
|
23
28
|
end
|
24
29
|
unless warmup
|
25
|
-
puts "=> Average: #{sprintf("%.3f", times.inject(0){|c, n| c += n} / times.size)}"
|
30
|
+
puts "\n=> Average: #{sprintf("%.3f", times.inject(0){|c, n| c += n} / times.size)}"
|
26
31
|
end
|
27
32
|
end
|
33
|
+
|
34
|
+
def self.prep_for_eval
|
35
|
+
@src = parse
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.eval_src
|
39
|
+
eval(@src)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.canonical_name
|
43
|
+
name.split('Benchmark').first
|
44
|
+
end
|
28
45
|
end
|
29
46
|
|
30
47
|
class ErbalBenchmark < Benchmark
|
31
48
|
def self.parse
|
32
|
-
Erbal.new(SRC
|
49
|
+
Erbal.new(SRC).parse
|
33
50
|
end
|
34
51
|
end
|
35
52
|
|
@@ -37,21 +54,45 @@ class ErbBenchmark < Benchmark
|
|
37
54
|
def self.parse
|
38
55
|
::ERB.new(SRC, nil, '-', '@output')
|
39
56
|
end
|
57
|
+
|
58
|
+
def self.prep_for_eval
|
59
|
+
@src = parse.src
|
60
|
+
end
|
40
61
|
end
|
41
62
|
|
42
|
-
class
|
63
|
+
class ErubisFastBenchmark < Benchmark
|
43
64
|
def self.parse
|
44
65
|
Erubis::FastEruby.new.convert(SRC)
|
45
66
|
end
|
67
|
+
|
68
|
+
def self.canonical_name
|
69
|
+
"Erubis (using FastEruby engine)"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ErubisBenchmark < Benchmark
|
74
|
+
def self.parse
|
75
|
+
Erubis::Eruby.new.convert(SRC)
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.canonical_name
|
79
|
+
"Erubis (using default Eruby engine)"
|
80
|
+
end
|
46
81
|
end
|
47
82
|
|
48
|
-
parsers = [ErbBenchmark,
|
83
|
+
parsers = [ErbBenchmark, ErubisFastBenchmark, ErubisBenchmark, ErbalBenchmark]
|
49
84
|
|
50
85
|
$stdout.write("=> Warming up.... ")
|
51
86
|
$stdout.flush
|
52
87
|
parsers.each do |b|
|
53
|
-
b.run(
|
88
|
+
b.run(:parse, 10, 2, true)
|
54
89
|
end
|
55
90
|
puts "done"
|
56
|
-
puts
|
57
|
-
|
91
|
+
puts
|
92
|
+
puts "=> Parsing Benchmark"
|
93
|
+
parsers.map {|b| b.run(:parse, RUNS, REPEAT)}
|
94
|
+
|
95
|
+
puts
|
96
|
+
puts "=> eval() Benchmark"
|
97
|
+
parsers.map {|b| b.prep_for_eval}
|
98
|
+
parsers.map {|b| b.run(:eval_src, RUNS, REPEAT)}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path(File.dirname(__FILE__)) + '/../lib/erbal'
|
4
|
+
|
5
|
+
SRC = File.read(File.expand_path(File.dirname(__FILE__)) + '/sample.erb')
|
6
|
+
GC.enable_stats
|
7
|
+
|
8
|
+
i = 0
|
9
|
+
while true do
|
10
|
+
i += 1
|
11
|
+
5000.times do
|
12
|
+
e = Erbal.new(SRC)
|
13
|
+
e.parse
|
14
|
+
end
|
15
|
+
GC.start
|
16
|
+
rss = `ps -orss #{Process.pid}`.split("\n").last.strip
|
17
|
+
puts
|
18
|
+
puts "Run #{i}"
|
19
|
+
puts "RSS #{rss}"
|
20
|
+
puts "Live objects #{ObjectSpace.live_objects}"
|
21
|
+
GC.dump
|
22
|
+
end
|
data/benchmark/sample.erb
CHANGED
@@ -1,24 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
22
|
-
Ut enim ad minim veniam, <% quis -%> nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
23
|
-
Duis aute irure dolor in reprehenderit in <%= voluptate velit -%> esse cillum dolore eu fugiat nulla pariatur.
|
24
|
-
Excepteur sint occaecat cupidatat non proident, sunt in culpa <%= qui -%> <%= officia -%> <%= deserunt -%> <%= mollit -%> <%= anim -%> <%= id est laborum. -%>
|
1
|
+
Hello! I'm the sample Erbal template.
|
2
|
+
|
3
|
+
The time now is: <%= Time.now -%>
|
4
|
+
|
5
|
+
<% if true %>
|
6
|
+
Once upon a time there was a small pig called Pinki. He died.
|
7
|
+
<% end -%>
|
8
|
+
|
9
|
+
1 + 1 is: <%= 1 + 1 %>
|
10
|
+
<%= "2 + 2 is :#{2 + 2}" %>
|
11
|
+
|
12
|
+
<%= %><%= %><%= "hi mom" %><%= %><%= -%><% true || false %>
|
13
|
+
|
14
|
+
#{exit 1}
|
15
|
+
|
16
|
+
`exit 1`
|
17
|
+
|
18
|
+
<%# almost the end %>
|
19
|
+
|
20
|
+
"The end."
|
data/ext/erbal/erbal.c
CHANGED
@@ -3,15 +3,9 @@
|
|
3
3
|
|
4
4
|
static VALUE cErbal;
|
5
5
|
|
6
|
-
void rb_erbal_free(void *data) {
|
7
|
-
if(data) {
|
8
|
-
free(data);
|
9
|
-
}
|
10
|
-
}
|
11
|
-
|
12
6
|
VALUE rb_erbal_alloc(VALUE klass) {
|
13
|
-
erbal_parser *parser =
|
14
|
-
VALUE obj = Data_Wrap_Struct(klass,
|
7
|
+
erbal_parser *parser = ALLOC(erbal_parser);
|
8
|
+
VALUE obj = Data_Wrap_Struct(klass, 0, free, parser);
|
15
9
|
return obj;
|
16
10
|
}
|
17
11
|
|
@@ -22,7 +16,7 @@ VALUE rb_erbal_initialize(int argc, VALUE *argv, VALUE self) {
|
|
22
16
|
|
23
17
|
Check_Type(str, T_STRING);
|
24
18
|
|
25
|
-
erbal_parser *parser
|
19
|
+
erbal_parser *parser;
|
26
20
|
Data_Get_Struct(self, erbal_parser, parser);
|
27
21
|
parser->str = str;
|
28
22
|
|
@@ -33,13 +27,32 @@ VALUE rb_erbal_initialize(int argc, VALUE *argv, VALUE self) {
|
|
33
27
|
parser->options = options;
|
34
28
|
}
|
35
29
|
|
30
|
+
rb_iv_set(self, "@options", parser->options);
|
31
|
+
|
32
|
+
if (rb_hash_aref(parser->options, ID2SYM(rb_intern("debug"))) == Qtrue) {
|
33
|
+
parser->debug = 1;
|
34
|
+
} else {
|
35
|
+
parser->debug = 0;
|
36
|
+
}
|
37
|
+
|
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);
|
48
|
+
|
36
49
|
return self;
|
37
50
|
}
|
38
51
|
|
39
52
|
VALUE rb_erbal_parse(VALUE self) {
|
40
|
-
erbal_parser *parser
|
53
|
+
erbal_parser *parser;
|
41
54
|
Data_Get_Struct(self, erbal_parser, parser);
|
42
|
-
erbal_parser_init(parser);
|
55
|
+
erbal_parser_init(self, parser);
|
43
56
|
erbal_parser_exec(parser);
|
44
57
|
return parser->src;
|
45
58
|
}
|
data/ext/erbal/parser.c
CHANGED
@@ -164,28 +164,14 @@ inline void erbal_parser_finish(erbal_parser *parser) {
|
|
164
164
|
}
|
165
165
|
}
|
166
166
|
|
167
|
-
void erbal_parser_init(erbal_parser *parser) {
|
167
|
+
void erbal_parser_init(VALUE self, erbal_parser *parser) {
|
168
168
|
parser->chars_seen = 0;
|
169
169
|
parser->in_buffer_shift = 0;
|
170
170
|
parser->state = OUTSIDE_TAG;
|
171
|
-
|
172
|
-
if (rb_hash_aref(parser->options, ID2SYM(rb_intern("debug"))) == Qtrue) {
|
173
|
-
parser->debug = 1;
|
174
|
-
} else {
|
175
|
-
parser->debug = 0;
|
176
|
-
}
|
177
|
-
|
178
|
-
VALUE buffer_name_val = rb_hash_aref(parser->options, ID2SYM(rb_intern("buffer")));
|
179
|
-
|
180
|
-
if (!NIL_P(buffer_name_val)) {
|
181
|
-
Check_Type(buffer_name_val, T_STRING);
|
182
|
-
parser->buffer_name = buffer_name_val;
|
183
|
-
} else {
|
184
|
-
parser->buffer_name = rb_str_new2("@output_buffer");
|
185
|
-
}
|
186
|
-
|
187
171
|
parser->src = rb_str_dup(parser->buffer_name);
|
188
172
|
|
173
|
+
rb_iv_set(self, "@src", parser->src);
|
174
|
+
|
189
175
|
VALUE buffer_init_val = rb_hash_aref(parser->options, ID2SYM(rb_intern("buffer_initial_value")));
|
190
176
|
|
191
177
|
if (!NIL_P(buffer_init_val)) {
|
@@ -198,7 +184,7 @@ void erbal_parser_init(erbal_parser *parser) {
|
|
198
184
|
}
|
199
185
|
|
200
186
|
|
201
|
-
#line
|
187
|
+
#line 188 "parser.c"
|
202
188
|
{
|
203
189
|
cs = erbal_parser_start;
|
204
190
|
ts = 0;
|
@@ -206,14 +192,14 @@ void erbal_parser_init(erbal_parser *parser) {
|
|
206
192
|
act = 0;
|
207
193
|
}
|
208
194
|
|
209
|
-
#line
|
195
|
+
#line 186 "parser.rl"
|
210
196
|
}
|
211
197
|
|
212
198
|
void erbal_parser_exec(erbal_parser *parser) {
|
213
199
|
p = RSTRING(parser->str)->ptr;
|
214
200
|
pe = p + strlen(p);
|
215
201
|
|
216
|
-
#line
|
202
|
+
#line 203 "parser.c"
|
217
203
|
{
|
218
204
|
if ( p == pe )
|
219
205
|
goto _test_eof;
|
@@ -263,7 +249,7 @@ st1:
|
|
263
249
|
case 1:
|
264
250
|
#line 1 "NONE"
|
265
251
|
{ts = p;}
|
266
|
-
#line
|
252
|
+
#line 253 "parser.c"
|
267
253
|
switch( (*p) ) {
|
268
254
|
case 37: goto st2;
|
269
255
|
case 45: goto tr4;
|
@@ -285,7 +271,7 @@ st3:
|
|
285
271
|
if ( ++p == pe )
|
286
272
|
goto _test_eof3;
|
287
273
|
case 3:
|
288
|
-
#line
|
274
|
+
#line 275 "parser.c"
|
289
275
|
if ( (*p) == 37 )
|
290
276
|
goto st0;
|
291
277
|
goto tr6;
|
@@ -335,6 +321,6 @@ case 5:
|
|
335
321
|
|
336
322
|
}
|
337
323
|
|
338
|
-
#line
|
324
|
+
#line 192 "parser.rl"
|
339
325
|
erbal_parser_finish(parser);
|
340
326
|
}
|
data/ext/erbal/parser.rl
CHANGED
@@ -163,28 +163,14 @@ inline void erbal_parser_finish(erbal_parser *parser) {
|
|
163
163
|
}
|
164
164
|
}
|
165
165
|
|
166
|
-
void erbal_parser_init(erbal_parser *parser) {
|
166
|
+
void erbal_parser_init(VALUE self, erbal_parser *parser) {
|
167
167
|
parser->chars_seen = 0;
|
168
168
|
parser->in_buffer_shift = 0;
|
169
169
|
parser->state = OUTSIDE_TAG;
|
170
|
-
|
171
|
-
if (rb_hash_aref(parser->options, ID2SYM(rb_intern("debug"))) == Qtrue) {
|
172
|
-
parser->debug = 1;
|
173
|
-
} else {
|
174
|
-
parser->debug = 0;
|
175
|
-
}
|
176
|
-
|
177
|
-
VALUE buffer_name_val = rb_hash_aref(parser->options, ID2SYM(rb_intern("buffer")));
|
178
|
-
|
179
|
-
if (!NIL_P(buffer_name_val)) {
|
180
|
-
Check_Type(buffer_name_val, T_STRING);
|
181
|
-
parser->buffer_name = buffer_name_val;
|
182
|
-
} else {
|
183
|
-
parser->buffer_name = rb_str_new2("@output_buffer");
|
184
|
-
}
|
185
|
-
|
186
170
|
parser->src = rb_str_dup(parser->buffer_name);
|
187
171
|
|
172
|
+
rb_iv_set(self, "@src", parser->src);
|
173
|
+
|
188
174
|
VALUE buffer_init_val = rb_hash_aref(parser->options, ID2SYM(rb_intern("buffer_initial_value")));
|
189
175
|
|
190
176
|
if (!NIL_P(buffer_init_val)) {
|
data/tasks/gem.rake
CHANGED
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erbal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 13
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: "1.
|
8
|
+
- 1
|
9
|
+
version: "1.1"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ian Leitch
|
@@ -38,6 +38,7 @@ files:
|
|
38
38
|
- tasks/gem.rake
|
39
39
|
- tasks/spec.rake
|
40
40
|
- benchmark/bench.rb
|
41
|
+
- benchmark/mem_leak_detect.rb
|
41
42
|
- benchmark/sample.erb
|
42
43
|
- ext/erbal/parser.h
|
43
44
|
- ext/erbal/erbal.c
|