url_for generates a url with current path inserted - ruby-on-rails

I am generating a url in my controller accessed from the relative path "/hangouts/test", for a url on an external site (facebook). I want to use url_for and pass in params using hashes so it can escape them. The URL I want is this:
http://www.facebook.com/connect/prompt_permissions.php?api_key=6aca22e72866c7eaaedfb15be69c4b93&...
Using this, however:
url_for(:host => "www.facebook.com/connect/prompt_permissions.php?", :api_key => Facebooker.api_key, :next => test_hangouts_url, :cancel => root_url, :ext_perm => "publish_stream")
I instead get my current path of /hangouts/test thrown in there:
http://www.facebook.com/connect/prompt_permissions.php/hangouts/test?api_key=6aca22e72866c7eaaedfb15be69c4b93&...
As you can see, I don't want "/hangouts/test" to be in there - played a bit with the options in the API docs but stumped, anybody know how to use url_for without it inserting the current path? Thanks!

You shouldn't be using the hash form of url_for to generate links outside of your application.
Instead you should just be using the string version form:
url_for "http://www.facebook.com/connect/prompt_permissions.php?api_key=6aca22e72866c7eaaedfb15be69c4b93&next=#{test_hangouts_url}&cancel=#{root_url}&ext_perm=publish_stream"
url_for will use the current action controller/action/id unless the hash given to url_for contains one of those arguments. url_for will then generate a url from a route that matches the arguments given in the hash. The arguments to url_for used in the question generates a valid url, because it can match a route using the current controller/action. You will not be able to generate the url you want with the hash form of url for without matching a route. Providing a controller in the hash you give url_for should match the default routes that rails generates for you when you create an application, but that's not very DRY.
The better solution is to use a named route:
map.prompt_fb_permissions "/connect/prompt_permissions.php",
:host => "www.facebook.com", :controller => nil, :action => nil
then you can use the following in place of url_for whenever you want to generate this this url.
prompt_fb_permissions(:api_key => Facebooker.api_key, :next => test_hangouts_url,
:cancel => root_url, :ext_perm => "publish_stream")

You left out the controller parameter, which is why it's automatically adding your controller path ("/hangouts/test"), to your base host.
Try this:
url_for(:host => "www.facebook.com", :controller=> "/connect/prompt_permissions.php", :api_key => Facebooker.api_key, :next => test_hangouts_url, :cancel => root_url, :ext_perm => "publish_stream")

Related

Multilingual routes - Advanced constraints not taken into consideration when paths are generated by url_for

I have a multilingual site in rails 3.2 that has some language-specific routes that map to the same action. Like:
For mydomain.fr
match "/bonjour_monde" => 'foo#bar'
For mydomain.de
match "/hallo_welt" => 'foo#bar'
To solve this I have used an advanced constraint when declaring the routes:
Application.routes.draw do
constraints(SiteInitializer.for_country("fr")) do
match "/bonjour_monde" => 'foo#bar'
end
constraints(SiteInitializer.for_country("de")) do
match "/hallo_welt" => 'foo#bar'
end
end
Where the SiteInitializer is just a class which responds to the matches? method and determines if the request is for the correct domain. This is actually only pseudo-code just demonstrating my setup.
class SiteInitializer
def initialize(country_code)
#country_code = country_code
end
def self.for_country(country_code)
new(country_code)
end
def matches?(request)
# based on the request, decide if this route should be declared
decide_which_country_code_from_request(request) == #country_code
end
end
This works fine. When requesting mysite.fr/bonjour_monde the app dispatches correctly and the paths are only bound to their specific domain.
mysite.fr/bonjour_monde => HTTP 200
mysite.fr/hallo_welt => HTTP 404
mysite.de/bonjour_monde => HTTP 404
mysite.de/hallo_welt => HTTP 200
Now, all is fine unless you start using things like url_for(:controller => 'foo', :action => 'bar'). If you do this, the constraints are not taken into consideration. This results in that the generated path from rails (Journey class), will be arbitrary.
If I somewhere in any view use url_for, like
url_for(:controller => 'foo', :action => 'bar')
rails will choose any arbitrary declared route that matches the controller action, probably the first one declared, skipping to check any advanced constraint.
If the user visits mysite.de/hallo_welt, and the view does something like:
= url_for(:controller => 'foo', :action => 'bar', :page => '2')
The output might be
mysite.de/bonjour_monde?page=2
^
wrong language
Actually, I don't use url_for specifically in code, but some gems like kaminari (paginator) do this and that's why you might be limited in using libraries that use the standard helper methods when generating paths.
Now, I'm not so sure if the Journey class should take the request context into consideration. But how would you approach this kind of problem?

Generate the URL for a generic path with possible options in Rails

I'm creating a customisable nav menu for our site and have run into the following problem.
I need to generate a URL to any controller and action on the site and optionally pass it parameters. I was able to do the former by simply saying:
url_for(:controller => nav[:controller_name], :action => nav[:action_name])
which is great for sending you to {controller}/{action}. eg. news/articles
Throwing options in suddenly changes the game. Now I need to send you to something like:
{controller}/{action}/{category}/{slug}/{id}
eg. news/articles/world-domination/montana-max-vows-revenge/12345
the helper for the above would be something along the lines of:
news_article_path('world-domination', 'montana-max-vows-revenge', '12345')
and I haven't been able to replicate that in a vanilla url_for due to the arguments.
What I have done, and I don't really like is:
url_for(send("#{nav[:controller_name]}_#{nav[:action_name]}_path", *nav[:options]))
which generates the helper using send and then passes it a kwargs list. I'm sure there's a better way to do that surely?
You can do this cleanly if you are able to name the options (split here over lines for legibility):
url_for({
:controller => nav[:controller_name],
:action => nav[:action_name]
}.merge(nav[:options] || {}))
where
nav = {
:controller_name => 'news',
:action_name => 'articles',
:options => {
:category => 'world-domination',
:slug => 'montana-max-vows-revenge',
:id => '12345'
}
}

