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.
Files changed (74) hide show
  1. data/Gemfile +2 -8
  2. data/Gemfile.lock +4 -8
  3. data/Manifest.txt +0 -2
  4. data/README.md +36 -7
  5. data/lib/wlang.rb +3 -0
  6. data/lib/wlang/command.rb +3 -11
  7. data/lib/wlang/compiler.rb +11 -20
  8. data/lib/wlang/compiler/autospacing.rb +4 -2
  9. data/lib/wlang/compiler/dialect_enforcer.rb +1 -1
  10. data/lib/wlang/compiler/to_ruby_code.rb +1 -1
  11. data/lib/wlang/dialect.rb +163 -23
  12. data/lib/wlang/html.rb +9 -8
  13. data/lib/wlang/loader.rb +2 -4
  14. data/lib/wlang/scope.rb +17 -8
  15. data/lib/wlang/scope/proc_scope.rb +18 -0
  16. data/lib/wlang/source.rb +56 -0
  17. data/lib/wlang/source/front_matter.rb +51 -0
  18. data/lib/wlang/template.rb +55 -17
  19. data/lib/wlang/tilt.rb +3 -0
  20. data/lib/wlang/tilt/wlang_template.rb +43 -0
  21. data/lib/wlang/version.rb +1 -2
  22. data/spec/fixtures/templates/{hello.tpl → hello.wlang} +0 -0
  23. data/spec/fixtures/templates/hello_with_data.wlang +4 -0
  24. data/spec/fixtures/templates/hello_with_explicit_locals.wlang +6 -0
  25. data/spec/fixtures/templates/hello_with_partials.wlang +7 -0
  26. data/spec/integration/examples/{1-basics.txt → 1-html-intro/1-basics.md} +1 -1
  27. data/spec/integration/examples/{2-imperative.txt → 1-html-intro/2-imperative.md} +1 -1
  28. data/spec/integration/examples/{3-partials.txt → 1-html-intro/3-partials.md} +0 -0
  29. data/spec/integration/examples/{4-recursion.txt → 1-html-intro/4-recursion.md} +1 -2
  30. data/spec/integration/examples/1-html-intro/5-polymorphism.md +17 -0
  31. data/spec/integration/html/test_caret.rb +15 -2
  32. data/spec/integration/html/test_question.rb +12 -2
  33. data/spec/integration/html/test_sharp.rb +5 -1
  34. data/spec/integration/html/test_star.rb +1 -3
  35. data/spec/integration/test_examples.rb +2 -1
  36. data/spec/integration/test_readme.rb +14 -2
  37. data/spec/integration/tilt/test_wlang_template.rb +13 -0
  38. data/spec/spec_helper.rb +3 -3
  39. data/spec/unit/compiler/test_dialect_enforcer.rb +1 -1
  40. data/spec/unit/compiler/test_to_ruby_proc.rb +15 -0
  41. data/spec/unit/dialect/test_compile.rb +0 -10
  42. data/spec/unit/dialect/test_evaluate.rb +36 -28
  43. data/spec/unit/dialect/test_new.rb +61 -0
  44. data/spec/unit/dialect/test_tag.rb +36 -0
  45. data/spec/unit/dialect/test_tag_dispatching_name.rb +22 -0
  46. data/spec/unit/dialect/test_with_scope.rb +8 -11
  47. data/spec/unit/scope/test_chain.rb +29 -0
  48. data/spec/unit/scope/test_coerce.rb +13 -2
  49. data/spec/unit/scope/test_proc_scope.rb +18 -0
  50. data/spec/unit/source/front_matter/test_locals.rb +39 -0
  51. data/spec/unit/source/front_matter/test_template_content.rb +22 -0
  52. data/spec/unit/source/test_locals.rb +12 -0
  53. data/spec/unit/source/test_path.rb +27 -0
  54. data/spec/unit/source/test_template_content.rb +37 -0
  55. data/spec/unit/source/test_with_front_matter.rb +19 -0
  56. data/spec/unit/template/test_call.rb +16 -0
  57. data/spec/unit/template/test_new.rb +20 -0
  58. data/spec/unit/template/test_path.rb +28 -0
  59. data/spec/unit/template/test_render.rb +44 -0
  60. data/spec/unit/template/test_to_ast.rb +12 -0
  61. data/spec/unit/template/test_to_ruby_code.rb +13 -0
  62. data/spec/unit/template/test_to_ruby_proc.rb +12 -0
  63. data/spec/unit/template/test_yaml_front_matter.rb +22 -0
  64. data/spec/unit/tilt/test_wlang_template.rb +65 -0
  65. data/wlang.gemspec +3 -5
  66. data/wlang.noespec +12 -17
  67. metadata +117 -85
  68. data/lib/wlang/dialect/dispatching.rb +0 -51
  69. data/lib/wlang/dialect/evaluation.rb +0 -30
  70. data/lib/wlang/dialect/tags.rb +0 -50
  71. data/lib/wlang/mustang.rb +0 -90
  72. data/spec/integration/test_mustang.rb +0 -120
  73. data/spec/unit/dialect/test_dispatching.rb +0 -19
  74. 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
