Hello fellow Rubyists,
I am writing a tiny custom web framework, similar in nature to web.py
(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, 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