erbse 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|