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.
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