Full URL with url_for in Rails - ruby-on-rails

How can I get a full url in rails?
url_for #book is returning only a path like /book/1 and not www.domain.com/book/1
Thanks (and sorry if the answer is obvious. Im learning rails!)

According to the docs, this shouldn't happen. The option you're looking for is :only_path and it's false by default. What happens if you set it to false explicitly?
url_for(#book, :only_path => false)
While you can use url_for you should prefer Ryan's method when you can - book_url(#book) for a full url or book_path(#book) for the path.

If it's a RESTful resource you'll be able to use this:
book_url(#book)

In Rails 4, url_for only takes one argument, so you need to pass an array with an explicit hash inside for the only_path option.
Good:
url_for([#post, #comment, {only_path: true}])
Bad:
url_for(#post, #comment, {only_path: true})
url_for([#post, #comment], {only_path: true})
From the source, url_for with an Array input just calls:
polymorphic_url([#post, #comment], {only_path: true})
as shown in #moose's answer.
As noted by #lime, only_path is generally not needed for polymorphic_url since you distinguish that with the _url _path suffixes.

It seems that this would work:
url_for(#book)
But it does not. The url_for method accepts only one argument, which can be either a string, an instance of a model, or a hash of options. This is rather unfortunate, as it would seem like you may need to link to #book and add options like :only_path or :host as well.
One way around it is to use polymorphic_url, which would render the correct absolute url even though your model is (likely) not actually polymorphic:
polymorphic_url(#book, :host => "domain.com")
Perhaps the best route would be to use a named route, which is set up automatically for you when declaring resources in your routes or using the :as option:
# in routes.rb:
resources :books
# or
get "books/:id" => "books#show", as: :book
# in your view:
book_path(#book, :host => "domain.com")

Use the :host option. For example, you can use:
url_for(#book, :host => "domain.com")
Note: with Rails 3 and above, use polymorphic_url instead of url_for.

In Rails 5, if you want the full url for the current controller/action (== current page), just use:
url_for(only_path: false)
Long answer:
In Rails 5, url_for in a view is ActionView::RoutingUrlFor#url_for. If you look at it's source code (https://api.rubyonrails.org/classes/ActionView/RoutingUrlFor.html#method-i-url_for), you'll see if you pass a Hash (keyword parameters are cast into a Hash by Ruby), it actually calls super, thus invoking the method of same name in it's ancestor.
ActionView::RoutingUrlFor.ancestors reveals that it's first ancestor is ActionDispatch::Routing::UrlFor.
Checking it's source code (https://api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html#method-i-url_for), you'll read this:
Missing routes keys may be filled in from the current request's
parameters (e.g. :controller, :action, :id and any other parameters
that are placed in the path).
This is very nice, since it will build automatically a URL for you for the current page (or path, if you just invoke url_for without the only_path: false). It will also intelligently ignore the query string params; if you need to merge those, you can use url_for(request.params.merge({arbitrary_argument:'value'})).

Related

How to tell whether your controller action was given parameters?

I am trying to determine whether my controller action was called with parameters or not, without hardcoding which parameters can be added on.
So I want to distinguish between
/my_controller
and
/my_controller?q=1
I know that I could look inside the params hash, and check whether it ONLY contains :controller and :action keys. This seems ugly to me, is there a smarter way of doing this check?
There is one direct solution:
request.env["QUERY_STRING"] # => "q=1"
Or with Ruby 1.9.2:
request.env.QUERY_STRING # => "q=1"
For GET request you can use request.query_parameters method. There is also request.request_parameters for POST requests.
Results for request.query_parameters.inspect are:
for '/my_controller' => '{}'
for '/my_controller?q=1' => {"q"=>"1"}

url_for generates a url with current path inserted

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")

renaming routes (map, link_to, to_param) in rails

I'm having a little issue...I setup a rails application that is to serve a german website. To make use of Rails' internal pluralization features, I kept all my models in english (e.g. the model "JobDescription").
Now, if I call "http://mysite.com/job_descriptions/", I get all my job_descriptions....so far, so good. Because I didn't want the english term "job_descriptions" in my url, I put the following into my routes.rb
map.german_term '/german_term', :controller => 'job_descriptions', :action => 'index'
map.german_term '/german_term/:id', :controller => 'job_descriptions', :action => 'show'
If I call "http://mysite.com/german_term/" or "http://mysite.com/german_term/283" I get all my job_descriptions, which is fine.
However, to make the URL more SEO friendly, I'd like to exchange the id for a more userfriendly slug in the URL. Thus, I put the following in my job_description.rb:
def to_param
"#{id}-#{name.gsub(/[^a-z0-9]+/i, '-')}"
end
which, whenever I use "job_description_path" in any link_to method, renders my URLs out to something like "http://mysite/job_descriptions/13-my-job-description-title".
However, and this is where I'm stuck, I'd like to get "http://mysite/german_term/13-my-job-description-title". I already tried to exchange the "job_description_path" with "german_term_path" in the link_to code, but that only generates "http://mysite/german_term/13". Obviously, to_param isn't called.
One workaround I found is to build the link with:
<%= link_to job_description.name, german_term_path(job_description.to_param) %>
But that's rather tedious to change all the link_to calls in my code. What I want is to replace "job_description" by "german_term" whenever it occurs in a URL.
Any thoughts?!?
Regards,
Sebastian
I think you're going to need to use the restful route helpers to get what you want.
In that case, it wouldn't take much re-factoring (assuming you've mapped JobDescriptions as a resource). Leave your to_param as is and change your JobDescriptions route to something like the following:
map.resources :job_descriptions, :as => 'german_term'
Hope this helps!
Rails only utilizes the
def to_params
end
URL builder when you are using a restful route/link helper. The only way I am aware of is to do it similar to how you did, unless you are willing to just scrap your english language links and do it all in German. In that case, just get rid of the named route lines and change the to_params to use the correct name field from the database. At that point, the REST routes should behave correctly.

Param name and value (independantly) as part of Rails Route

DocumentsController#common_query can handle multiple different request styles.
i.e. all docs in batch 4 or all docs tagged "happy"
I want a single route to make em pretty, so:
/documents/common_query?batch=4
/documents/common_query?tag=happy
become:
/documents/batch/4
/documents/tag/happy
So the end result is that #common_query is called but part of the url was used as the param name and part as it's value.
The second option, with two routes, is almost certainly the better way to go, because it will only match the kinds of URLs that you want to support, while the first option will also "match" URLs like /documents/foo/bar, which will likely cause your #common_query method to, at best, return a RecordNotFound (404) response. At worst, if you're not ready to not see any of your expected params, you'll get a 500 error instead...
Of course, if you start having a lot of variations, you end up with a lot of routes. And if you need to use them in combination, e.g., /documents/batch/4/tag/happy, then you'll need to use a wildcard route, and do the parameter processing in your controller. This might look something like:
map.connect 'documents/*specs', :controller => "documents_controller", :action => "common_query"
The various elements of the URL will be available your controller as params[:specs]. You might turn that into a find like so:
#items = Item.find(:all, :conditions => Hash[params[:specs]])
That Hash[] technique converts the one dimensional array of options into a key-value hash, which might be useful even if you're not feeding it directly to a find().
As a single route:
ActionController::Routing::Routes.draw do |map|
map.connect "documents/:type/:id", :controller => "documents_controller",
:action => "common_query"
end
Then params[:type] will either be "batch" or "tag", and params[:id] either "4" or "happy". You will have to make sure that other actions for the DocumentsController come before this in the routes because this will match any url that looks like "documents/*/*".
But why does it have to be a single route? You could use two routes like this:
map.with_options(:controller => "documents_controller",
:action => "common_query") do |c|
c.connect "documents/batch/:page", :type => "batch"
c.connect "documents/tag/:tag", :type => "tag"
end
which will have the same effect, but is more specific, so you wouldn't have to worry about the priority order of the routes.

Identify GET and POST parameters in Ruby on Rails

What is the simplest way to identify and separate GET and POST parameters from a controller in Ruby on Rails, which will be equivalent to $_GET and $_POST variables in PHP?
You can use the request.get? and request.post? methods to distinguish between HTTP Gets and Posts.
See http://api.rubyonrails.org/classes/ActionDispatch/Request.html
I don't know of any convenience methods in Rails for this, but you can access the querystring directly to parse out parameters that are set there. Something like the following:
request.query_string.split(/&/).inject({}) do |hash, setting|
key, val = setting.split(/=/)
hash[key.to_sym] = val
hash
end
You can do it using:
request.POST
and
request.GET
There are three very-lightly-documented hash accessors on the request object for this:
request.query_parameters - sent as part of the query string, i.e. after a ?
request.path_parameters - decoded from the URL via routing, i.e. controller, action, id
request.request_parameters - All params, including above as well as any sent as part of the POST body
You can use Hash#reject to get to the POST-only params as needed.
Source: http://guides.rubyonrails.org/v2.3.8/action_controller_overview.html section 9.1.1
I looked in an old Rails 1.2.6 app and these accessors existed back then as well.
There is a difference between GET and POST params. A POST HTTP request can still have GET params.
GET parameters are URL query parameters.
POST parameters are parameters in the body of the HTTP request.
you can access these separately from the request.GET and request.POST hashes.
request.get? will return boolean true if it is GET method,
request.post? will return boolean true if it is POST method,
If you want to check the type of request in order to prevent doing anything when the wrong method is used, be aware that you can also specify it in your routes.rb file:
map.connect '/posts/:post_id', :controller => 'posts', :action => 'update', :conditions => {:method => :post}
or
map.resources :posts, :conditions => {:method => :post}
Your PostsController's update method will now only be called when you effectively had a post. Check out the doc for resources.
I think what you want to do isn't very "Rails", if you know what I mean. Your GET requests should be idempotent - you should be able to issue the same GET request many times and get the same result each time.
You don't need to know that level of detail in the controller. Your routes and forms will cause appropriate items to be added to the params hash. Then in the controller you just access say params[:foo] to get the foo parameter and do whatever you need to with it.
The mapping between GET and POST (and PUT and DELETE) and controller actions is set up in config/routes.rb in most modern Rails code.
I think what Jesse Reiss is talking about is a situation where in your routes.rb file you have
post 'ctrllr/:a/:b' => 'ctrllr#an_action'
and you POST to "/ctrllr/foo/bar?a=not_foo" POST values {'a' => 'still_not_foo'}, you will have three different values of 'a': 'foo', 'not_foo', and 'still_not_foo'
'params' in the controller will have 'a' set to 'foo'. To find 'a' set to 'not_foo' and 'still_not_foo', you need to examine request.GET and request.POST
I wrote a gem which distinguishes between these different key=>value pairs at https://github.com/pdxrod/routesfordummies.
if request.query_parameters().to_a.empty?

Resources