erbse 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +9 -1
- data/Gemfile +3 -1
- data/README.md +42 -23
- data/erbse.gemspec +2 -0
- data/lib/erbse.rb +31 -45
- data/lib/erbse/parser.rb +62 -0
- data/lib/erbse/version.rb +2 -2
- data/test/erbse_test.rb +110 -8
- metadata +18 -6
- data/lib/erbse/converter.rb +0 -157
- data/lib/erbse/eruby.rb +0 -114
- data/lib/erbse/template.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df7ee95b6879b5591ad8cd4ee86fd374ac12e66d
|
4
|
+
data.tar.gz: a4c1100201726eebaada8b091c763d1b1925892c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 999650c0c6f0e0ac13d597874b10f62010ae573f04a51964f50a43543808c58b6b1a53456d4f931a6d0cfe41f6272cb22c8cd225e21913edb961a795585663d7
|
7
|
+
data.tar.gz: 42fb43d4d2de42d32c97e9e752175c1170395a9361be975c6ca59c65f5c6298055aea1430f421b755d850effd776b4c85c809b6c4d7b81af46d63f1f981114be
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# 0.1.0
|
2
|
+
|
3
|
+
* Internally, we're parsing the ERB template into a SEXP structure and let [Temple](https://github.com/judofyr/temple) compile it to Ruby. Many thanks to the Temple team! 😘
|
4
|
+
* Yielding ERB blocks will simply return the content, no output buffering with instance variables will happen.
|
5
|
+
This allows to pass ERB blocks around and yield them in other objects without having it output twice as in 0.0.2.
|
6
|
+
* No instance variables are used anymore, output buffering always happens via locals the way [Slim](https://github.com/slim-template/slim) does it. This might result in a minimal speed decrease but cleans up the code and architecture immensely.
|
7
|
+
* Removed `Erbse::Template`, it was completely unnecessary code.
|
8
|
+
|
1
9
|
# 0.0.2
|
2
10
|
|
3
|
-
* First release. No escaping is happening and I'm not sure how capture works, yet. But: it's great!
|
11
|
+
* First release. No escaping is happening and I'm not sure how capture works, yet. But: it's great!
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,32 +2,54 @@
|
|
2
2
|
|
3
3
|
_An updated version of Erubis._
|
4
4
|
|
5
|
-
Erbse compiles an ERB string to a string of Ruby.
|
5
|
+
Erbse compiles an ERB string to a string of Ruby.
|
6
6
|
|
7
|
-
##
|
7
|
+
## API
|
8
8
|
|
9
|
-
|
9
|
+
The API is one public method.
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
...
|
15
|
-
<% end %>
|
16
|
-
<% end %>
|
17
|
-
```
|
11
|
+
```ruby
|
12
|
+
Erbse::Engine.new.call("<% ... %>") #=> string of compiled ruby.
|
13
|
+
```
|
18
14
|
|
19
|
-
|
15
|
+
The returned string can then be `eval`uated in a certain context.
|
16
|
+
|
17
|
+
## Block Yielding
|
20
18
|
|
21
|
-
|
19
|
+
Erbse supports blocks à la Rails.
|
20
|
+
|
21
|
+
You may pass any mix of text/ERB via blocks to Ruby methods.
|
22
|
+
|
23
|
+
```erb
|
24
|
+
<%= form do %>
|
25
|
+
<em>Please fill out all fields!</em>
|
26
|
+
<%= input :email %>
|
27
|
+
<button type="submit">
|
28
|
+
<% end %>
|
29
|
+
```
|
30
|
+
|
31
|
+
Here, the `form` method receives a block of compiled Ruby.
|
32
|
+
|
33
|
+
When `yield`ed, the block simply returns its evaluated content as a string. It's your job to assign it to an output buffer, **no instance variables are used**.
|
22
34
|
|
23
35
|
```ruby
|
24
|
-
|
36
|
+
def form(&block)
|
37
|
+
content = yield
|
38
|
+
"<form>#{content}</form>"
|
39
|
+
end
|
25
40
|
```
|
41
|
+
Usually, returning the content from the helper will be sufficient.
|
42
|
+
|
43
|
+
However, you can totally pass that block to a completely different object and yield it there. Since there's no global state as in ERB, this will work.
|
26
44
|
|
27
|
-
|
45
|
+
## Removed Features
|
28
46
|
|
29
|
-
|
47
|
+
Erbse does *not* support any tags other than `<% %>` and `<%= %>`. Tags such as `<%% %>`, `<%== %>`, `<%- %>` or `<% -%>` will be reduced to the supported tags.
|
30
48
|
|
49
|
+
## TODO
|
50
|
+
|
51
|
+
* Block comments
|
52
|
+
* Add newlines in compiled Ruby.
|
31
53
|
|
32
54
|
## Planned
|
33
55
|
|
@@ -45,20 +67,17 @@ This fragment could then be overridden.
|
|
45
67
|
|
46
68
|
Feel free to contribute!!!
|
47
69
|
|
70
|
+
## Users
|
48
71
|
|
49
|
-
|
50
|
-
|
51
|
-
Erbse is the ERB engine in [Cells 4](https://github.com/apotonick/cells).
|
52
|
-
|
53
|
-
It also hopefully gets used in Rails 5/6, so we can remove those horrible hacks from AV.
|
54
|
-
|
72
|
+
Erbse is the ERB engine in [Cells](https://github.com/apotonick/cells).
|
55
73
|
|
56
|
-
|
74
|
+
## License
|
57
75
|
|
58
76
|
MIT License
|
59
77
|
|
60
|
-
|
78
|
+
## Contributors
|
61
79
|
|
80
|
+
* Special thanks to [Aman Gupta](https://github.com/tmm1) for [performance tweaks](https://github.com/rails/rails/pull/9555) that are merged in Erbse.
|
62
81
|
* @iazel
|
63
82
|
* @seuros
|
64
83
|
|
data/erbse.gemspec
CHANGED
@@ -19,6 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
+
spec.add_dependency "temple"
|
23
|
+
|
22
24
|
spec.add_development_dependency "rake"
|
23
25
|
spec.add_development_dependency "minitest"
|
24
26
|
end
|
data/lib/erbse.rb
CHANGED
@@ -1,49 +1,35 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
##
|
4
|
-
## ex.
|
5
|
-
## input = <<'END'
|
6
|
-
## <ul>
|
7
|
-
## <% for item in @list %>
|
8
|
-
## <li><%= item %>
|
9
|
-
## <%== item %></li>
|
10
|
-
## <% end %>
|
11
|
-
## </ul>
|
12
|
-
## END
|
13
|
-
## list = ['<aaa>', 'b&b', '"ccc"']
|
14
|
-
## eruby = Erubis::Eruby.new(input)
|
15
|
-
## puts "--- code ---"
|
16
|
-
## puts eruby.src
|
17
|
-
## puts "--- result ---"
|
18
|
-
## context = Erubis::Context.new() # or new(:list=>list)
|
19
|
-
## context[:list] = list
|
20
|
-
## puts eruby.evaluate(context)
|
21
|
-
##
|
22
|
-
## result:
|
23
|
-
## --- source ---
|
24
|
-
## _buf = ''; _buf << '<ul>
|
25
|
-
## '; for item in @list
|
26
|
-
## _buf << ' <li>'; _buf << ( item ).to_s; _buf << '
|
27
|
-
## '; _buf << ' '; _buf << Erubis::XmlHelper.escape_xml( item ); _buf << '</li>
|
28
|
-
## '; end
|
29
|
-
## _buf << '</ul>
|
30
|
-
## ';
|
31
|
-
## _buf.to_s
|
32
|
-
## --- result ---
|
33
|
-
## <ul>
|
34
|
-
## <li><aaa>
|
35
|
-
## <aaa></li>
|
36
|
-
## <li>b&b
|
37
|
-
## b&b</li>
|
38
|
-
## <li>"ccc"
|
39
|
-
## "ccc"</li>
|
40
|
-
## </ul>
|
41
|
-
##
|
42
|
-
|
1
|
+
require "temple"
|
2
|
+
require "erbse/parser"
|
43
3
|
|
44
4
|
module Erbse
|
5
|
+
class BlockFilter < Temple::Filter
|
6
|
+
# Highly inspired by https://github.com/slim-template/slim/blob/master/lib/slim/controls.rb#on_slim_output
|
7
|
+
def on_erb_block(code, content_ast)
|
8
|
+
# this is for <%= do %>
|
9
|
+
outter_i = unique_name
|
10
|
+
inner_i = unique_name
|
11
|
+
|
12
|
+
# this still needs the Temple::Filters::ControlFlow run-through.
|
13
|
+
[:multi,
|
14
|
+
[:block, "#{outter_i} = #{code}",
|
15
|
+
[:capture, inner_i, compile(content_ast)] # compile() is recursion on nested block content.
|
16
|
+
],
|
17
|
+
[:dynamic, outter_i] # return the outter buffer. # DISCUSS: why do we need that, again?
|
18
|
+
]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Engine < Temple::Engine
|
23
|
+
use Parser
|
24
|
+
use BlockFilter
|
25
|
+
|
26
|
+
# filter :MultiFlattener
|
27
|
+
# filter :StaticMerger
|
28
|
+
# filter :DynamicInliner
|
29
|
+
filter :ControlFlow
|
30
|
+
|
31
|
+
generator :ArrayBuffer
|
32
|
+
end
|
33
|
+
# DISCUSS: can we add more optimizers?
|
45
34
|
end
|
46
35
|
|
47
|
-
require "erbse/converter"
|
48
|
-
require "erbse/template"
|
49
|
-
require "erbse/eruby"
|
data/lib/erbse/parser.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Erbse
|
2
|
+
class Parser
|
3
|
+
# ERB_EXPR = /<%(=|\#)?(.*?)%>(\n)*/m # this is the desired pattern.
|
4
|
+
ERB_EXPR = /<%(=+|-|\#|%)?(.*?)[-=]?%>(\n)*/m # this is for backward-compatibility.
|
5
|
+
# BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
6
|
+
BLOCK_EXPR = /\b(if|unless)\b|\bdo\b/
|
7
|
+
|
8
|
+
def initialize(*)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(str)
|
12
|
+
pos = 0
|
13
|
+
buffers = []
|
14
|
+
result = [:multi]
|
15
|
+
buffers << result
|
16
|
+
match = nil
|
17
|
+
|
18
|
+
str.scan(ERB_EXPR) do |indicator, code, newlines|
|
19
|
+
match = Regexp.last_match
|
20
|
+
len = match.begin(0) - pos
|
21
|
+
|
22
|
+
text = str[pos, len]
|
23
|
+
pos = match.end(0)
|
24
|
+
ch = indicator ? indicator[0] : nil
|
25
|
+
|
26
|
+
if text and !text.strip.empty? # text
|
27
|
+
buffers.last << [:static, text]
|
28
|
+
end
|
29
|
+
|
30
|
+
if ch == ?= # <%= %>
|
31
|
+
if code =~ BLOCK_EXPR
|
32
|
+
buffers.last << [:erb, :block, code, block = [:multi]] # picked up by our own BlockFilter.
|
33
|
+
buffers << block
|
34
|
+
else
|
35
|
+
buffers.last << [:dynamic, code]
|
36
|
+
end
|
37
|
+
elsif code =~ /\bend\b/ # <% end %>
|
38
|
+
buffers.pop
|
39
|
+
elsif ch =~ /#/ # DISCUSS: doesn't catch <% # this %>
|
40
|
+
newlines = code.count("\n")
|
41
|
+
buffers.last.concat [[:newline]] * newlines if newlines > 0
|
42
|
+
else # <% %>
|
43
|
+
if code =~ BLOCK_EXPR
|
44
|
+
buffers.last << [:block, code, block = [:multi]] # picked up by Temple's ControlFlow filter.
|
45
|
+
buffers << block
|
46
|
+
else
|
47
|
+
buffers.last << [:code, code]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# FIXME: only adds one newline.
|
52
|
+
# TODO: does that influence speed?
|
53
|
+
buffers.last << [:newline] if newlines
|
54
|
+
end
|
55
|
+
|
56
|
+
# add text after last/none ERB tag.
|
57
|
+
buffers.last << [:static, str[pos..str.length]] if pos < str.length
|
58
|
+
|
59
|
+
buffers.last
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/erbse/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Erbse
|
2
|
-
VERSION = "0.0
|
3
|
-
end
|
2
|
+
VERSION = "0.1.0"
|
3
|
+
end
|
data/test/erbse_test.rb
CHANGED
@@ -1,14 +1,116 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
#
|
3
|
+
#ob_0 = '';;ob_0<< ( true ).to_s;ob_0 << ' '.freeze;;ob_1 = form_for do ; ob_2='';;ob_2<< ( 1 ).to_s;;ob_2<< ( 2 ).to_s;;ob_3 = nested do ; ob_4='';;ob_4<< ( 3 ).to_s;;ob_4<< ( 4 ).to_s;ob_4; end ;ob_2 << ob_3;ob_3; end ;ob_1 << ob_2;ob_0.to_s
|
4
4
|
|
5
|
-
class ErbseTest <
|
6
|
-
|
7
|
-
|
5
|
+
class ErbseTest < Minitest::Spec
|
6
|
+
let (:str) { %{
|
7
|
+
<%= true %>
|
8
|
+
Text
|
9
|
+
<%= form_for do %><%= 1 %><% 2 %>
|
10
|
+
<%= nested do %>
|
11
|
+
<%= this %>
|
12
|
+
<a/>
|
13
|
+
<% end %>
|
14
|
+
<% end %>}
|
15
|
+
}
|
16
|
+
|
17
|
+
it "what" do
|
18
|
+
Erbse::Parser.new.(str).must_equal [:multi,
|
19
|
+
[:dynamic, " true "], [:newline],
|
20
|
+
[:static, "Text\n"],
|
21
|
+
[:erb, :block, " form_for do ", [:multi,
|
22
|
+
[:dynamic, " 1 "],
|
23
|
+
[:code, " 2 "], [:newline],
|
24
|
+
[:erb, :block, " nested do ", [:multi, [:newline],
|
25
|
+
[:dynamic, " this "], [:newline],
|
26
|
+
[:static, " <a/>\n "],
|
27
|
+
]], [:newline]]]]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "generates ruby" do
|
31
|
+
code = %{_buf = []; _buf << ( true ); @; _buf << ("Text@@".freeze); _erbse_blockfilter1 = form_for do ; _erbse_blockfilter2 = ''; _erbse_blockfilter2 << (( 1 ).to_s); 2 ; @; _erbse_blockfilter3 = nested do ; _erbse_blockfilter4 = ''; @; _erbse_blockfilter4 << (( this ).to_s); @; _erbse_blockfilter4 << (" <a/>@@ ".freeze); _erbse_blockfilter4; end; _erbse_blockfilter2 << ((_erbse_blockfilter3).to_s); @; _erbse_blockfilter2; end; _buf << (_erbse_blockfilter1); _buf = _buf.join("".freeze)}
|
32
|
+
ruby = Erbse::Engine.new.(str).gsub("\n", "@").gsub('\n', "@@")
|
33
|
+
# puts ruby
|
34
|
+
ruby.must_equal code
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "<% %>" do
|
38
|
+
let (:str) { %{
|
39
|
+
<% self %>
|
40
|
+
<% 2.times do |i| %>
|
41
|
+
<%= i+1 %>
|
42
|
+
<% puts %>
|
43
|
+
<% end %>
|
44
|
+
|
45
|
+
<% if 1 %>
|
46
|
+
Hello
|
47
|
+
<% end %>
|
48
|
+
}
|
49
|
+
}
|
50
|
+
it "what" do
|
51
|
+
Erbse::Parser.new.(str).must_equal [:multi,
|
52
|
+
[:code, " self "], [:newline],
|
53
|
+
[:block, " 2.times do |i| ", [:multi, [:newline],
|
54
|
+
[:dynamic, " i+1 "], [:newline],
|
55
|
+
[:code, " puts "], [:newline]]], [:newline],
|
56
|
+
[:block, " if 1 ", [:multi, [:newline],
|
57
|
+
[:static, " Hello
|
58
|
+
"]]], [:newline]]
|
59
|
+
end
|
60
|
+
|
61
|
+
it do
|
62
|
+
ruby = Erbse::Engine.new.(str)
|
63
|
+
ruby = ruby.gsub("\n", "@")
|
64
|
+
|
65
|
+
# ruby.must_equal %{_buf = []; self ; 2.times do |i| ; _buf << ( i+1 ); puts ; end; _buf = _buf.join(\"\".freeze)}
|
66
|
+
ruby.must_equal '_buf = []; self ; @; 2.times do |i| ; @; _buf << ( i+1 ); @; puts ; @; end; @; if 1 ; @; _buf << (" Hello\n".freeze); end; @; _buf = _buf.join("".freeze)'
|
67
|
+
end
|
68
|
+
|
69
|
+
it do
|
70
|
+
ruby = Erbse::Engine.new.(str)
|
71
|
+
eval(ruby).must_equal "12 Hello\n"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "pure text" do
|
76
|
+
let (:str) { %{Holla
|
77
|
+
Hi}
|
78
|
+
}
|
79
|
+
it { Erbse::Parser.new.(str).must_equal [:multi, [:static, "Holla\nHi"]] }
|
80
|
+
end
|
81
|
+
|
82
|
+
# comments
|
83
|
+
describe "<%# this %>" do
|
84
|
+
let (:str) { %{Hello
|
85
|
+
<%# Ignore this %>
|
86
|
+
Hola
|
87
|
+
<%# Ignore
|
88
|
+
this %>
|
89
|
+
Hi
|
90
|
+
<% # this %>
|
91
|
+
} }
|
92
|
+
|
93
|
+
it do
|
94
|
+
Erbse::Parser.new.(str).must_equal [:multi, [:static, "Hello\n"], [:newline], [:static, "Hola\n"], [:newline], [:newline], [:static, "Hi\n"], [:code, " # this "], [:newline]]
|
95
|
+
end
|
96
|
+
|
97
|
+
it do
|
98
|
+
ruby = Erbse::Engine.new.(str).gsub("\n", "@").gsub('\n', "@@")
|
99
|
+
code = %{_buf = []; _buf << ("Hello@@".freeze); @; _buf << ("Hola@@".freeze); @; @; _buf << ("Hi@@".freeze); # this ; @; _buf = _buf.join("".freeze)}
|
100
|
+
ruby.must_equal code
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "content after last ERB tag" do
|
105
|
+
let (:str) { %{<b><%= 1 %>bla
|
106
|
+
blubb</b>} }
|
107
|
+
|
108
|
+
it { Erbse::Parser.new.(str).must_equal [:multi, [:static, "<b>"], [:dynamic, " 1 "], [:static, "bla\nblubb</b>"]] }
|
8
109
|
end
|
9
110
|
|
10
|
-
|
11
|
-
|
12
|
-
Erbse::
|
111
|
+
describe "<%* %>" do
|
112
|
+
it { Erbse::Parser.new.(%{<%- 1 %>}).must_equal [:multi, [:code, " 1 "]] }
|
113
|
+
it { Erbse::Parser.new.(%{<%% 1 %>}).must_equal [:multi, [:code, " 1 "]] }
|
114
|
+
it { Erbse::Parser.new.(%{<%% 1 -%>}).must_equal [:multi, [:code, " 1 "]] }
|
13
115
|
end
|
14
|
-
end
|
116
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erbse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: temple
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rake
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -60,9 +74,7 @@ files:
|
|
60
74
|
- benchmark/templates/bench_eruby.rhtml
|
61
75
|
- erbse.gemspec
|
62
76
|
- lib/erbse.rb
|
63
|
-
- lib/erbse/
|
64
|
-
- lib/erbse/eruby.rb
|
65
|
-
- lib/erbse/template.rb
|
77
|
+
- lib/erbse/parser.rb
|
66
78
|
- lib/erbse/version.rb
|
67
79
|
- test/erbse_test.rb
|
68
80
|
- test/test_helper.rb
|
@@ -86,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
98
|
version: '0'
|
87
99
|
requirements: []
|
88
100
|
rubyforge_project:
|
89
|
-
rubygems_version: 2.
|
101
|
+
rubygems_version: 2.5.1
|
90
102
|
signing_key:
|
91
103
|
specification_version: 4
|
92
104
|
summary: Updated Erubis.
|
data/lib/erbse/converter.rb
DELETED
@@ -1,157 +0,0 @@
|
|
1
|
-
module Erbse
|
2
|
-
# convert input string into target language
|
3
|
-
class Converter
|
4
|
-
def initialize(properties={}, generator)
|
5
|
-
init_converter!(properties)
|
6
|
-
@generator = generator
|
7
|
-
end
|
8
|
-
|
9
|
-
def convert(input) # TODO: rename to #call.
|
10
|
-
codebuf = "" # or []
|
11
|
-
@preamble.nil? ? generator.add_preamble(codebuf) : (@preamble && (codebuf << @preamble))
|
12
|
-
convert_input(codebuf, input)
|
13
|
-
@postamble.nil? ? generator.add_postamble(codebuf) : (@postamble && (codebuf << @postamble))
|
14
|
-
@_proc = nil # clear cached proc object
|
15
|
-
return codebuf # or codebuf.join()
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
attr_accessor :preamble, :postamble, :escape
|
20
|
-
|
21
|
-
attr_reader :generator
|
22
|
-
|
23
|
-
def init_converter!(properties)
|
24
|
-
@preamble = properties[:preamble]
|
25
|
-
@postamble = properties[:postamble]
|
26
|
-
@escape = properties[:escape]
|
27
|
-
end
|
28
|
-
|
29
|
-
##
|
30
|
-
## detect spaces at beginning of line
|
31
|
-
##
|
32
|
-
def detect_spaces_at_bol(text, is_bol)
|
33
|
-
lspace = nil
|
34
|
-
if text.empty?
|
35
|
-
lspace = "" if is_bol
|
36
|
-
elsif text[-1] == ?\n
|
37
|
-
lspace = ""
|
38
|
-
else
|
39
|
-
rindex = text.rindex(?\n)
|
40
|
-
if rindex
|
41
|
-
s = text[rindex+1..-1]
|
42
|
-
if s =~ /\A[ \t]*\z/
|
43
|
-
lspace = s
|
44
|
-
#text = text[0..rindex]
|
45
|
-
text[rindex+1..-1] = ''
|
46
|
-
end
|
47
|
-
else
|
48
|
-
if is_bol && text =~ /\A[ \t]*\z/
|
49
|
-
#lspace = text
|
50
|
-
#text = nil
|
51
|
-
lspace = text.dup
|
52
|
-
text[0..-1] = ''
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
return lspace
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
|
61
|
-
module Basic
|
62
|
-
end
|
63
|
-
|
64
|
-
|
65
|
-
##
|
66
|
-
## basic converter which supports '<% ... %>' notation.
|
67
|
-
##
|
68
|
-
class Basic::Converter < Converter
|
69
|
-
def self.supported_properties # :nodoc:
|
70
|
-
return [
|
71
|
-
[:pattern, '<% %>', "embed pattern"],
|
72
|
-
[:trim, true, "trim spaces around <% ... %>"],
|
73
|
-
]
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
attr_accessor :pattern, :trim
|
80
|
-
|
81
|
-
def init_converter!(properties={})
|
82
|
-
super(properties)
|
83
|
-
@pattern = properties[:pattern]
|
84
|
-
@trim = properties[:trim] != false
|
85
|
-
end
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
## return regexp of pattern to parse eRuby script
|
90
|
-
def self.pattern_regexp(pattern)
|
91
|
-
@prefix, @postfix = pattern.split() # '<% %>' => '<%', '%>'
|
92
|
-
return /#{@prefix}(=+|-|\#|%)?(.*?)([-=])?#{@postfix}([ \t]*\r?\n)?/m
|
93
|
-
end
|
94
|
-
|
95
|
-
DEFAULT_REGEXP = pattern_regexp('<% %>')
|
96
|
-
|
97
|
-
def convert_input(src, input)
|
98
|
-
pat = @pattern
|
99
|
-
regexp = pat.nil? || pat == '<% %>' ? DEFAULT_REGEXP : pattern_regexp(pat)
|
100
|
-
pos = 0
|
101
|
-
is_bol = true # is beginning of line
|
102
|
-
input.scan(regexp) do |indicator, code, tailch, rspace|
|
103
|
-
match = Regexp.last_match()
|
104
|
-
len = match.begin(0) - pos
|
105
|
-
text = input[pos, len]
|
106
|
-
pos = match.end(0)
|
107
|
-
ch = indicator ? indicator[0] : nil
|
108
|
-
lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
|
109
|
-
is_bol = rspace ? true : false
|
110
|
-
generator.add_text(src, text) if text && !text.empty?
|
111
|
-
## * when '<%= %>', do nothing
|
112
|
-
## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
|
113
|
-
if ch == ?= # <%= %>
|
114
|
-
rspace = nil if tailch && !tailch.empty?
|
115
|
-
generator.add_text(src, lspace) if lspace
|
116
|
-
add_expr(src, code, indicator)
|
117
|
-
generator.add_text(src, rspace) if rspace
|
118
|
-
elsif ch == ?\# # <%# %>
|
119
|
-
n = code.count("\n") + (rspace ? 1 : 0)
|
120
|
-
if @trim && lspace && rspace
|
121
|
-
generator.add_stmt(src, "\n" * n)
|
122
|
-
else
|
123
|
-
generator.add_text(src, lspace) if lspace
|
124
|
-
generator.add_stmt(src, "\n" * n)
|
125
|
-
generator.add_text(src, rspace) if rspace
|
126
|
-
end
|
127
|
-
elsif ch == ?% # <%% %>
|
128
|
-
s = "#{lspace}#{@prefix||='<%'}#{code}#{tailch}#{@postfix||='%>'}#{rspace}"
|
129
|
-
generator.add_text(src, s)
|
130
|
-
else # <% %>
|
131
|
-
if @trim && lspace && rspace
|
132
|
-
generator.add_stmt(src, "#{lspace}#{code}#{rspace}")
|
133
|
-
else
|
134
|
-
generator.add_text(src, lspace) if lspace
|
135
|
-
generator.add_stmt(src, code)
|
136
|
-
generator.add_text(src, rspace) if rspace
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
#rest = $' || input # ruby1.8
|
141
|
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
|
142
|
-
generator.add_text(src, rest)
|
143
|
-
end
|
144
|
-
|
145
|
-
## add expression code to src
|
146
|
-
def add_expr(src, code, indicator)
|
147
|
-
case indicator
|
148
|
-
when '='
|
149
|
-
@escape ? generator.add_expr_escaped(src, code) : generator.add_expr_literal(src, code)
|
150
|
-
when '=='
|
151
|
-
@escape ? generator.add_expr_literal(src, code) : generator.add_expr_escaped(src, code)
|
152
|
-
when '==='
|
153
|
-
generator.add_expr_debug(src, code)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
data/lib/erbse/eruby.rb
DELETED
@@ -1,114 +0,0 @@
|
|
1
|
-
module Erbse
|
2
|
-
class RubyGenerator
|
3
|
-
def init_generator(properties={})
|
4
|
-
@escapefunc ||= "Erubis::XmlHelper.escape_xml"
|
5
|
-
end
|
6
|
-
|
7
|
-
def escape_text(text)
|
8
|
-
text.gsub(/['\\]/, '\\\\\&') # "'" => "\\'", '\\' => '\\\\'
|
9
|
-
end
|
10
|
-
|
11
|
-
def escaped_expr(code)
|
12
|
-
return "#{@escapefunc}(#{code})"
|
13
|
-
end
|
14
|
-
|
15
|
-
#--
|
16
|
-
#def add_preamble(src)
|
17
|
-
# src << "#{@bufvar} = [];"
|
18
|
-
#end
|
19
|
-
#++
|
20
|
-
def add_preamble(src)
|
21
|
-
@newline_pending = 0
|
22
|
-
src << "@output_buffer = output_buffer;" # DISCUSS: i removed the output_buffer || ActionView::OB.new rubbish here.
|
23
|
-
end
|
24
|
-
|
25
|
-
# def add_text(src, text)
|
26
|
-
# src << " #{@bufvar} << '" << escape_text(text) << "';" unless text.empty?
|
27
|
-
# end
|
28
|
-
def add_text(src, text)
|
29
|
-
return if text.empty?
|
30
|
-
|
31
|
-
if text == "\n"
|
32
|
-
@newline_pending += 1
|
33
|
-
else
|
34
|
-
src << "@output_buffer.safe_append='"
|
35
|
-
src << "\n" * @newline_pending if @newline_pending > 0
|
36
|
-
src << escape_text(text)
|
37
|
-
src << "'.freeze;"
|
38
|
-
|
39
|
-
@newline_pending = 0
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Erubis toggles <%= and <%== behavior when escaping is enabled.
|
44
|
-
# We override to always treat <%== as escaped.
|
45
|
-
# def add_expr(src, code, indicator)
|
46
|
-
# case indicator
|
47
|
-
# when '=='
|
48
|
-
# add_expr_escaped(src, code)
|
49
|
-
# else
|
50
|
-
# super
|
51
|
-
# end
|
52
|
-
# end
|
53
|
-
|
54
|
-
def ____add_stmt(src, code)
|
55
|
-
src << code
|
56
|
-
src << ';' unless code[-1] == ?\n
|
57
|
-
end
|
58
|
-
|
59
|
-
def add_stmt(src, code)
|
60
|
-
flush_newline_if_pending(src)
|
61
|
-
____add_stmt(src, code)
|
62
|
-
end
|
63
|
-
|
64
|
-
# def add_expr_literal(src, code)
|
65
|
-
# src << " #{@bufvar} << (" << code << ').to_s;'
|
66
|
-
# end
|
67
|
-
BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
68
|
-
|
69
|
-
def add_expr_literal(src, code)
|
70
|
-
flush_newline_if_pending(src)
|
71
|
-
if code =~ BLOCK_EXPR
|
72
|
-
src << '@output_buffer.append= ' << code
|
73
|
-
else
|
74
|
-
src << '@output_buffer.append=(' << code << ');'
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def add_expr_escaped(src, code)
|
79
|
-
flush_newline_if_pending(src)
|
80
|
-
if code =~ BLOCK_EXPR
|
81
|
-
src << "@output_buffer.safe_expr_append= " << code
|
82
|
-
else
|
83
|
-
src << "@output_buffer.safe_expr_append=(" << code << ");"
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
# def add_expr_escaped(src, code)
|
88
|
-
# src << " #{@bufvar} << " << escaped_expr(code) << ';'
|
89
|
-
# end
|
90
|
-
|
91
|
-
def add_expr_debug(src, code)
|
92
|
-
code.strip!
|
93
|
-
s = (code.dump =~ /\A"(.*)"\z/) && $1
|
94
|
-
src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");'
|
95
|
-
end
|
96
|
-
|
97
|
-
#--
|
98
|
-
#def add_postamble(src)
|
99
|
-
# src << "\n#{@bufvar}.join\n"
|
100
|
-
#end
|
101
|
-
#++
|
102
|
-
def add_postamble(src)
|
103
|
-
flush_newline_if_pending(src)
|
104
|
-
src << '@output_buffer.to_s'
|
105
|
-
end
|
106
|
-
|
107
|
-
def flush_newline_if_pending(src)
|
108
|
-
if @newline_pending > 0
|
109
|
-
src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
|
110
|
-
@newline_pending = 0
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
data/lib/erbse/template.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
module Erbse
|
2
|
-
# Compiles the runtime method for an ERB input string.
|
3
|
-
class Template
|
4
|
-
def initialize(input, properties={})
|
5
|
-
generator = RubyGenerator.new
|
6
|
-
converter = Basic::Converter.new(properties, generator)
|
7
|
-
@src = converter.convert(input)
|
8
|
-
end
|
9
|
-
|
10
|
-
def call
|
11
|
-
@src
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|