I'm using template.erb
as my template example, which contains: <%= @text %>
.
I'd like to be able to render a tilt template with the scope being a block
.
I've done this, which works:
proc = Proc.new do
@text = "Hello, World"
Tilt.new('template.erb').render self
end
proc.call # => "Hello, World"
But I'd like to render the template outside of the block, like this:
proc = Proc.new do
@text = "Hello, World"
end
tilt = Tilt.new('template.erb')
# I tried these alternatives:
tilt.render proc # => ""
tilt.render proc.binding # => ""
tilt.render &proc # => ""
However, I succeeded when I used the standard ERB library like so:
proc = Proc.new do
@text = "Hello, World"
@num = 100
end
ERB.new('<%= @text %> | <%= @num %>').result proc.binding # => "Hello, World | 100"
I'd still like to use Tilt, though, because I want to support other template engines.
However, I succeeded when I used the standard ERB library like so:
proc = Proc.new do @text = "Hello, World" @num = 100 end ERB.new('<%= @text %> | <%= @num %>').result proc.binding # => "Hello, World | 100"
If I insert the line, require 'erb'
at the start of your code, and if I append 'p ' to your last line, the output I get is:
" | "
I tested that in rubies: 1.8.7, 1.9.3, 2.0, 2.1.
The reason @text and @num are nil is because they are not part of the proc's binding. First, the code in a block, like the code in a def, does not even execute until the block is called, so whether the proc is empty or has @ variables in it makes no difference if the proc is never executed.
Second, the only bindings (i.e. the variables and their values in the surrounding scope) that can be seen by your proc is self=main. And an @ variable attaches itself to whatever object is self when the @ variable springs into existence. As a result, when your proc does execute, the @ variables will fly away and attach themselves to main, which means they will have no association with your proc at all.
Third, you can't even change the value for self in the binding:
class Dog
def bark(proc_obj)
#self is a dog instance in here
proc_obj.call
p @text
end
end
my_proc = Proc.new{@text = "hello"}
Dog.new.bark my_proc
puts @text
--output:--
nil
hello
When the proc executes there is a variable named self in the surrounding scope, and its value is the dog instance--yet the @ variable in the proc attaches itself to self=main(the last puts statement shows that) rather than self=dog_instance. To me, that indicates that self acts like a local variable, and the proc closes over the local variable named self in the toplevel scope. Then, in the def ruby creates another local variable named self, which ruby sets equal to the dog instance that called the def. As a result, the toplevel self and the self in the def are two different variables, and therefore the value of the self in the def has no effect on the value the proc sees for self.
Finally, if you look at the docs for ruby 2.1, the Binding class now has a method for retrieving variables from the binding, and it's called local_variable_get()
. Note the name local. There is no method named instance_variable_get()
because as far as I can tell instance variables are not part of a binding--instead they fly away and attach themselves to some object, i.e. whatever self was equal to at the time the proc was created--not what self is equal to when the proc executes.