wlang 2.0.0.beta → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -8
- data/Gemfile.lock +4 -8
- data/Manifest.txt +0 -2
- data/README.md +36 -7
- data/lib/wlang.rb +3 -0
- data/lib/wlang/command.rb +3 -11
- data/lib/wlang/compiler.rb +11 -20
- data/lib/wlang/compiler/autospacing.rb +4 -2
- data/lib/wlang/compiler/dialect_enforcer.rb +1 -1
- data/lib/wlang/compiler/to_ruby_code.rb +1 -1
- data/lib/wlang/dialect.rb +163 -23
- data/lib/wlang/html.rb +9 -8
- data/lib/wlang/loader.rb +2 -4
- data/lib/wlang/scope.rb +17 -8
- data/lib/wlang/scope/proc_scope.rb +18 -0
- data/lib/wlang/source.rb +56 -0
- data/lib/wlang/source/front_matter.rb +51 -0
- data/lib/wlang/template.rb +55 -17
- data/lib/wlang/tilt.rb +3 -0
- data/lib/wlang/tilt/wlang_template.rb +43 -0
- data/lib/wlang/version.rb +1 -2
- data/spec/fixtures/templates/{hello.tpl → hello.wlang} +0 -0
- data/spec/fixtures/templates/hello_with_data.wlang +4 -0
- data/spec/fixtures/templates/hello_with_explicit_locals.wlang +6 -0
- data/spec/fixtures/templates/hello_with_partials.wlang +7 -0
- data/spec/integration/examples/{1-basics.txt → 1-html-intro/1-basics.md} +1 -1
- data/spec/integration/examples/{2-imperative.txt → 1-html-intro/2-imperative.md} +1 -1
- data/spec/integration/examples/{3-partials.txt → 1-html-intro/3-partials.md} +0 -0
- data/spec/integration/examples/{4-recursion.txt → 1-html-intro/4-recursion.md} +1 -2
- data/spec/integration/examples/1-html-intro/5-polymorphism.md +17 -0
- data/spec/integration/html/test_caret.rb +15 -2
- data/spec/integration/html/test_question.rb +12 -2
- data/spec/integration/html/test_sharp.rb +5 -1
- data/spec/integration/html/test_star.rb +1 -3
- data/spec/integration/test_examples.rb +2 -1
- data/spec/integration/test_readme.rb +14 -2
- data/spec/integration/tilt/test_wlang_template.rb +13 -0
- data/spec/spec_helper.rb +3 -3
- data/spec/unit/compiler/test_dialect_enforcer.rb +1 -1
- data/spec/unit/compiler/test_to_ruby_proc.rb +15 -0
- data/spec/unit/dialect/test_compile.rb +0 -10
- data/spec/unit/dialect/test_evaluate.rb +36 -28
- data/spec/unit/dialect/test_new.rb +61 -0
- data/spec/unit/dialect/test_tag.rb +36 -0
- data/spec/unit/dialect/test_tag_dispatching_name.rb +22 -0
- data/spec/unit/dialect/test_with_scope.rb +8 -11
- data/spec/unit/scope/test_chain.rb +29 -0
- data/spec/unit/scope/test_coerce.rb +13 -2
- data/spec/unit/scope/test_proc_scope.rb +18 -0
- data/spec/unit/source/front_matter/test_locals.rb +39 -0
- data/spec/unit/source/front_matter/test_template_content.rb +22 -0
- data/spec/unit/source/test_locals.rb +12 -0
- data/spec/unit/source/test_path.rb +27 -0
- data/spec/unit/source/test_template_content.rb +37 -0
- data/spec/unit/source/test_with_front_matter.rb +19 -0
- data/spec/unit/template/test_call.rb +16 -0
- data/spec/unit/template/test_new.rb +20 -0
- data/spec/unit/template/test_path.rb +28 -0
- data/spec/unit/template/test_render.rb +44 -0
- data/spec/unit/template/test_to_ast.rb +12 -0
- data/spec/unit/template/test_to_ruby_code.rb +13 -0
- data/spec/unit/template/test_to_ruby_proc.rb +12 -0
- data/spec/unit/template/test_yaml_front_matter.rb +22 -0
- data/spec/unit/tilt/test_wlang_template.rb +65 -0
- data/wlang.gemspec +3 -5
- data/wlang.noespec +12 -17
- metadata +117 -85
- data/lib/wlang/dialect/dispatching.rb +0 -51
- data/lib/wlang/dialect/evaluation.rb +0 -30
- data/lib/wlang/dialect/tags.rb +0 -50
- data/lib/wlang/mustang.rb +0 -90
- data/spec/integration/test_mustang.rb +0 -120
- data/spec/unit/dialect/test_dispatching.rb +0 -19
- data/spec/unit/dialect/test_tags.rb +0 -32
data/Gemfile
CHANGED
@@ -3,22 +3,16 @@ source 'http://rubygems.org'
|
|
3
3
|
group :runtime do
|
4
4
|
gem "citrus", "~> 2.4.1"
|
5
5
|
gem "temple", "~> 0.4.0"
|
6
|
-
gem "backports", "~> 2.6.1", :platforms => ["ruby_18", "mri_18", "mingw_18", 'jruby']
|
7
6
|
gem "quickl", "~> 0.4.3"
|
8
7
|
gem "awesome_print", "~> 1.0.2"
|
9
|
-
|
10
|
-
|
11
|
-
group :profiling do
|
12
|
-
gem "ruby-prof", "~> 0.11.2"
|
13
|
-
gem "mustache", "~> 0.99.4"
|
14
|
-
#gem "viiite", :git => "git://github.com/blambeau/viiite.git"
|
8
|
+
gem "epath", ">= 0.2"
|
15
9
|
end
|
16
10
|
|
17
11
|
group :development do
|
12
|
+
gem "tilt", "~> 1.3"
|
18
13
|
gem "rake", "~> 0.9.2"
|
19
14
|
gem "bundler", "~> 1.0"
|
20
15
|
gem "rspec", "~> 2.10.0"
|
21
|
-
gem "epath", "~> 0.1.0"
|
22
16
|
gem "yard", "~> 0.8.1"
|
23
17
|
gem "bluecloth", "~> 2.2.0"
|
24
18
|
end
|
data/Gemfile.lock
CHANGED
@@ -2,12 +2,10 @@ GEM
|
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
4
|
awesome_print (1.0.2)
|
5
|
-
backports (2.6.1)
|
6
5
|
bluecloth (2.2.0)
|
7
6
|
citrus (2.4.1)
|
8
7
|
diff-lcs (1.1.3)
|
9
|
-
epath (0.
|
10
|
-
mustache (0.99.4)
|
8
|
+
epath (0.2.0)
|
11
9
|
quickl (0.4.3)
|
12
10
|
rake (0.9.2.2)
|
13
11
|
rspec (2.10.0)
|
@@ -18,8 +16,8 @@ GEM
|
|
18
16
|
rspec-expectations (2.10.0)
|
19
17
|
diff-lcs (~> 1.1.3)
|
20
18
|
rspec-mocks (2.10.1)
|
21
|
-
ruby-prof (0.11.2)
|
22
19
|
temple (0.4.0)
|
20
|
+
tilt (1.3.3)
|
23
21
|
yard (0.8.1)
|
24
22
|
|
25
23
|
PLATFORMS
|
@@ -27,15 +25,13 @@ PLATFORMS
|
|
27
25
|
|
28
26
|
DEPENDENCIES
|
29
27
|
awesome_print (~> 1.0.2)
|
30
|
-
backports (~> 2.6.1)
|
31
28
|
bluecloth (~> 2.2.0)
|
32
29
|
bundler (~> 1.0)
|
33
30
|
citrus (~> 2.4.1)
|
34
|
-
epath (
|
35
|
-
mustache (~> 0.99.4)
|
31
|
+
epath (>= 0.2)
|
36
32
|
quickl (~> 0.4.3)
|
37
33
|
rake (~> 0.9.2)
|
38
34
|
rspec (~> 2.10.0)
|
39
|
-
ruby-prof (~> 0.11.2)
|
40
35
|
temple (~> 0.4.0)
|
36
|
+
tilt (~> 1.3)
|
41
37
|
yard (~> 0.8.1)
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -2,11 +2,9 @@
|
|
2
2
|
|
3
3
|
[![Build Status](https://secure.travis-ci.org/blambeau/wlang.png?branch=wlang2)](http://travis-ci.org/blambeau/wlang)
|
4
4
|
|
5
|
-
WLang is a powerful code generation and templating engine.
|
5
|
+
WLang is a powerful code generation and templating engine, implemented on top of [temple](https://github.com/judofyr/temple) and much inspired by the excellent [mustache](http://mustache.github.com/).
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
**WLang2 is work in progress**. It does not support rubinius so far, due to an incompatibility with the Citrus parser generator. It also have some issues with spacing; not a big issue for HTML rendering but might prevent certain generation tasks.
|
7
|
+
**WLang2 is still work in progress**.
|
10
8
|
|
11
9
|
## Links
|
12
10
|
|
@@ -15,7 +13,19 @@ This is the README of wlang2, a fresh new implementation of the [wlang templatin
|
|
15
13
|
* http://rubygems.org/gems/wlang
|
16
14
|
* http://revision-zero.org/wlang
|
17
15
|
|
18
|
-
##
|
16
|
+
## Features
|
17
|
+
|
18
|
+
* Tunable templating engine. You can define your own tags, and their behavior.
|
19
|
+
* Powerful logic-less HTML concretization to render web pages à la Mustache with extra.
|
20
|
+
* Compiled templates for speedy generation.
|
21
|
+
* [Tilt](https://github.com/rtomayko/tilt) and [Sinatra](https://github.com/sinatra/sinatra) integration.
|
22
|
+
|
23
|
+
WLang 2.0 also has a few remaining issues.
|
24
|
+
|
25
|
+
* It does not support rubinius so far, due to an incompatibility with the Citrus parser generator.
|
26
|
+
* It also have some issues with spacing; not a big issue for HTML rendering but might prevent certain generation tasks.
|
27
|
+
|
28
|
+
## Tunable templating engine
|
19
29
|
|
20
30
|
WLang is a templating engine, written in ruby. In that, it is similar to ERB, Mustache and the like:
|
21
31
|
|
@@ -43,7 +53,7 @@ Highlighter.render('Hello ${who}!'), who: 'you & the world'
|
|
43
53
|
# => "Hello YOU & THE WORLD !"
|
44
54
|
```
|
45
55
|
|
46
|
-
WLang already provides a few useful dialects, such as WLang::
|
56
|
+
WLang already provides a few useful dialects, such as WLang::Html (inspired by Mustache but a bit more powerful in my opinion). If they don't match your needs, it is up to you to define you own dialect for making your generation task easy. Have a look at the implementation of WLang's ones, it's pretty simple to get started!
|
47
57
|
|
48
58
|
## Abstract semantics
|
49
59
|
|
@@ -97,4 +107,23 @@ class HighLevel < WLang::Dialect
|
|
97
107
|
end
|
98
108
|
```
|
99
109
|
|
100
|
-
Use at your own risk, though, as it might lead to dialects that are difficult to understand and/or use and present serious injections risks! Otherwise, higher-order constructions provides you with very powerful tools.
|
110
|
+
Use at your own risk, though, as it might lead to dialects that are difficult to understand and/or use and present serious injections risks! Otherwise, higher-order constructions provides you with very powerful tools.
|
111
|
+
|
112
|
+
# Tilt integration
|
113
|
+
|
114
|
+
WLang 2.0 has built-in support for [Tilt](https://github.com/rtomayko/tilt) facade to templating engines. In order to use that API:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
require 'tilt' # needed in your bundle, not a wlang dependency
|
118
|
+
require 'wlang' # loads Tilt support provided Tilt has already been required
|
119
|
+
|
120
|
+
template = Tilt.new("path/to/a/template.wlang") # suppose 'Hello ${who}!'
|
121
|
+
template.render(:who => "world")
|
122
|
+
# => Hello world!
|
123
|
+
|
124
|
+
template = Tilt.new(hello_path.to_s, :dialect => Highlighter)
|
125
|
+
template.render(:who => "world")
|
126
|
+
# => Hello WORLD!
|
127
|
+
```
|
128
|
+
|
129
|
+
Please note that you should require tilt first, then wlang. Otherwise, you'll have to require `wlang/tilt` explicitely.
|
data/lib/wlang.rb
CHANGED
data/lib/wlang/command.rb
CHANGED
@@ -45,14 +45,13 @@ module WLang
|
|
45
45
|
def execute(argv)
|
46
46
|
install(argv)
|
47
47
|
|
48
|
-
compiler = @dialect.compiler(@compiling_options)
|
49
48
|
if @ast
|
50
49
|
require 'awesome_print'
|
51
|
-
ap
|
50
|
+
ap @template.to_ast
|
52
51
|
end
|
53
52
|
|
54
53
|
with_output do |output|
|
55
|
-
|
54
|
+
@template.render(@context, output)
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
@@ -71,15 +70,8 @@ module WLang
|
|
71
70
|
@dialect = WLang::Html
|
72
71
|
|
73
72
|
# template and context
|
74
|
-
@template = File.read(@tpl_file)
|
73
|
+
@template = Template.new(File.read(@tpl_file), @compiling_options)
|
75
74
|
@context = {}
|
76
|
-
|
77
|
-
if @yaml_front_matter and
|
78
|
-
@template =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
79
|
-
require 'yaml'
|
80
|
-
@context.merge! YAML::load($1)
|
81
|
-
@template = $'
|
82
|
-
end
|
83
75
|
end
|
84
76
|
|
85
77
|
def with_output(&proc)
|
data/lib/wlang/compiler.rb
CHANGED
@@ -30,16 +30,12 @@ module WLang
|
|
30
30
|
dialect.options
|
31
31
|
end
|
32
32
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
source
|
37
|
-
when Proc
|
38
|
-
Template.new(@dialect, source)
|
33
|
+
def to_ruby_proc(source)
|
34
|
+
source = eval(to_ruby_code(source), TOPLEVEL_BINDING)
|
35
|
+
if String===source
|
36
|
+
Proc.new{|d,buf| buf << source}
|
39
37
|
else
|
40
|
-
|
41
|
-
proc = eval(code, TOPLEVEL_BINDING)
|
42
|
-
Template.new(@dialect, proc)
|
38
|
+
source
|
43
39
|
end
|
44
40
|
end
|
45
41
|
|
@@ -47,12 +43,12 @@ module WLang
|
|
47
43
|
engine.call(source)
|
48
44
|
end
|
49
45
|
|
50
|
-
def
|
46
|
+
def to_ast(source)
|
51
47
|
parser.new.call(source)
|
52
48
|
end
|
53
49
|
|
54
|
-
def parser
|
55
|
-
|
50
|
+
def parser(engine = Class.new(Temple::Engine))
|
51
|
+
engine.tap{|c|
|
56
52
|
c.use Parser
|
57
53
|
c.use DialectEnforcer, :dialect => @dialect
|
58
54
|
c.use Autospacing if options[:autospacing]
|
@@ -61,16 +57,11 @@ module WLang
|
|
61
57
|
}
|
62
58
|
end
|
63
59
|
|
64
|
-
def engine(
|
65
|
-
|
66
|
-
c.use Parser
|
67
|
-
c.use DialectEnforcer, :dialect => @dialect
|
68
|
-
c.use Autospacing if options[:autospacing]
|
69
|
-
c.use StrconcatFlattener
|
70
|
-
c.use StaticMerger
|
60
|
+
def engine(generate_code = true)
|
61
|
+
parser.tap{|c|
|
71
62
|
c.use ProcCallRemoval
|
72
63
|
c.use ToRubyAbstraction
|
73
|
-
c.use ToRubyCode if
|
64
|
+
c.use ToRubyCode if generate_code
|
74
65
|
}.new
|
75
66
|
end
|
76
67
|
|
@@ -15,8 +15,10 @@ module WLang
|
|
15
15
|
|
16
16
|
def on_wlang(symbols, *fns)
|
17
17
|
fns.inject [:wlang, symbols] do |rw,fn|
|
18
|
-
|
19
|
-
|
18
|
+
if multiline?(fn)
|
19
|
+
fn = Unindent.new.call(fn)
|
20
|
+
fn = RightStrip.new.call(fn)
|
21
|
+
end
|
20
22
|
rw << call(fn)
|
21
23
|
end
|
22
24
|
end
|
@@ -28,7 +28,7 @@ module WLang
|
|
28
28
|
ds = dialect.dialects_for(symbols) || []
|
29
29
|
fns.zip(ds).inject [:wlang, symbols] do |rw, (fn, d)|
|
30
30
|
if d
|
31
|
-
enforcer = DialectEnforcer.new(:dialect => d.
|
31
|
+
enforcer = DialectEnforcer.new(:dialect => d.new)
|
32
32
|
rw << [:modulo, d, enforcer.call(fn)]
|
33
33
|
else
|
34
34
|
rw << call(fn)
|
@@ -39,7 +39,7 @@ module WLang
|
|
39
39
|
else
|
40
40
|
id = idgen.next
|
41
41
|
code = call(fn)
|
42
|
-
"Proc.new{|d#{id},b#{id}| #{code}.call(#{dialect}.
|
42
|
+
"Proc.new{|d#{id},b#{id}| #{code}.call(#{dialect}.new(d#{id}.options, d#{id}.template), b#{id}) }"
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
data/lib/wlang/dialect.rb
CHANGED
@@ -1,52 +1,192 @@
|
|
1
|
-
require 'wlang/dialect/dispatching'
|
2
|
-
require 'wlang/dialect/evaluation'
|
3
|
-
require 'wlang/dialect/tags'
|
4
1
|
module WLang
|
5
2
|
class Dialect
|
6
|
-
include Dialect::Dispatching
|
7
|
-
include Dialect::Evaluation
|
8
|
-
include Dialect::Tags
|
9
3
|
|
10
4
|
class << self
|
11
5
|
|
12
|
-
|
13
|
-
new(default_options.merge(options))
|
14
|
-
end
|
6
|
+
# facade
|
15
7
|
|
16
8
|
def default_options(options = {})
|
17
9
|
@default_options ||= (superclass.default_options.dup rescue {})
|
18
10
|
@default_options.merge!(options)
|
19
11
|
end
|
20
12
|
|
21
|
-
def
|
22
|
-
|
13
|
+
def compile(source, options = {})
|
14
|
+
Template.new source, :dialect => self
|
23
15
|
end
|
24
16
|
|
25
|
-
def
|
26
|
-
|
17
|
+
def render(source, scope = {}, buffer = "")
|
18
|
+
compile(source).call(scope, buffer)
|
27
19
|
end
|
28
20
|
|
29
|
-
|
30
|
-
|
21
|
+
# tag installation and dispatching
|
22
|
+
|
23
|
+
# Install a new tag on the dialect for `symbols`.
|
24
|
+
#
|
25
|
+
# Optional `dialects` can be passed if dialect modulation needs to occur for some
|
26
|
+
# blocks. The source code of the tag can either be passed as a `method` Symbol or
|
27
|
+
# as a block.
|
28
|
+
#
|
29
|
+
# Examples:
|
30
|
+
#
|
31
|
+
# # A simple tag with explicit code as a block
|
32
|
+
# tag('$') do |buf,fn| ... end
|
33
|
+
#
|
34
|
+
# # A simple tag, reusing a method (better for testing the method easily)
|
35
|
+
# tag('$', :some_existing_method)
|
36
|
+
#
|
37
|
+
# # Specifying that the first block of #{...}{...} has to be parsed in the same
|
38
|
+
# # dialect whereas the second has to be parsed in the dummy one.
|
39
|
+
# tag('#', [self, WLang::Dummy], ::some_existing_method)
|
40
|
+
#
|
41
|
+
def tag(symbols, dialects = nil, method = nil, &block)
|
42
|
+
if block
|
43
|
+
tag(symbols, dialects, block)
|
44
|
+
else
|
45
|
+
dialects, method = nil, dialects if method.nil?
|
46
|
+
define_tag_method(symbols, dialects, method)
|
47
|
+
end
|
31
48
|
end
|
32
49
|
|
33
|
-
|
34
|
-
|
50
|
+
# Returns the dispatching method name for a given tag symbol and optional prefix
|
51
|
+
# (defaults to '_tag').
|
52
|
+
#
|
53
|
+
# Example:
|
54
|
+
#
|
55
|
+
# Dialect.tag_dispatching_name('!$')
|
56
|
+
# # => :_tag_33_36
|
57
|
+
#
|
58
|
+
# Dialect.tag_dispatching_name('!$', "my_prefix")
|
59
|
+
# # => :my_prefix_33_36
|
60
|
+
#
|
61
|
+
def tag_dispatching_name(symbols, prefix = "_tag")
|
62
|
+
symbols = symbols.chars unless symbols.is_a?(Array)
|
63
|
+
chars = symbols.map{|s| s.ord}.join("_")
|
64
|
+
"#{prefix}_#{chars}".to_sym
|
35
65
|
end
|
36
66
|
|
37
|
-
|
67
|
+
# Binds two methods for the given `symbols`:
|
68
|
+
#
|
69
|
+
# 1) _tag_xx_yy that executes `code`
|
70
|
+
# 2) _diatag_xx_yy that returns the dialect information of the tag blocks.
|
71
|
+
#
|
72
|
+
# `code` can either be a Symbol (existing method) or a Proc (some explicit code).
|
73
|
+
#
|
74
|
+
def define_tag_method(symbols, dialects, code)
|
75
|
+
rulename = tag_dispatching_name(symbols, "_tag")
|
76
|
+
case code
|
77
|
+
when Symbol
|
78
|
+
module_eval %Q{ alias :#{rulename} #{code} }
|
79
|
+
when Proc
|
80
|
+
define_method(rulename, code)
|
81
|
+
else
|
82
|
+
raise "Unable to use #{code} for a tag"
|
83
|
+
end
|
84
|
+
dialects_info_name = tag_dispatching_name(symbols, "_diatag")
|
85
|
+
define_method(dialects_info_name) do dialects end
|
86
|
+
end
|
38
87
|
|
88
|
+
end # class methods
|
89
|
+
|
90
|
+
# Set the default options, '{' and '}' for braces and no autospacing
|
39
91
|
default_options :braces => WLang::BRACES,
|
40
92
|
:autospacing => false
|
41
93
|
|
94
|
+
# All dialect options
|
42
95
|
attr_reader :options
|
43
|
-
def braces; options[:braces]; end
|
44
96
|
|
45
|
-
|
97
|
+
# The template that uses this dialect instance to render
|
98
|
+
attr_reader :template
|
99
|
+
|
100
|
+
# Creates a dialect instance with options
|
101
|
+
def initialize(*args)
|
102
|
+
template, options = nil, {}
|
103
|
+
args.each do |arg|
|
104
|
+
options = arg.to_hash if arg.respond_to?(:to_hash)
|
105
|
+
template = arg if Template===arg
|
106
|
+
end
|
107
|
+
@template = template
|
108
|
+
@options = self.class.default_options.merge(options)
|
109
|
+
end
|
110
|
+
|
111
|
+
# meta information
|
46
112
|
|
47
|
-
|
48
|
-
|
49
|
-
|
113
|
+
# Returns the braces to use, as set in the options
|
114
|
+
def braces
|
115
|
+
options[:braces]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the dialects used to parse the blocks associated with `symbols`, as
|
119
|
+
# previously installed by `define_tag_method`.
|
120
|
+
def dialects_for(symbols)
|
121
|
+
info = self.class.tag_dispatching_name(symbols, "_diatag")
|
122
|
+
raise ArgumentError, "No tag for #{symbols}" unless respond_to?(info)
|
123
|
+
send(info)
|
124
|
+
end
|
125
|
+
|
126
|
+
# rendering
|
127
|
+
|
128
|
+
# Renders `fn` to a `buffer`, optionally extending the current scope.
|
129
|
+
#
|
130
|
+
# `fn` can either be a String (immediately pushed on the buffer), a Proc (taken as a
|
131
|
+
# tag block to render further), or a Template (taken as a partial to render in the
|
132
|
+
# current scope).
|
133
|
+
#
|
134
|
+
# Is `scope` is specified, the current scope is first branched to use to new one in
|
135
|
+
# priority, then rendering applies and the newly created scope if then poped.
|
136
|
+
#
|
137
|
+
# Returns the buffer.
|
138
|
+
#
|
139
|
+
def render(fn, scope = nil, buffer = "")
|
140
|
+
if scope.nil?
|
141
|
+
case fn
|
142
|
+
when String
|
143
|
+
buffer << fn
|
144
|
+
when Proc
|
145
|
+
fn.call(self, buffer)
|
146
|
+
when Template
|
147
|
+
fn.call(@scope, buffer)
|
148
|
+
else
|
149
|
+
raise ArgumentError, "Unable to render `#{fn}`"
|
150
|
+
end
|
151
|
+
buffer
|
152
|
+
else
|
153
|
+
with_scope(scope){ render(fn, nil, buffer) }
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# evaluation
|
158
|
+
|
159
|
+
# Returns the current rendering scope.
|
160
|
+
def scope
|
161
|
+
@scope ||= Scope.root
|
162
|
+
end
|
163
|
+
|
164
|
+
# Yields the block with a scope branched with a sub-scope `x`.
|
165
|
+
def with_scope(x)
|
166
|
+
@scope = scope.push(x)
|
167
|
+
res = yield
|
168
|
+
@scope = scope.pop
|
169
|
+
res
|
170
|
+
end
|
171
|
+
|
172
|
+
# Evaluates `expr` in the current scope. `expr` can be either
|
173
|
+
#
|
174
|
+
# * a Symbol or a String, taken as a dot expression to evaluate
|
175
|
+
# * a Proc or a Template, first rendered and then evaluated
|
176
|
+
#
|
177
|
+
# Evaluation is delegated to the scope (@see Scope#evaluate) and the result returned
|
178
|
+
# by this method.
|
179
|
+
#
|
180
|
+
def evaluate(expr, *default)
|
181
|
+
case expr
|
182
|
+
when Symbol, String
|
183
|
+
catch(:fail) do
|
184
|
+
return scope.evaluate(expr, *default)
|
185
|
+
end
|
186
|
+
raise NameError, "Unable to find `#{expr}`"
|
187
|
+
else
|
188
|
+
evaluate(render(expr), *default)
|
189
|
+
end
|
50
190
|
end
|
51
191
|
|
52
192
|
end # class Dialect
|