wlang 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +27 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +16 -0
- data/README.md +32 -55
- data/lib/wlang.rb +8 -0
- data/lib/wlang/compiler/grammar.citrus +19 -16
- data/lib/wlang/dialect.rb +18 -14
- data/lib/wlang/html.rb +7 -11
- data/lib/wlang/scope.rb +68 -28
- data/lib/wlang/scope/binding_scope.rb +3 -3
- data/lib/wlang/scope/null_scope.rb +34 -0
- data/lib/wlang/scope/object_scope.rb +18 -7
- data/lib/wlang/scope/proc_scope.rb +4 -4
- data/lib/wlang/scope/sinatra_scope.rb +44 -0
- data/lib/wlang/template.rb +10 -2
- data/lib/wlang/tilt/wlang_template.rb +1 -1
- data/lib/wlang/version.rb +2 -2
- data/spec/assumptions/test_core.rb +8 -0
- data/spec/fixtures/templates/hello_from_sinatra.wlang +1 -0
- data/spec/integration/html/test_greater.rb +12 -2
- data/spec/integration/sinatra/test_partials.rb +35 -0
- data/spec/integration/test_examples.rb +2 -2
- data/spec/spec_helper.rb +7 -0
- data/spec/unit/compiler/test_grammar.rb +1 -1
- data/spec/unit/compiler/test_parser.rb +17 -0
- data/spec/unit/dialect/test_context.rb +28 -0
- data/spec/unit/dialect/test_evaluate.rb +10 -0
- data/spec/unit/dialect/test_render.rb +4 -0
- data/spec/unit/scope/sinatra_scope/test_fetch.rb +28 -0
- data/spec/unit/scope/test_binding_scope.rb +1 -1
- data/spec/unit/scope/test_chain.rb +5 -5
- data/spec/unit/scope/test_coerce.rb +7 -3
- data/spec/unit/scope/test_null_scope.rb +35 -0
- data/spec/unit/scope/test_object_scope.rb +1 -1
- data/spec/unit/scope/test_proc_scope.rb +1 -1
- data/spec/unit/scope/test_push.rb +70 -0
- data/spec/unit/template/test_call_args_conventions.rb +101 -0
- data/spec/unit/test_assumptions.rb +12 -0
- data/spec/unit/test_scope.rb +26 -16
- data/wlang.gemspec +2 -0
- data/wlang.noespec +3 -1
- metadata +143 -33
- data/lib/wlang/scope/proxy_scope.rb +0 -18
- data/lib/wlang/scope/root_scope.rb +0 -24
- data/spec/unit/scope/test_proxy_scope.rb +0 -22
- data/spec/unit/scope/test_root_scope.rb +0 -22
@@ -2,10 +2,10 @@ module WLang
|
|
2
2
|
class Scope
|
3
3
|
class BindingScope < Scope
|
4
4
|
|
5
|
-
def fetch(
|
6
|
-
subject.eval(
|
5
|
+
def fetch(key, dialect = nil, unfound = nil)
|
6
|
+
subject.eval(key.to_s)
|
7
7
|
rescue NameError
|
8
|
-
|
8
|
+
safe_parent.fetch(key, dialect, unfound)
|
9
9
|
end
|
10
10
|
|
11
11
|
def inspect
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module WLang
|
2
|
+
class Scope
|
3
|
+
class NullScope < Scope
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super(nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
def fetch(key, dialect = nil, unfound = nil)
|
10
|
+
unfound ? unfound.call : throw(:fail)
|
11
|
+
end
|
12
|
+
|
13
|
+
def subjects
|
14
|
+
[]
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
"NullScope"
|
19
|
+
end
|
20
|
+
alias :to_s :inspect
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def append(x)
|
25
|
+
x
|
26
|
+
end
|
27
|
+
|
28
|
+
def prepend(x)
|
29
|
+
x
|
30
|
+
end
|
31
|
+
|
32
|
+
end # class NullScope
|
33
|
+
end # class Scope
|
34
|
+
end # module WLang
|
@@ -2,17 +2,28 @@ module WLang
|
|
2
2
|
class Scope
|
3
3
|
class ObjectScope < Scope
|
4
4
|
|
5
|
-
def fetch(
|
5
|
+
def fetch(key, dialect = nil, unfound = nil)
|
6
6
|
s = subject
|
7
|
-
|
7
|
+
|
8
|
+
# self special case
|
9
|
+
if key == :self
|
10
|
+
return s
|
11
|
+
end
|
12
|
+
|
13
|
+
# hash indirect access
|
8
14
|
if s.respond_to?(:has_key?)
|
9
|
-
return s[
|
10
|
-
return s[
|
15
|
+
return s[key] if s.has_key?(key)
|
16
|
+
return s[key.to_s] if s.has_key?(key.to_s)
|
11
17
|
end
|
12
|
-
|
13
|
-
|
18
|
+
|
19
|
+
# getter
|
20
|
+
if s.respond_to?(key)
|
21
|
+
return s.send(key)
|
22
|
+
end
|
23
|
+
|
24
|
+
safe_parent.fetch(key, dialect, unfound)
|
14
25
|
rescue NameError
|
15
|
-
|
26
|
+
safe_parent.fetch(key, dialect, unfound)
|
16
27
|
end
|
17
28
|
|
18
29
|
def inspect
|
@@ -2,10 +2,10 @@ module WLang
|
|
2
2
|
class Scope
|
3
3
|
class ProcScope < Scope
|
4
4
|
|
5
|
-
def fetch(key,
|
6
|
-
Scope.coerce(subject.call)
|
7
|
-
|
8
|
-
|
5
|
+
def fetch(key, dialect = nil, unfound = nil)
|
6
|
+
scoped = Scope.coerce(subject.call)
|
7
|
+
fallbk = lambda{ safe_parent.fetch(key, dialect, unfound) }
|
8
|
+
scoped.fetch(key, dialect, fallbk)
|
9
9
|
end
|
10
10
|
|
11
11
|
def inspect
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module WLang
|
2
|
+
class Scope
|
3
|
+
class SinatraScope < ObjectScope
|
4
|
+
|
5
|
+
def fetch(key, dialect = nil, unfound = nil)
|
6
|
+
find_partial(key, subject) || super
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"SinatraScope"
|
11
|
+
end
|
12
|
+
alias :to_s :inspect
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def find_partial(key, app)
|
17
|
+
find_internal_partial(key, app) || find_external_partial(key, app)
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_internal_partial(key, app)
|
21
|
+
return unless app.settings.templates[key]
|
22
|
+
app.send(:compile_template, :wlang, key, {}, app.settings.views)
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_external_partial(key, app)
|
26
|
+
views = app.settings.views
|
27
|
+
find_files(views, key) do |file|
|
28
|
+
if engine = Tilt[file]
|
29
|
+
tpl = app.template_cache.fetch(file) do
|
30
|
+
engine.new(file.to_s, 1, {})
|
31
|
+
end
|
32
|
+
return tpl
|
33
|
+
end
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_files(folder, name, &bl)
|
39
|
+
Path(folder).glob("#{name}.*").each(&bl)
|
40
|
+
end
|
41
|
+
|
42
|
+
end # class SinatraScope
|
43
|
+
end # class Scope
|
44
|
+
end # module WLang
|
data/lib/wlang/template.rb
CHANGED
@@ -44,8 +44,8 @@ module WLang
|
|
44
44
|
compiler.to_ast(template_content)
|
45
45
|
end
|
46
46
|
|
47
|
-
def call(
|
48
|
-
scope =
|
47
|
+
def call(*args)
|
48
|
+
scope, buffer = call_args_conventions(args)
|
49
49
|
dialect_instance.dup.render compiled, scope, buffer
|
50
50
|
end
|
51
51
|
alias :render :call
|
@@ -59,5 +59,13 @@ module WLang
|
|
59
59
|
opt.nil? or opt
|
60
60
|
end
|
61
61
|
|
62
|
+
def call_args_conventions(args)
|
63
|
+
args << '' unless args.last.respond_to?(:<<)
|
64
|
+
buffer = args.pop
|
65
|
+
args << self.locals unless self.locals.empty?
|
66
|
+
scope = WLang::Scope.chain(args)
|
67
|
+
[scope, buffer]
|
68
|
+
end
|
69
|
+
|
62
70
|
end # class Template
|
63
71
|
end # module WLang
|
data/lib/wlang/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
Hello >{hello}!
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'wlang/html'
|
3
2
|
module WLang
|
4
3
|
describe Html, ">{...}" do
|
5
4
|
|
@@ -13,11 +12,22 @@ module WLang
|
|
13
12
|
render("Hello >{partial}", binding).should eq("Hello World")
|
14
13
|
end
|
15
14
|
|
15
|
+
it 'recognizes a Tilt::WLangTemplate' do
|
16
|
+
who = "World"
|
17
|
+
partial = Tilt::WLangTemplate.new{ "${who}" }
|
18
|
+
render("Hello >{partial}", binding).should eq("Hello World")
|
19
|
+
end
|
20
|
+
|
16
21
|
it 'compiles a String to a Template' do
|
17
22
|
who = "World"
|
18
23
|
partial = "${who}"
|
19
24
|
render("Hello >{partial}", binding).should eq("Hello World")
|
20
25
|
end
|
21
26
|
|
27
|
+
it 'call a Proc as +{...} does' do
|
28
|
+
partial = Proc.new{ "World" }
|
29
|
+
render("Hello >{partial}", binding).should eq("Hello World")
|
30
|
+
end
|
31
|
+
|
22
32
|
end
|
23
|
-
end
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rack/test'
|
3
|
+
describe 'Integration with Sinatra for partials' do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
let(:app){
|
7
|
+
sinatra_app do
|
8
|
+
set :accessible, "world"
|
9
|
+
set :views, fixtures_folder/'templates'
|
10
|
+
template :internal_partial do
|
11
|
+
"Hello ${who}!"
|
12
|
+
end
|
13
|
+
helpers do
|
14
|
+
def accessible; settings.accessible; end
|
15
|
+
end
|
16
|
+
get '/external' do
|
17
|
+
wlang ">{hello}", :locals => {:who => "sinatra"}
|
18
|
+
end
|
19
|
+
get '/internal' do
|
20
|
+
wlang ">{internal_partial}", :locals => {:who => "sinatra"}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
it 'renders external partials correcty' do
|
26
|
+
get '/external'
|
27
|
+
last_response.body.should eq("Hello sinatra!")
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'renders internal partials correcty' do
|
31
|
+
get '/internal'
|
32
|
+
last_response.body.should eq("Hello sinatra!")
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -3,7 +3,7 @@ require 'wlang/command'
|
|
3
3
|
module WLang
|
4
4
|
describe "The examples", :hash_ordering => true do
|
5
5
|
|
6
|
-
(root_folder
|
6
|
+
(root_folder/"examples/1-html-intro").glob("**/*.*").each do |ex_file|
|
7
7
|
next if ex_file.basename.to_s == "README.md"
|
8
8
|
describe "the example file #{ex_file}" do
|
9
9
|
|
@@ -27,4 +27,4 @@ module WLang
|
|
27
27
|
end
|
28
28
|
|
29
29
|
end
|
30
|
-
end
|
30
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'epath'
|
2
2
|
$root_folder ||= Path.backfind('.[Rakefile]')
|
3
3
|
|
4
|
+
require 'tilt'
|
5
|
+
require 'sinatra/base'
|
6
|
+
|
4
7
|
# Require wlang
|
5
8
|
$LOAD_PATH.unshift(($root_folder/:lib).to_s)
|
6
9
|
require 'wlang'
|
@@ -53,6 +56,10 @@ module Helpers
|
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
59
|
+
def sinatra_app(&block)
|
60
|
+
Sinatra.new(Sinatra::Base, &block).new!
|
61
|
+
end
|
62
|
+
|
56
63
|
end
|
57
64
|
include Helpers
|
58
65
|
|
@@ -158,7 +158,7 @@ module WLang
|
|
158
158
|
describe 'the block rule' do
|
159
159
|
let(:rule){ :block }
|
160
160
|
let(:text){ "{ world }" }
|
161
|
-
it{ should eq([:static,
|
161
|
+
it{ should eq([:strconcat, [:static, "{"], [:static, " world "], [:static, "}"]]) }
|
162
162
|
end
|
163
163
|
|
164
164
|
describe 'the wlang rule' do
|
@@ -43,6 +43,23 @@ module WLang
|
|
43
43
|
parse("${first}{second}").should eq(expected)
|
44
44
|
end
|
45
45
|
|
46
|
+
it 'should support wlang tags inside normal { ... }' do
|
47
|
+
expected = \
|
48
|
+
[:template,
|
49
|
+
[:fn,
|
50
|
+
[:strconcat,
|
51
|
+
[:static, "hello "],
|
52
|
+
[:strconcat,
|
53
|
+
[:static, "{"],
|
54
|
+
[:strconcat,
|
55
|
+
[:static, "bar "],
|
56
|
+
[:wlang, "$",
|
57
|
+
[:fn,
|
58
|
+
[:static, "second"]]]],
|
59
|
+
[:static, "}"]]]]]
|
60
|
+
parse("hello {bar ${second}}").should eq(expected)
|
61
|
+
end
|
62
|
+
|
46
63
|
it 'is idempotent' do
|
47
64
|
parse(parse(hello_tpl)).should eq(expected)
|
48
65
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module WLang
|
3
|
+
describe Dialect, 'context' do
|
4
|
+
|
5
|
+
let(:dialect){ Dialect.new }
|
6
|
+
|
7
|
+
subject{ dialect.context }
|
8
|
+
|
9
|
+
it 'defaults to nil if no scope' do
|
10
|
+
subject.should be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "is the scope's subject when one scope" do
|
14
|
+
dialect.with_scope(:x) do
|
15
|
+
subject.should eq(:x)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "is the root scope's subject when multiple scope" do
|
20
|
+
dialect.with_scope(:x) do
|
21
|
+
dialect.with_scope(:y) do
|
22
|
+
subject.should eq(:x)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -40,6 +40,16 @@ module WLang
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
it 'supports a default value' do
|
44
|
+
evaluate("who", 12).should eq(12)
|
45
|
+
evaluate("who", nil).should be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'supports a default value through a Proc' do
|
49
|
+
evaluate("who"){ 12 }.should eq(12)
|
50
|
+
evaluate("who"){ nil }.should be_nil
|
51
|
+
end
|
52
|
+
|
43
53
|
it 'raises a NameError when not found' do
|
44
54
|
lambda{ evaluate("who") }.should raise_error(NameError)
|
45
55
|
end
|
@@ -17,6 +17,10 @@ module WLang
|
|
17
17
|
U.render(hello_tpl, {}).should eq(expected)
|
18
18
|
end
|
19
19
|
|
20
|
+
it "accepts multiple scope objects" do
|
21
|
+
U.render(hello_tpl, 12, {}).should eq(expected)
|
22
|
+
end
|
23
|
+
|
20
24
|
it 'accepts a :to_path object' do
|
21
25
|
U.render(hello_path).should eq(expected)
|
22
26
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module WLang
|
3
|
+
class Scope
|
4
|
+
describe SinatraScope, 'fetch' do
|
5
|
+
|
6
|
+
let(:app){
|
7
|
+
sinatra_app do
|
8
|
+
set :accessible, "world"
|
9
|
+
set :views, fixtures_folder/'templates'
|
10
|
+
helpers do
|
11
|
+
def accessible; settings.accessible; end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
}
|
15
|
+
|
16
|
+
let(:scope){ Scope::SinatraScope.new(app) }
|
17
|
+
|
18
|
+
it 'delegates to helpers correctly' do
|
19
|
+
scope.fetch(:accessible).should eq("world")
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'returns Tilt templates on existing views' do
|
23
|
+
scope.fetch('hello', app).should be_a(Tilt::Template)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|