diff --git a/lib/sinatra/url_for.rb b/lib/sinatra/url_for.rb index 3dfa1dd..b32631b 100644 --- a/lib/sinatra/url_for.rb +++ b/lib/sinatra/url_for.rb @@ -10,10 +10,32 @@ module UrlForHelper # url_for "/" # Returns "/myapp/" # url_for "/foo" # Returns "/myapp/foo" # url_for "/foo", :full # Returns "http://example.com/myapp/foo" + # + # You can also pass in a hash of options, which will be appended to the + # URL as escaped parameters, like so: + # + # url_for "/", :x => "y" # Returns "/myapp/?x=y" + # url_for "/foo", :x => "M&Ms" # Returns "/myapp/foo?x=M%26Ms" + # + # You can also specify the mode: + # + # url_for "/foo", :full, :x => "y" # Returns "http://example.com/myapp/foo?x=y" + # #-- # See README.rdoc for a list of some of the people who helped me clean # up earlier versions of this code. - def url_for url_fragment, mode=:path_only + def url_for url_fragment, mode=nil, options = nil + if mode.nil? + mode = :path_only + end + + mode = mode.to_sym unless mode.is_a? Symbol + optstring = nil + + if options.is_a? Hash + optstring = '?' + options.map { |k,v| "#{k}=#{URI.escape(v, /[^#{URI::PATTERN::UNRESERVED}]/)}" }.join('&') + end + case mode when :path_only base = request.script_name @@ -27,9 +49,9 @@ def url_for url_fragment, mode=:path_only end base = "#{scheme}://#{request.host}#{port}#{request.script_name}" else - raise TypeError, "Unknown url_for mode #{mode}" + raise TypeError, "Unknown url_for mode #{mode.inspect}" end - "#{base}#{url_fragment}" + "#{base}#{url_fragment}#{optstring}" end end diff --git a/spec/url_for_spec.rb b/spec/url_for_spec.rb index d34788a..5f1e8e2 100644 --- a/spec/url_for_spec.rb +++ b/spec/url_for_spec.rb @@ -3,13 +3,14 @@ require 'sinatra/url_for' require 'rack/test' +# HTML exception pages in tests isn't useful; raise them to rspec so it can +# tell us about them +disable :show_exceptions +enable :raise_errors + get "/" do content_type "text/plain" - <<"EOD" -#{url_for("/")} -#{url_for("/foo")} -#{url_for("/foo", :full)} -EOD + url_for params[:url], params[:mode], params[:options] end describe Sinatra::UrlForHelper do @@ -19,13 +20,52 @@ def app Sinatra::Application end - it "should return absolute paths and full URLs" do - get "/" + it "should handle the root URL" do + get "/", :url => "/" + + last_response.should be_ok + last_response.body.should == "/" + end + + it "should handle sub-URLs" do + get "/", :url => "/foo" + + last_response.should be_ok + last_response.body.should == "/foo" + end + + it "should provide full paths if asked" do + get "/", :url => "/foo", :mode => :full + + last_response.should be_ok + last_response.body.should == "http://example.org/foo" + end + + it "should accept options" do + get "/", :url => "/foo", :options => { :x => "y" } + + last_response.should be_ok + last_response.body.should == "/foo?x=y" + end + + it "should accept multiple options" do + get "/", :url => "/foo", :options => { :x => "y", :bar => "wombat" } + + last_response.should be_ok + last_response.body.should == "/foo?x=y&bar=wombat" + end + + it "should escape option values" do + get "/", :url => "/foo", :options => { :x => "M&Ms", :amount => "15%", :equals => "=" } + + last_response.should be_ok + last_response.body.should == "/foo?x=M%26Ms&amount=15%25&equals=%3D" + end + + it "should even escape URLs" do + get "/", :url => "/foo", :options => { :return_to => "http://example.com/bar?x=y" } + last_response.should be_ok - last_response.body.should == <