# Extracting instance variables from a block?

**URL:** <https://www.ruby-forum.com/t/extracting-instance-variables-from-a-block/101810>\
**Category:** Ruby\
**Created:** [August 8, 2006, 3:08am UTC](https://www.ruby-forum.com/t/extracting-instance-variables-from-a-block/101810 "2006-08-08T03:08:00Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![azraelnewtype](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/13edae/32.png) [@azraelnewtype](https://www.ruby-forum.com/u/azraelnewtype)\
**Post date:** [August 8, 2006, 3:08am UTC](https://www.ruby-forum.com/t/extracting-instance-variables-from-a-block/101810/1 "2006-08-08T03:08:00Z")

</div>

Hello fellow Rubyists,

I am writing a tiny custom web framework, similar in nature to web.py  
([http://webpy.org/](http://webpy.org/)). However, I’d like someone with better  
metaprogramming-foo to help me clean up some syntax a bit.

Here’s a _very_ basic abbreviated version of the framework:

class Web  
def initialize  
@pages = {}  
@vars = {}  
end

# Associates a URI regular expression with a block of code.

def page(regexp, &action)  
@pages[/^#{regexp}$/] = action  
end

# Sets a variable for use in the template. See the example below.

def []=(var, val)  
@vars[var] = val  
end

# Renders the given ERB template file.

def render(template)  
o = Object.new  
def o.b; binding; end  
@vars.each { |var, val| o.instance\_variable\_set(“@#{var}”, val) }  
print CGI.new.header(‘text/html’)  
ERB.new(File.read(template)).run(o.b)  
end

# Compares each regexp in @pages to ENV[‘REQUEST\_URI’]. If a match

# is found, calls the associated action block, passing in the match

# results as arguments.

def run  
for regexp, action in @pages  
if ENV[‘REQUEST\_URI’] =~ regexp  
action[\*$~.to\_a[1…-1]]  
return  
end  
end  
# Oh noes! 404 error handling stuff goes here.  
end  
end

A simple application would look something like this:

web = Web.new

web.page ‘/hello/(\w+)’ do |name|  
web[:title] = ‘Hello’  
web[:name] = name  
web.render ‘hello.rhtml’  
end

web.run

# Contents of hello.rhtml
 \<%= @title %\> 
# Hello, \<%= @name %\>!

How are you today?

The Web#run method reads the request URI, and sees if it matches with  
any of your defined regexps. If it does, it runs the block of code you  
associated that URL with (passing in any needed parameters). So when you  
visit [http://example.com/hello/\_why](http://example.com/hello/_why), it displays a nice welcome message  
to \_why.

As you can see, you can use web[:foo] = ‘bar’ to set an instance  
variable, @foo, in the template’s binding. This is all nice and great,  
but the syntax is a bit… crusty. I’d love to do something like this  
instead:

web.page ‘/hello/(\w+)’ do |name|  
@title = ‘Hello’  
@name = name  
web.render ‘hello.rhtml’  
end

So, my question: is there any way to capture the instance variables set  
in the block, and use them for a template binding elsewhere? Or at least  
do something else that gives me my desired results?

Thanks,  
Chris

---

<div class="post-metadata">

**Author:** ![azraelnewtype](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/13edae/32.png) [@azraelnewtype](https://www.ruby-forum.com/u/azraelnewtype)\
**Post date:** [August 8, 2006, 4:37am UTC](https://www.ruby-forum.com/t/extracting-instance-variables-from-a-block/101810/2 "2006-08-08T04:37:45Z")

</div>

On Aug 7, 2006, at 6:08 PM, Chris Eskow wrote:

> Hello fellow Rubyists,
> 
> I am writing a tiny custom web framework, similar in nature to web.py  
> ([http://webpy.org/](http://webpy.org/)). However, I’d like someone with better  
> metaprogramming-foo to help me clean up some syntax a bit.
> 
> Here’s a _very_ basic abbreviated version of the framework:

> 

> Posted via [http://www.ruby-forum.com/](http://www.ruby-forum.com/).

Hey Chris-

```
Cool little class ;) Take a look at this, you should be able to use

```

something like it to do what you want.

class Web  
def page(url, &blk)  
@url = url  
instance\_eval &blk  
end  
end

web = Web.new

web.page ‘/hello/(\w+)’ do |name|  
@title = ‘Hello’  
@name = “Ernie”  
end

puts web.inspect

=\> #\<Web:0x32c998 @title=“Hello”, @name=“Ernie”, @url=“/hello/(\w+)”\>

-Ezra

---

<div class="post-metadata">

**Author:** ![azraelnewtype](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/13edae/32.png) [@azraelnewtype](https://www.ruby-forum.com/u/azraelnewtype)\
**Post date:** [August 8, 2006, 5:12am UTC](https://www.ruby-forum.com/t/extracting-instance-variables-from-a-block/101810/3 "2006-08-08T05:12:20Z")

</div>

Ezra Z. wrote:

> [snip]
> 
> class Web  
> def page(url, &blk)  
> @url = url  
> instance\_eval &blk  
> end  
> end
> 
> web = Web.new
> 
> web.page ‘/hello/(\w+)’ do |name|  
> @title = ‘Hello’  
> @name = “Ernie”  
> end
> 
> puts web.inspect
> 
> =\> #\<Web:0x32c998 @title=“Hello”, @name=“Ernie”, @url="/hello/(\w+)"\>
> 
> -Ezra

Thanks for the suggestion (and the compliment), but the problem with  
that is that for every page you define, the block of code you associate  
it with will be called. So, if you define several pages like so:

web.page(’/’) { web.render ‘home.rhtml’ }  
web.page(’/about’) { web.render ‘about.rhtml’ }  
web.page(’/hello/(\w+)’) { |name| @name = name; web.render ‘hello.rhtml’  
}

It will render all three templates at once! But it did get me  
thinking… What if I instance\_eval it within Web#run? For example:

class Web  
def initialize  
#…  
@obj = Object.new  
def @obj.b; binding; end  
end

def render(template)  
#…  
ERB.new(File.read(template)).run(@obj.b)  
end

def run  
#…  
@obj.instance\_eval(&action)  
end  
end

That sort of works nice… But you can’t pass in the URL parameters (the  
ones that your URL regexps match). Is there some way that you can call a  
Proc object (with parameters), but within the context of a specific  
object’s binding?

Chris

---

<div class="post-metadata">

**Author:** ![azraelnewtype](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/13edae/32.png) [@azraelnewtype](https://www.ruby-forum.com/u/azraelnewtype)\
**Post date:** [August 8, 2006, 5:37am UTC](https://www.ruby-forum.com/t/extracting-instance-variables-from-a-block/101810/4 "2006-08-08T05:37:15Z")

</div>

Wait! I got it!

Instead of keeping an object to store the binding in, I can store the  
Proc object’s binding itself:

class Web  
def initialize  
@pages = {}  
@binding = nil  
end

def page(regexp, &action)  
@pages[/^#{regexp}$/] = action  
end

def render(template)  
print CGI.new.header(‘text/html’)  
ERB.new(File.read(template)).run(@binding)  
end

def run  
for regexp, action in @pages  
if ENV[‘REQUEST\_URI’] =~ regexp  
@binding = action.binding  
action[\*$~.to\_a[1…-1]]  
return  
end  
end  
end  
end

As you can see, once I found the matching page, I store its action’s  
binding (by calling Proc#binding) into an instance variable (@binding),  
right before I actually call the action. Then, when running the ERB  
template, I use @binding for template’s binding.

Thanks for your help, anyway!

Chris
