Last active
June 3, 2018 02:24
-
-
Save JoshCheek/add98df43634e7a79c764c90692956ca to your computer and use it in GitHub Desktop.
How to get a binding for ERB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'erb' | |
class Num | |
def initialize(num) | |
@n = num | |
end | |
end | |
# okay, some object we want to evaluate our ERB within, | |
# to do that we need a binding for it | |
num = Num.new 2 | |
# we're in main, so even though we call `binding` on `num` | |
# it's a binding to our current scope... sure feels like a bug | |
num.send(:binding).receiver # => main | |
# you can get a binding off the obj with something like this: | |
num.instance_eval { binding }.receiver # => #<Num:0x00007fa8ba0c5188 @n=2> | |
# but note that we call `binding` in a block there, and blocks are closures, | |
# so that binding has access to our local variables. | |
num.instance_eval { binding }.local_variables # => [:num, :b] | |
# NOTE: | |
# it sees "b", which is defined below, due to implementation details... | |
# Ruby does allocate space for the var, like JS hoisting, which is why | |
# we can already see it, but it will error if you try to access it | |
# before initializing it, unlike JS hoisting | |
# soooo, we need a pristine binding, which methods have | |
# (methods in Ruby are not closures), so we'll go into a method | |
# to get a pristine outter binding and use our trick from there. | |
# but how do we get the object, if we can't use locals? | |
# we store it as an ivar (you could also pass it via an implicit block) | |
class GetBinding | |
def initialize(obj) | |
@obj = obj | |
end | |
def call | |
@obj.instance_eval { binding } | |
end | |
end | |
# there, now we have a pristine binding on the right object | |
b = GetBinding.new(num).call | |
b.receiver # => #<Num:0x00007fa8ba0c5188 @n=2> | |
b.local_variables # => [] | |
# if setting locals is something you want to do, as a feature of your lib: | |
b.local_variable_set :l, 3 | |
b.local_variables # => [:l] | |
# and now, we can finally pass it to `ERB` *whew* | |
# (note that IDK if anything is changed by omitting the rest of the init args) | |
ERB.new('1 <%= @n %> <%= l %> 4').result(b) | |
# => "1 2 3 4" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment