wlang 2.0.0.beta → 2.0.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.
- 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
|
[](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
|