wlang 2.0.1 → 2.1.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/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
|