Can't access constants when include Tilt::CompileSite

30 views
Skip to first unread message

Magnus Holm

unread,
Apr 24, 2010, 8:50:23 AM4/24/10
to til...@googlegroups.com
Test:

diff --git a/test/tilt_template_test.rb b/test/tilt_template_test.rb
index ec31be8..101db33 100644
--- a/test/tilt_template_test.rb
+++ b/test/tilt_template_test.rb
@@ -122,6 +122,8 @@ class TiltTemplateTest < Test::Unit::TestCase
end

class Person
+ CONSTANT = "Bob"
+
attr_accessor :name
def initialize(name)
@name = name
@@ -138,4 +140,18 @@ class TiltTemplateTest < Test::Unit::TestCase
inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{yield}!' }
assert_equal "Hey Joe!", inst.render(Object.new){ 'Joe' }
end
+
+ test "template which accesses a constant" do
+ inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{CONSTANT}!' }
+ assert_equal "Hey Bob!", inst.render(Person.new("Joe"))
+ end
+
+ class FastPerson < Person
+ include Tilt::CompileSite
+ end
+
+ test "template which accesses a constant with Tilt::CompileSite" do
+ inst = SourceGeneratingMockTemplate.new { |t| 'Hey #{CONSTANT}!' }
+ assert_equal "Hey Bob!", inst.render(FastPerson.new("Joe"))
+ end
end


Example:

class Scope
CONSTANT = 1
# Works if you comment the next line:
include Tilt::CompileSite
end

Tilt::StringTemplate.new { '#{CONSTANT}' }.render(Scope.new)


Any suggestions on how we can solve this?

// Magnus Holm


--
Subscription settings: http://groups.google.com/group/tiltrb/subscribe?hl=en

Ryan Tomayko

unread,
Apr 25, 2010, 1:36:07 AM4/25/10
to til...@googlegroups.com
Oh that blows. This is ostensibly due to the non-dynamic constant
lookup path. Here's a pure Ruby example to illustrate roughly what's
happening:

class Base
CONSTANT = 'FOO'
end

module Mixin
def test
puts "CONSTANT: #{CONSTANT}"
end
end

class Base
include Mixin
end

Base.new.test

I could swear there were plans to make constant lookup dynamic in Ruby
1.9 but I get the same behavior in all current release versions of
Ruby from 1.8.7 through 1.9.2.

I don't have any ideas for how to get around this. Constants in
compiled templates are going to be limited to those visible from
Tilt::CompileSite.

Anyone else have any ideas? What does Rails do here?

I've committed the failing test case. Hopefully we can figure something out.

Thanks,
Ryan

Magnus Holm

unread,
Apr 25, 2010, 2:57:53 PM4/25/10
to til...@googlegroups.com
This works in 1.8, but unfortunately not in 1.9:

diff --git a/lib/tilt.rb b/lib/tilt.rb
index e61efe9..136bd54 100644
--- a/lib/tilt.rb
+++ b/lib/tilt.rb
@@ -188,7 +188,7 @@ module Tilt
if scope.respond_to?(method_name)
scope.send(method_name, locals, &block)
else
- compile_template_method(method_name, locals)
+ compile_template_method(scope.class, method_name, locals)
scope.send(method_name, locals, &block)
end
else
@@ -280,12 +280,15 @@ module Tilt
"__tilt_#{digest}"
end

- def compile_template_method(method_name, locals)
+ def compile_template_method(klass, method_name, locals)
source, offset = precompiled(locals)
- offset += 1
- CompileSite.module_eval <<-RUBY, eval_file, line - offset
- def #{method_name}(locals)
- #{source}
+ offset += 2
+
+ klass.class_eval <<-RUBY, eval_file, line - offset
+ ::Tilt::CompileSite.class_eval do
+ def #{method_name}(locals)
+ #{source}
+ end
end
RUBY


// Magnus Holm

Magnus Holm

unread,
Apr 25, 2010, 3:42:30 PM4/25/10
to til...@googlegroups.com
Okay, this is hackish as hell, but seems to work fine in 1.9 too:

diff --git a/lib/tilt.rb b/lib/tilt.rb
index e61efe9..4ea328b 100644
--- a/lib/tilt.rb
+++ b/lib/tilt.rb
@@ -188,7 +188,7 @@ module Tilt
if scope.respond_to?(method_name)
scope.send(method_name, locals, &block)
else
- compile_template_method(method_name, locals)
+ compile_template_method(scope.class, method_name, locals)
scope.send(method_name, locals, &block)
end
else
@@ -279,18 +279,51 @@ module Tilt
digest = Digest::MD5.hexdigest(parts.join(':'))
"__tilt_#{digest}"
end
-
- def compile_template_method(method_name, locals)
- source, offset = precompiled(locals)
- offset += 1
- CompileSite.module_eval <<-RUBY, eval_file, line - offset
- def #{method_name}(locals)
- #{source}
- end
- RUBY
-
- ObjectSpace.define_finalizer self,
- Template.compiled_template_method_remover(CompileSite, method_name)
+
+ if RUBY_VERSION < '1.9'
+ def compile_template_method(klass, method_name, locals)
+ source, offset = precompiled(locals)
+ offset += 2
+
+ klass.class_eval <<-RUBY, eval_file, line - offset
+ ::Tilt::CompileSite.class_eval do
+ def #{method_name}(locals)
+ #{source}
+ end
+ end
+ RUBY
+
+ ObjectSpace.define_finalizer self,
+ Template.compiled_template_method_remover(CompileSite, method_name)
+ end
+ else
+ def compile_template_method(klass, method_name, locals)
+ source, offset = precompiled(locals)
+ definer = "__tilt_definer_#{rand.to_s[2..-1]}"
+ offset += 3
+
+ klass.class_eval <<-RUBY, eval_file, line - offset
+ def self.#{definer}
+ ::Tilt::CompileSite.send(:define_method,
#{method_name.inspect}) do |locals, &blk|
+ Thread.current[:tilt_blk] = blk
+ #{source}
+ end
+ end
+
+ #{definer} do |*a|
+ if blk = Thread.current[:tilt_blk]
+ blk.call(*a)
+ else
+ raise LocalJumpError, "no block given"
+ end
+ end
+
+ class << self; undef #{definer} end
+ RUBY
+
+ ObjectSpace.define_finalizer self,
+ Template.compiled_template_method_remover(CompileSite, method_name)
+ end
end

def self.compiled_template_method_remover(site, method_name)


// Magnus Holm
Reply all
Reply to author
Forward
0 new messages