path-creating iteration

2 views
Skip to first unread message

morbusg

unread,
Nov 5, 2009, 8:06:53 AM11/5/09
to sinatrarb
There is an example on the FAQ about multiple urls triggering the same
handler, but I'm not sure why doesn't something like this work:

for resource in %w(page something) do
get "/new/#{resource}" do
@object = eval("#{resource}.capitalize}.new")
haml <<-"EOF"
%form{:action => "/new/#{resource}", :method => 'POST'}
= haml(:_#{resource}_fields, :layout => false)
EOF
end
end

Both routes get created, but the first ones (page) route creates a
form for "something" rather than for "page".

Paul Walker

unread,
Nov 5, 2009, 10:17:05 AM11/5/09
to sina...@googlegroups.com
I dynamically create routes from configuration as security filters
like this:

#admin pages
config.admin_routes.each do |route|
method(route.keys.first.to_sym).call route.values.first do
if @context[:user] && @context[:user].enabled &&
@context[:user_mgmt]
@context[:authenticated] = true
else
render_redirect '/publisher'
end
pass
end
end

Christopher Schneider

unread,
Nov 5, 2009, 10:57:30 AM11/5/09
to sina...@googlegroups.com
It appears like it's a variable binding issue with the for loop. I
haven't narrowed it down too tightly, but I bet a clever combination
of .dup calls would fix it.

Or, you can use the .each version of the loop, which I've verified
works.

require 'rubygems'
require 'sinatra'

%w(page something).each do |resource|
get "/#{resource}" do
"Visited the #{resource} route"
end
end

Bruno Deferrari

unread,
Nov 5, 2009, 11:20:43 AM11/5/09
to sinatrarb
It works for creating the routes because at the time you reference the
'resource' variable it has the value you expect, but once the code
inside the route handler block is called, the value of 'resource' is
the last one set by the for loop (in this case, "something").

Normally you would want to use #each and avoid for loops because of
things like this (each creates a new binding on each iteration,
avoiding the problem).

Jesús Gabriel y Galán

unread,
Nov 5, 2009, 12:23:49 PM11/5/09
to sina...@googlegroups.com
The "for" construct doesn't create a new scope, so the "resource"
variable is the same for all iterations. The closure created by the
do..end block passed to the get method closes on just a single
variable "resource". So when later the block is executed, both blocks
have the same value of "resource", which is the one from the last
iteration. Check this:

irb(main):007:0> procs = []
=> []
irb(main):008:0> for s in %w{a b c}
irb(main):009:1> procs << lambda {puts s}
irb(main):010:1> end
=> ["a", "b", "c"]
irb(main):011:0> procs.each {|x| x[]}
c
c
c

The "each" method, on the other hand gets passed a regular block,
which creates a new local scope. In that case each iteration creates a
new variable "resource", so that each do...end block closes on a
*different* variable "resource". Check:

irb(main):022:0> procs = []
=> []
irb(main):023:0> %w{a b c}.each do |string|
irb(main):024:1* procs << lambda {puts string}
irb(main):025:1> end
=> ["a", "b", "c"]
irb(main):026:0> procs.each {|x| x[]}
a
b
c

Also note, that (at least in 1.8), if a variable with the same name as
the block parameter exists, the above doesn't hold, and the block
parameter is again the same for all iterations:

irb(main):027:0> string = "something"
=> "something"
irb(main):028:0> procs = []
=> []
irb(main):029:0> %w{a b c}.each do |string|
irb(main):030:1* procs << lambda {puts string}
irb(main):031:1> end
=> ["a", "b", "c"]
irb(main):032:0> procs.each {|x| x[]}
c
c
c

I think this has changed in 1.9, so that local variables and block
parameters are independent, but I can't test it right now.

Hope this helps,

Jesus.

morbusg

unread,
Nov 5, 2009, 3:38:51 PM11/5/09
to sinatrarb
On Nov 5, 6:20 pm, Bruno Deferrari <uti...@gmail.com> wrote:
> Normally you would want to use #each and avoid for loops because of
> things like this (each creates a new binding on each iteration,
> avoiding the problem).

I must say this is a bit unexpected behaviour from ruby.
Thank you all very much for explaining this.

Jason Rogers

unread,
Nov 5, 2009, 8:56:43 PM11/5/09
to sina...@googlegroups.com
What is unexpected? That a closure would "close" its scope or that a
for-loop iterates over a single variable?
--
Jason Rogers

"I am crucified with Christ: nevertheless I live;
yet not I, but Christ liveth in me: and the life
which I now live in the flesh I live by the faith of
the Son of God, who loved me, and gave
himself for me."
Galatians 2:20

morbusg

unread,
Nov 6, 2009, 4:06:55 AM11/6/09
to sinatrarb
That there is a functional difference between .each and for i in ...
do.

On Nov 6, 3:56 am, Jason Rogers <jacaete...@gmail.com> wrote:
> What is unexpected?

Jason Rogers

unread,
Nov 6, 2009, 10:10:29 AM11/6/09
to sina...@googlegroups.com
Oh, I guess it's not unexpected to me. They are 2 different
constructs. In Java I wouldn't expect a for-loop to behave like an
Iterator.
Reply all
Reply to author
Forward
0 new messages