The variable is ours.
作者:gugod 發佈於:As I was writing the controller class of Railsish , I was facing a rather interesting problem: what's a good way to pass variables into the template ?
If you know Rails, the one that I'm trying to cloning, or clone-ish-ing, the view code or sort of like an instance method of its corresponding class. All the instance variables that gets initialized in the controller are automatically viewable in the view code. This is super neat. Because then you don't really have to aggregate those into a hash, and them pass it to render
.
Since there is no such obvious thing in Perl that's known as ``instance variables'', I have to make something up. At least, something that's sweet enough, that I don't have to pass any variables to the `render` function.
So the goal is something like:
package FooController; # My action code sub index { my $name = "gugod"; ## @*#$&{}.... Magic! render; # Then "$name" is somehow available in the template code. }
Then the solution is somehow easy: PadWalker.
If the render
function can automatically grab variables from its caller, including their names and values, then they can be passed into my template system (in my case, it's TT2) without worrying name changes.
But in a way, I don't want render
to grab all variables from its caller scope, because then it'll be too much trash like $bar
and $i
. I want to make it very neat, and yet still controllable.
Easy, just grab our
variable instead of my
variables.
Heres' the code that I wrote for my render
function in the Railsish:
sub build_stash { my $caller_vars = Binding->of_caller(2)->our_vars; my $stash = {}; for my $varname (keys %$caller_vars) { my $val = $caller_vars->{$varname}; $varname =~ s/^[\$%@]//; $val = $$val if ref($val) eq any('SCALAR', 'REF'); $stash->{$varname} = $val; } return $stash; }
I used my Binding
module instead using PadWalker
directly, to fetch our variables above, and re-arrange them by their sigils into proper forms that's ready for TT2 system.
So then in my controller code I wrote:
sub index { our $title = "Welcome"; render; }
And in the view it's:
[% title %]
It's probably better to invent other variable decelerator instead of just using "our", since it also clashes with package variables a bit, but this is a rather easy solution so far.