Rails Routing having a URL in the query string

So I need to hit a url like mydomain.com/proxies/www.msn.com in order to fulfill some ajax requests for an API
In my route I have
get "/proxies/:url" => "proxies#get"
and I in the controller I have
url_contents = Net::HTTP.get(URI.parse(params[:url]))
which if i put /proxies/www i get
undefined method `request_uri' for #<URI::Generic:0x3f89508 URL:www>
if i put /proxies/www.msn.com
I get
No route matches [GET] "/proxies/www.msn.com"
You have two separate problems here:
You're trying to treat a URL without a scheme as an HTTP URL.
Your /proxies route won't match :urls with dots and slashes the way you're expecting it to.
For the first one, you'll have to add the schema manually:
url = 'http://' + params[:url]
content = Net::HTTP.get(URI.parse(url))
For the second one, you can use a splat-route to deal with embedded slashes and :format => false to keep Rails from trying to treat .com, for example, as a format:
get '/proxies/*url' => 'proxies#get', :format => false
If you use :url, Rails will see embedded slashes as component separators and that's probably not what you want. Without the the :format => false, Rails will try to interpret .X (for any X) as a format (just like .html, .json, ...) rather than as part of the URL.
This is applicable for rails-2
The problem is with the dot(.) I guess. Ive tried something like below and it worked.
#routes.rb
map.get_proxy 'proxies/:url', :controller => "Proxies", :action => "get", :requirements => { :url => /.*/ }
#view file
#Here dot(.) are needed to replace with its encoded string %2E
<%= link_to 'Get The Site', get_proxy_url('www.msncom') %>
#Proxies Controller
#After receiving the url the %2E need to conovert to dot(.) again.
url_contents = Net::HTTP.get(URI.parse('http://'+params[:url]))
modified as stated by #mu.
match '/proxies/:url' => 'proxies#get', :as => :proxies_get

Escaping elements in a URI Ruby On Rails

I have a rails app that I am trying to do a get request with co-ordinates in...
I have a route in my routes.rb like this:
map.connect 'feeds/get/:location', :controller => "feeds", :action => "get"
I can send a string consisting of alphanumeric characters fine, but I need to send co-ordinates in a string in the URI as a get request:
51.896834,0.878906.
So, I escaped the string like so, and append it to my URI.
http://thisisnottheurl.net/feeds/get/51%2E896834%2C0%2E878906.xml
however it looks like rails automatically unescapes the string before the controller and gives me this routing error in the log:
ActionController::RoutingError (No route matches "/feeds/get/51.896834,0.878906.xml" with {:method=>:get}):
How do I stop rails escaping this string (with routes?) so that it can be read in the controller?
I looked at using the match function in routes.rb with regex, but that is rails 3 only...
The only real way I can think of doing this would be as follows, give the route a name as follows:
map.connect 'feeds/get', :controller => "feeds", :action => "get", as: 'get_feeds'
Then you would have a named route helper get_feeds_path, which you could then pass in location and a format as follows:
get_feeds_path(:location => '51.896834,0.878906', :format => 'xml')
What might be an even better idea however, is if you passed in two params, one for each of the coordinates.
get_feeds_path(:x_location => '51.896834', :y_location => '0.878906', :format => 'xml')
Then, the params hash passed to the controller should have a params[:x_location] and a params[:y_location] which you can manipulate to your liking.

Github like routes in Rails

Using github like chain routes in rails
I have URLs similar to this:
'localhost:3000/document_managers/[:module_name]'
'localhost:3000/document_managers/[:module_name]/1/2/3/.' # can be any level deep
Here is the route definition for them:
map.connect '/document_managers/:module',
:controller => "document_managers",
:action => :new_tree,
:module => ["A","B","C"]
map.connect '/docuemnt_managers/:module/*path',
:controller => "document_managers",
:action => "new_tree",
:module => ["A","B","C"]
Here is the problem:
The idea that module name value can't be anything except from the
given above array i.e("A","B","C") like at any time the URL must be something like
localhost:3000/document_managers/A/1 or
localhost:3000/document_managers/B/221/1 or
localhost:3000/document_managers/C/121/1
but that not the case even though
localhost:3000/document_managers/D/121/1 is treated as valid url
and module is set to D even though the "D" is not in listed array
above
I want the the URL localhost:3000/document_managers/A to
also redirect to same action i.e new_tree if the extra parameter isn't
provided as in the URL contain extra parameters
localhost:3000/document_managers/C/121/1 then the URL is redirected
appropriately to the desired controller and action but if the URL only
contain the path until the module name the Rails return a routes
ActionController::UnknownAction I don't know why as I have already
defined the controller and action.
In Rails 3.1, you can do this in your routes file to get what you want:
match '/document_managers/:module',
:controller => "document_managers",
:action => :new_tree,
:constraints => {:module => /[ABC]/}

Resources