require 'rubygems'
require 'active_support'
# generates a normalized URI object, adding the http scheme as default
def NormalizeURI(uri)
new = uri.to_s
new_uri = (new =~ /^https?\:\/\/.*/) ? URI(new) : URI("http://#{ new }")
new_uri.normalize!
# Google does not like this... strong hint that this is wrong....
# new_uri.path << "/" unless new_uri.path.last == "/"
new_uri
end
# Merge two uris without loosing query params
# Add more query params if provided.
#
# Options:
#
# uri: A string or URI object
# options: A Hash with query params, or
# another URI or string, or
# another URI or string plus a Hash of query params
#
def JoinURI(source, *options)
uri = NormalizeURI(source)
return uri if options.empty?
query_params_1 = uri.query
case options.length
when 1
first = options.first
if String === first || URI === first
other_uri = URI(first.to_s)
query_params_2 = other_uri.query
elsif Hash === first
query_params_2 = first.to_query
else
raise ArgumentError, "supply a Hash of query params or another URI or String"
end
when 2
other_uri = URI(options.first.to_s)
query_params_2 = other_uri.query
query_params_3 = options.last.to_query
else
raise ArgumentError, "please check the documentation and supply good params"
end
# Oh my god, ruby URIs suck so badly.... URI#merge does not work very intuitively!
if other_uri && !other_uri.path.blank?
uri.path << "/" unless uri.path.last == "/"
other_path = other_uri.path
other_path = other_path.slice(1..-1) if other_path.first == "/"
uri.path << other_path
end
uri.query = [query_params_1, query_params_2, query_params_3].reject(&:blank?).join("&")
NormalizeURI(uri)
end
if __FILE__ == $0
require 'spec'
describe "NormalizeURI" do
it "should add scheme and final / to an uri" do
NormalizeURI("www.yahoo.com?something=true").to_s.should == "http://www.yahoo.com/?something=true"
end
end
describe "JoinedURI" do
it "should handle google's case" do
uri = JoinURI(
"http://www.google.com/m8/feeds",
'/contacts/default/full',
"max-results" => 999999, "hd" => "default", "alt" => "json"
)
uri.to_s.should =~ /^http\:\/\/www\.google\.com\/m8\/feeds\/contacts\/default\/full\\?.*/
end
it "should raise ArgumentError when called with something not a Hash, String or URI" do
lambda do
JoinURI("http://localhost:3000", Object.new)
end.should raise_error(ArgumentError)
lambda do
JoinURI("http://localhost:3000", "/path")
JoinURI("http://localhost:3000", URI("/path"))
JoinURI("http://localhost:3000", Hash.new)
end.should_not raise_error(ArgumentError)
end
it "should raise ArgumentError when called with more than 3 params" do
lambda do
JoinURI("http://localhost:3000", 2, 3, 4)
end.should raise_error(ArgumentError)
end
it "should return an new uri equal to the provided if called with a single String" do
url = "http://test.com/"
JoinURI(url).to_s.should == url
end
it "should return an new uri equal to the provided if called with a single URI" do
uri = URI("http://test.com/")
JoinURI(uri).should == uri
end
it "should append params to an uri" do
JoinURI("http://localhost:3000/a", :x => "y").to_s.should == "http://localhost:3000/a?x=y"
JoinURI("http://localhost:3000?b=c" , :x => "y").to_s.should == "http://localhost:3000/?b=c&x=y"
JoinURI("http://localhost:3000/?b=c&j=k", :x => "y").to_s.should == "http://localhost:3000/?b=c&j=k&x=y"
JoinURI("http://localhost:3000/d?e=f", :x => "y").to_s.should == "http://localhost:3000/d?e=f&x=y"
JoinURI("http://localhost:3000/g/?h=i", :x => "y").to_s.should == "http://localhost:3000/g/?h=i&x=y"
JoinURI("yahoo.com", :a => "uno", :b => "dos").to_s.should == "http://yahoo.com/?a=uno&b=dos"
end
describe "should append two uris preserving the query params of each one" do
one = URI("www.yahoo.com?uno=dos")
two = URI("/peteco/carabal?tres=4&cinco=seis")
result = "http://www.yahoo.com/peteco/carabal?uno=dos&tres=4&cinco=seis"
it "with two uris" do
JoinURI(one, two).to_s.should == result
end
it "with two string" do
JoinURI(one.to_s, two.to_s).to_s.should == result
end
it "with a uri and a string" do
JoinURI(one, two.to_s).to_s.should == result
end
it "with a string and an uri" do
JoinURI(one.to_s, two).to_s.should == result
end
it "with two uris and additional query params" do
JoinURI(one, two, :more => :params).to_s.should == "#{ result }&more=params"
end
it "with two string and additional query params" do
JoinURI(one.to_s, two.to_s, :more => :params).to_s.should == "#{ result }&more=params"
end
it "with a uri and a string and additional query params" do
JoinURI(one, two.to_s, :more => :params).to_s.should == "#{ result }&more=params"
end
it "with a string and an uri and additional query params" do
JoinURI(one.to_s, two, :more => :params).to_s.should == "#{ result }&more=params"
end
end
it "should work with https schemes" do
uri = JoinURI("https://something.com?param=a", "/other/?param=b", :param => "c")
uri.to_s.should == "https://something.com/other/?param=a¶m=b¶m=c"
end
end
end