- end
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
@@ -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.1.1)
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 (~> 0.1.0)
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)
@@ -1,6 +1,5 @@
1
1
  wlang.gemspec
2
2
  wlang.noespec
3
- .gemtest
4
3
  CHANGELOG.md
5
4
  Gemfile
6
5
  Gemfile.lock
@@ -12,4 +11,3 @@ Rakefile
12
11
  README.md
13
12
  spec/**/*
14
13
  tasks/**/*
15
- test/**/*
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
- This is the README of wlang2, a fresh new implementation of the [wlang templating language concept](http://revision-zero.org/wlang), this one implemented on top of [temple](https://github.com/judofyr/temple) and much inspired by the excellent [mustache](http://mustache.github.com/). (For users of wlang 1.0 (formaly 0.10.2), this rewrite cleans most concepts as well as the abstract wlang semantics; it also uses a simple compiler architecture to gain huge perfomance gains in comparison to early wlang days).
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
- ## A user-defined templating engine
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::Mustang (mimicing mustache) and WLang::Html (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!
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.
@@ -29,6 +29,9 @@ module WLang
29
29
 
30
30
  end # module WLang
31
31
  require 'wlang/compiler'
32
+ require 'wlang/source'
32
33
  require 'wlang/template'
33
34
  require 'wlang/dialect'
34
35
  require 'wlang/scope'
36
+ require 'wlang/html'
37
+ require 'wlang/tilt' if defined?(::Tilt)
@@ -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 compiler.ast(@template)
50
+ ap @template.to_ast
52
51
  end
53
52
 
54
53
  with_output do |output|
55
- compiler.compile(@template).render(@context, output)
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)
@@ -30,16 +30,12 @@ module WLang
30
30
  dialect.options
31
31
  end
32
32
 
33
- def compile(source)
34
- case source
35
- when Template
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
- code = to_ruby_code(source)
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 ast(source)
46
+ def to_ast(source)
51
47
  parser.new.call(source)
52
48
  end
53
49
 
54
- def parser
55
- Class.new(Temple::Engine).tap{|c|
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(gencode = true)
65
- Class.new(Temple::Engine).tap{|c|
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 gencode
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
- fn = Unindent.new.call(fn)
19
- fn = RightStrip.new.call(fn)
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.factor)
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}.factor(d#{id}.options), b#{id}) }"
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
 
@@ -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
- def factor(options = {})
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 compiler(options = {})
22
- factor(options).compiler
13
+ def compile(source, options = {})
14
+ Template.new source, :dialect => self
23
15
  end
24
16
 
25
- def compile(source, options = {})
26
- compiler(options).compile(source)
17
+ def render(source, scope = {}, buffer = "")
18
+ compile(source).call(scope, buffer)
27
19
  end
28
20
 
29
- def to_ruby_code(source, options = {})
30
- compiler(options).to_ruby_code(source)
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
- def render(source, scope = {}, buffer = "")
34
- compile(source).call(scope, buffer)
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
- end
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
- attr_reader :compiler
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
- def initialize(options = {})
48
- @options = options
49
- @compiler = WLang::Compiler.new(self)
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