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/lib/wlang/html.rb
CHANGED
@@ -5,8 +5,8 @@ module WLang
|
|
5
5
|
|
6
6
|
module Helpers
|
7
7
|
|
8
|
-
def value_of(fn)
|
9
|
-
evaluate(render(fn).to_s.strip)
|
8
|
+
def value_of(fn, *defaults)
|
9
|
+
evaluate(render(fn).to_s.strip, *defaults)
|
10
10
|
end
|
11
11
|
private :value_of
|
12
12
|
|
@@ -56,13 +56,14 @@ module WLang
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def question(buf, fn_if, fn_then, fn_else)
|
59
|
-
val
|
60
|
-
|
59
|
+
val = value_of(fn_if)
|
60
|
+
val = val.call if Proc===val
|
61
|
+
block = val ? fn_then : fn_else
|
62
|
+
render(block, nil, buf) if block
|
61
63
|
end
|
62
64
|
|
63
65
|
def caret(buf, fn_if, fn_then, fn_else)
|
64
|
-
|
65
|
-
render(val, nil, buf) if val
|
66
|
+
question(buf, fn_if, fn_else, fn_then)
|
66
67
|
end
|
67
68
|
|
68
69
|
def star(buf, coll_fn, elm_fn, between_fn)
|
@@ -75,12 +76,12 @@ module WLang
|
|
75
76
|
|
76
77
|
def greater(buf, fn)
|
77
78
|
val = value_of(fn)
|
78
|
-
val = Html.compile(val)
|
79
|
+
val = Html.compile(val) if String === val
|
79
80
|
render(val, nil, buf)
|
80
81
|
end
|
81
82
|
|
82
83
|
def sharp(buf, who_fn, then_fn)
|
83
|
-
val = value_of(who_fn)
|
84
|
+
val = value_of(who_fn, nil)
|
84
85
|
if val and not(val.respond_to?(:empty?) and val.empty?)
|
85
86
|
render(then_fn, val, buf)
|
86
87
|
end
|
data/lib/wlang/loader.rb
CHANGED
data/lib/wlang/scope.rb
CHANGED
@@ -12,18 +12,26 @@ module WLang
|
|
12
12
|
@root ||= RootScope.new
|
13
13
|
end
|
14
14
|
|
15
|
-
def self.coerce(arg, parent =
|
15
|
+
def self.coerce(arg, parent = nil)
|
16
|
+
return arg if Scope===arg && parent.nil?
|
17
|
+
parent ||= root
|
16
18
|
clazz = case arg
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
when Binding
|
20
|
+
BindingScope
|
21
|
+
when Scope
|
22
|
+
ProxyScope
|
23
|
+
when Proc
|
24
|
+
ProcScope
|
25
|
+
else
|
26
|
+
ObjectScope
|
27
|
+
end
|
24
28
|
clazz.new(arg, parent)
|
25
29
|
end
|
26
30
|
|
31
|
+
def self.chain(scopes)
|
32
|
+
scopes.compact.inject(nil){|parent,child| Scope.coerce(child, parent)} || root
|
33
|
+
end
|
34
|
+
|
27
35
|
def push(x)
|
28
36
|
Scope.coerce(x, self)
|
29
37
|
end
|
@@ -55,3 +63,4 @@ require 'wlang/scope/root_scope'
|
|
55
63
|
require 'wlang/scope/proxy_scope'
|
56
64
|
require 'wlang/scope/object_scope'
|
57
65
|
require 'wlang/scope/binding_scope'
|
66
|
+
require 'wlang/scope/proc_scope'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module WLang
|
2
|
+
class Scope
|
3
|
+
class ProcScope < Scope
|
4
|
+
|
5
|
+
def fetch(key, &blk)
|
6
|
+
Scope.coerce(subject.call).fetch(key) do
|
7
|
+
parent.fetch(key, &blk)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
subject.inspect
|
13
|
+
end
|
14
|
+
alias :to_s :inspect
|
15
|
+
|
16
|
+
end # class ProcScope
|
17
|
+
end # class Scope
|
18
|
+
end # module WLang
|
data/lib/wlang/source.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module WLang
|
2
|
+
class Source
|
3
|
+
|
4
|
+
attr_reader :subject
|
5
|
+
attr_reader :template
|
6
|
+
|
7
|
+
def initialize(subject, template = nil)
|
8
|
+
@subject = subject
|
9
|
+
@template = template
|
10
|
+
@locals = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def path
|
14
|
+
find_on_subject(:path, :to_path)
|
15
|
+
end
|
16
|
+
alias :to_path :path
|
17
|
+
|
18
|
+
def locals
|
19
|
+
@locals
|
20
|
+
end
|
21
|
+
|
22
|
+
def raw_content
|
23
|
+
find_on_subject(:read, :to_str){
|
24
|
+
raise ArgumentError, "Invalid template source `#{subject}`"
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def template_content
|
29
|
+
raw_content
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_str
|
33
|
+
template_content
|
34
|
+
end
|
35
|
+
alias :to_s :to_str
|
36
|
+
|
37
|
+
def with_front_matter(enabled = true)
|
38
|
+
enabled ? FrontMatter.new(self, template) : self
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def find_on_subject(*methods)
|
44
|
+
s = subject
|
45
|
+
if meth = methods.find{|m| s.respond_to?(m) }
|
46
|
+
subject.send(meth)
|
47
|
+
elsif block_given?
|
48
|
+
yield
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end # class Source
|
55
|
+
end # module WLang
|
56
|
+
require 'wlang/source/front_matter'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module WLang
|
2
|
+
class Source
|
3
|
+
class FrontMatter < Source
|
4
|
+
|
5
|
+
def initialize(*args, &block)
|
6
|
+
super
|
7
|
+
compile
|
8
|
+
end
|
9
|
+
|
10
|
+
def locals
|
11
|
+
@locals
|
12
|
+
end
|
13
|
+
|
14
|
+
def template_content
|
15
|
+
@template_content
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def compiler
|
21
|
+
template.compiler
|
22
|
+
end
|
23
|
+
|
24
|
+
def compile
|
25
|
+
raw = raw_content
|
26
|
+
if raw =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
27
|
+
@locals, @template_content = {}, $'
|
28
|
+
|
29
|
+
require 'yaml'
|
30
|
+
yaml = YAML::load($1)
|
31
|
+
|
32
|
+
# append explicit locals
|
33
|
+
@locals.merge!(yaml.delete("locals") || {})
|
34
|
+
|
35
|
+
# compile explicit partials
|
36
|
+
partials = yaml.delete("partials") || {}
|
37
|
+
partials.each_pair do |k,tpl|
|
38
|
+
@locals[k] = compiler.to_ruby_proc(tpl)
|
39
|
+
end
|
40
|
+
|
41
|
+
# append remaining data
|
42
|
+
@locals.merge!(yaml) unless yaml.empty?
|
43
|
+
else
|
44
|
+
@locals = {}
|
45
|
+
@template_content = raw
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/wlang/template.rb
CHANGED
@@ -1,25 +1,63 @@
|
|
1
1
|
module WLang
|
2
2
|
class Template
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
4
|
+
# Compilation options for the template, as initially passed to `new`
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
# The dialect class to use
|
8
|
+
attr_reader :dialect
|
9
|
+
|
10
|
+
# The underlying template compiler
|
11
|
+
attr_reader :compiler
|
12
|
+
|
13
|
+
# Creates a template instance
|
14
|
+
def initialize(source, options = {})
|
15
|
+
@options = options
|
16
|
+
@dialect = @options.delete(:dialect) || WLang::Html
|
17
|
+
@dialect_instance = @dialect.new(options, self)
|
18
|
+
@compiler = Compiler.new(dialect_instance)
|
19
|
+
@source = Source.new(source, self).with_front_matter(yaml_front_matter?)
|
20
|
+
@compiled = to_ruby_proc
|
21
|
+
end
|
22
|
+
|
23
|
+
def path
|
24
|
+
@options[:path] || @source.path
|
25
|
+
end
|
26
|
+
|
27
|
+
def locals
|
28
|
+
@source.locals
|
29
|
+
end
|
30
|
+
|
31
|
+
def template_content
|
32
|
+
@source.template_content
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_ruby_proc
|
36
|
+
compiler.to_ruby_proc(template_content)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_ruby_code
|
40
|
+
compiler.to_ruby_code(template_content)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_ast
|
44
|
+
compiler.to_ast(template_content)
|
45
|
+
end
|
46
|
+
|
47
|
+
def call(locs = {}, buffer = '')
|
48
|
+
scope = WLang::Scope.chain([locals, locs])
|
49
|
+
dialect_instance.dup.render compiled, scope, buffer
|
21
50
|
end
|
22
51
|
alias :render :call
|
23
52
|
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_reader :source, :compiled, :dialect_instance
|
56
|
+
|
57
|
+
def yaml_front_matter?
|
58
|
+
opt = options[:yaml_front_matter]
|
59
|
+
opt.nil? or opt
|
60
|
+
end
|
61
|
+
|
24
62
|
end # class Template
|
25
63
|
end # module WLang
|
data/lib/wlang/tilt.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Tilt
|
2
|
+
class WLangTemplate < ::Tilt::Template
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def engine_initialized?
|
7
|
+
defined? ::WLang
|
8
|
+
end
|
9
|
+
|
10
|
+
def with_options(options)
|
11
|
+
Class.new(WLangTemplate).tap{|c| c.default_options = options }
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_options=(options)
|
15
|
+
@default_options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_options
|
19
|
+
(superclass.default_options rescue {}).merge(@default_options || {})
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize_engine
|
25
|
+
require_template_library('wlang')
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def prepare
|
31
|
+
opts = self.class.default_options.merge(self.options)
|
32
|
+
opts.merge!(:path => file) if file
|
33
|
+
@engine = WLang::Template.new(data, opts)
|
34
|
+
end
|
35
|
+
|
36
|
+
def evaluate(scope, locals, &block)
|
37
|
+
locals[:yield] = block if block
|
38
|
+
@engine.render WLang::Scope.chain([scope, locals])
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
register WLangTemplate, 'wlang'
|
43
|
+
end
|
data/lib/wlang/version.rb
CHANGED
File without changes
|
@@ -60,6 +60,6 @@ The last useful tag allows you to simply disable wlang rendering in specific tem
|
|
60
60
|
|
61
61
|
For instance, the following template renders the block text unchanged, even if it contains wlang tags:
|
62
62
|
|
63
|
-
Hello ${struct.name}, how are you?
|
63
|
+
Hello ${struct.name}, how are you?
|
64
64
|
|
65
65
|
This is especially useful for writing blog posts explaining wlang, using wlang itself. Therefore, it is a feature mostly needed by me ;-)
|
File without changes
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Now that we know how to use partials, one might ask how to render a menu in a recursive way. For this, we only need another tag for explicitely manipulating the scope.
|
2
2
|
|
3
|
-
* sharp (#) renders its second block, in the scope of the value evaluated in first block
|
3
|
+
* sharp (#) renders its second block, in the scope of the value evaluated in first block, provided the later is not nil, undefined or empty
|
4
4
|
|
5
5
|
For the example above, our menu rendering is initiated as follows:
|
6
6
|
<ul>
|
@@ -13,4 +13,3 @@ For the example above, our menu rendering is initiated as follows:
|
|
13
13
|
</ul>
|
14
14
|
</ul>
|
15
15
|
|
16
|
-
Note that it is important to keep empty menus in the source data to avoid infinite recursions due the parent scope that already binds a value under `menu`.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Sometimes, you want to apply polymorphism to your templates; this it is really easy to do in wlang thanks to its high-order constructions.
|
2
|
+
|
3
|
+
For example, assume that you iterate some data and want render a partial in a polymorphism way, that is, selecting the partial according to some type, here the shape of the current iterated component. This is really simple:
|
4
|
+
|
5
|
+
<ul>
|
6
|
+
<li> Hello [] of width 10cm </li>
|
7
|
+
<li> Hello () of diameter 25cm </li>
|
8
|
+
</ul>
|
9
|
+
|
10
|
+
Which gives the following result:
|
11
|
+
|
12
|
+
<ul>
|
13
|
+
<li> Hello [] of width 10cm </li>
|
14
|
+
<li> Hello () of diameter 25cm <li>
|
15
|
+
</ul>
|
16
|
+
|
17
|
+
Great, isn't?
|
@@ -3,11 +3,18 @@ require 'wlang/html'
|
|
3
3
|
module WLang
|
4
4
|
describe Html, "^{...}" do
|
5
5
|
|
6
|
+
debug = Module.new{ def inspect; "lambda{ #{call.inspect} }"; end }
|
7
|
+
|
6
8
|
def render(tpl, scope = {})
|
7
9
|
Html.render(tpl, scope, "")
|
8
10
|
end
|
9
11
|
|
10
|
-
[
|
12
|
+
[
|
13
|
+
false,
|
14
|
+
nil,
|
15
|
+
lambda{ nil }.extend(debug),
|
16
|
+
lambda{ false }.extend(debug)
|
17
|
+
].each do |test|
|
11
18
|
context "when #{test.inspect}" do
|
12
19
|
it 'renders the then block' do
|
13
20
|
render("^{test}{hello}", binding).should eq("hello")
|
@@ -18,7 +25,13 @@ module WLang
|
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
21
|
-
[
|
28
|
+
[
|
29
|
+
true,
|
30
|
+
"Something",
|
31
|
+
[],
|
32
|
+
lambda{ true }.extend(debug),
|
33
|
+
lambda{ "something" }.extend(debug)
|
34
|
+
].each do |test|
|
22
35
|
context "when #{test.inspect}" do
|
23
36
|
it 'do not render the then block' do
|
24
37
|
render("^{test}{hello}", binding).should eq("")
|