The following logic:
Iterate over the URLs array and If the HTTP request with the given URL does not timeout, continue. If the URL times out, remove it from the array and continue with the next one until the array is exhausted.
Has been implemented in the following way:
@urls.delete_if do |url|
begin
doc = perform_request(some_params)
break
rescue TimeoutError
Rails.logger.warn("URL #{url} times out, will be removed from list")
true
end
end
Anyone for a cleaner solution? Basically, I want to iterate over an array and remove elements from it if there is a timeout, but if the URL works loop should end and app should continue processing.
-
\$\begingroup\$ Specifically, hate the break in there - maybe someone can propose a cleaner solution. \$\endgroup\$Emo– Emo2013年01月04日 15:23:43 +00:00Commented Jan 4, 2013 at 15:23
-
\$\begingroup\$ Btw "true" in the rescue can be removed, not needed. \$\endgroup\$Emo– Emo2013年01月04日 15:33:12 +00:00Commented Jan 4, 2013 at 15:33
-
\$\begingroup\$ if not needed just edit the question! I have a doubt, why do you want to modify inplace @urls? wouldn't do a functional solution? \$\endgroup\$tokland– tokland2013年01月04日 17:57:56 +00:00Commented Jan 4, 2013 at 17:57
3 Answers 3
note : your break
is useless, because if the block returns a falsey value, the url is kept.
Maybe it can just be a bit more expressive ?
def timeouts?( url )
perform_request( url ) && false
rescue TimeoutError
true
end
@urls.delete_if {|url| timeouts?( url ) }
You could even make timeouts?
a method on your @urls
objects, for extra readability :
@urls.delete_if( &:timeouts? )
if you want to keep @tokland's functionnal approach, just use :
@kept_urls = @urls.reject{|url| timeouts?( url ) }
however, it would not be fully "functionnal", because timeouts
may not return the same results with the same arguments, depending on the execution environment.
-
\$\begingroup\$ Maybe calling the function
timesout?
instead oftimeouts?
would semantically be better. \$\endgroup\$jox– jox2020年01月26日 10:03:01 +00:00Commented Jan 26, 2020 at 10:03
You may use drop_while to get rid of iterative operator break
:
@urls = @urls.drop_while do |url|
begin
doc = perform_request(some_params)
false
rescue TimeoutError
Rails.logger.warn("URL #{url} times out, will be removed from list")
true
end
end
-
\$\begingroup\$ the problem is
doc
won't be visible outside the block's scope. \$\endgroup\$tokland– tokland2013年01月04日 21:23:57 +00:00Commented Jan 4, 2013 at 21:23
I'd take a functional approach (no in-place updates):
url, doc = @urls.map_detect do |url|
begin
[url, perform_request(some_params)]
rescue TimeoutError
Rails.logger.warn("URL #{url} timed out")
false
end
end
This snippet uses the custom abstraction Enumerable#map_detect (see also Facets)
You can then use url
to update @urls
, but it's better not to mix the algorithm (even if it's so simple) with the updates required by the app.