wlang 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/CHANGELOG.md +27 -0
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +16 -0
  4. data/README.md +32 -55
  5. data/lib/wlang.rb +8 -0
  6. data/lib/wlang/compiler/grammar.citrus +19 -16
  7. data/lib/wlang/dialect.rb +18 -14
  8. data/lib/wlang/html.rb +7 -11
  9. data/lib/wlang/scope.rb +68 -28
  10. data/lib/wlang/scope/binding_scope.rb +3 -3
  11. data/lib/wlang/scope/null_scope.rb +34 -0
  12. data/lib/wlang/scope/object_scope.rb +18 -7
  13. data/lib/wlang/scope/proc_scope.rb +4 -4
  14. data/lib/wlang/scope/sinatra_scope.rb +44 -0
  15. data/lib/wlang/template.rb +10 -2
  16. data/lib/wlang/tilt/wlang_template.rb +1 -1
  17. data/lib/wlang/version.rb +2 -2
  18. data/spec/assumptions/test_core.rb +8 -0
  19. data/spec/fixtures/templates/hello_from_sinatra.wlang +1 -0
  20. data/spec/integration/html/test_greater.rb +12 -2
  21. data/spec/integration/sinatra/test_partials.rb +35 -0
  22. data/spec/integration/test_examples.rb +2 -2
  23. data/spec/spec_helper.rb +7 -0
  24. data/spec/unit/compiler/test_grammar.rb +1 -1
  25. data/spec/unit/compiler/test_parser.rb +17 -0
  26. data/spec/unit/dialect/test_context.rb +28 -0
  27. data/spec/unit/dialect/test_evaluate.rb +10 -0
  28. data/spec/unit/dialect/test_render.rb +4 -0
  29. data/spec/unit/scope/sinatra_scope/test_fetch.rb +28 -0
  30. data/spec/unit/scope/test_binding_scope.rb +1 -1
  31. data/spec/unit/scope/test_chain.rb +5 -5
  32. data/spec/unit/scope/test_coerce.rb +7 -3
  33. data/spec/unit/scope/test_null_scope.rb +35 -0
  34. data/spec/unit/scope/test_object_scope.rb +1 -1
  35. data/spec/unit/scope/test_proc_scope.rb +1 -1
  36. data/spec/unit/scope/test_push.rb +70 -0
  37. data/spec/unit/template/test_call_args_conventions.rb +101 -0
  38. data/spec/unit/test_assumptions.rb +12 -0
  39. data/spec/unit/test_scope.rb +26 -16
  40. data/wlang.gemspec +2 -0
  41. data/wlang.noespec +3 -1
  42. metadata +143 -33
  43. data/lib/wlang/scope/proxy_scope.rb +0 -18
  44. data/lib/wlang/scope/root_scope.rb +0 -24
  45. data/spec/unit/scope/test_proxy_scope.rb +0 -22
  46. 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(k, &blk)
6
- subject.eval(k.to_s)
5
+ def fetch(key, dialect = nil, unfound = nil)
6
+ subject.eval(key.to_s)
7
7
  rescue NameError
8
- parent.fetch(k, &blk)
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(k, &blk)
5
+ def fetch(key, dialect = nil, unfound = nil)
6
6
  s = subject
7
- return s if k == :self
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[k] if s.has_key?(k)
10
- return s[k.to_s] if s.has_key?(k.to_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
- return s.send(k) if s.respond_to?(k)
13
- parent.fetch(k, &blk)
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
- parent.fetch(k, &blk)
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, &blk)
6
- Scope.coerce(subject.call).fetch(key) do
7
- parent.fetch(key, &blk)
8
- end
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
@@ -44,8 +44,8 @@ module WLang
44
44
  compiler.to_ast(template_content)
45
45
  end
46
46
 
47
- def call(locs = {}, buffer = '')
48
- scope = WLang::Scope.chain([locals, locs])
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
@@ -35,7 +35,7 @@ module Tilt
35
35
 
36
36
  def evaluate(scope, locals, &block)
37
37
  locals[:yield] = block if block
38
- @engine.render WLang::Scope.chain([scope, locals])
38
+ @engine.render(scope, locals)
39
39
  end
40
40
 
41
41
  end
data/lib/wlang/version.rb CHANGED
@@ -2,8 +2,8 @@ module WLang
2
2
  module Version
3
3
 
4
4
  MAJOR = 2
5
- MINOR = 0
6
- TINY = 1
5
+ MINOR = 1
6
+ TINY = 0
7
7
 
8
8
  def self.to_s
9
9
  [ MAJOR, MINOR, TINY ].join('.')
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ describe "Core assumptions" do
3
+
4
+ specify 'Hash does not respond to #<<' do
5
+ {}.should_not respond_to(:<<)
6
+ end
7
+
8
+ end
@@ -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/:examples).glob("**/*.*").each do |ex_file|
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, text]) }
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
@@ -10,7 +10,7 @@ module WLang
10
10
  end
11
11
 
12
12
  it 'delegates fetch to its parent when not found' do
13
- scope = Scope.coerce(binding, Scope.coerce(:who => "World"))
13
+ scope = Scope.coerce(:who => "World").push(binding)
14
14
  scope.fetch(:who).should eq("World")
15
15
  end
16
16