Rails keeps changing my string "?" into "%3F" - ruby-on-rails

Basicaly I just want to insert this + "?direction=desc" in helper method.
But once it parses it comes out like this..
/organizations/search?order_by=contactable%3Fdirection%3Ddesc
Anyone know a way around this?
My Helper Method:
def search_sort(name, sort_by, order = 'asc')
link_to(name, url_for(:overwrite_params => { :order_by => sort_by + "?direction=desc" :page => nil }), :class => 'selected save_pushstate')
...
I know what you're thinking. Just add :order into it. The problem being is that I 'm using an AJAX history saver from #175 of railscasts.
$(".save_pushstate").live("click", function() {
$.setFragment({"order_by" : $.queryString($(this).attr('href')).order_by});
//$.setFragment({"direction" : $.queryString($(this).attr('href')).direction});
return false;
});
And it rewrites my url to just one "fragment". I can't have two! So I decided that if I can just add the direction param in the href hard-coded, it could deal with this whole mess.

Try:
+ "?direction=desc".html_safe
Edit:
Since you're using rails 2.3.5, try this:
def search_sort(name, sort_by, order = 'asc')
link_to(name, url_for(:overwrite_params => { :order_by => sort_by + "?direction=desc" :page => nil }, :escape => false), :class => 'selected save_pushstate')
...
Note the ":escape => false" in url_for.
Edit2:
After reading this:
http://www.ruby-forum.com/topic/80381
Specifically this excerpt:
I think this is where the confusion is
arising. There are two different kinds
of escaping going on.
It sounds like you're talking about
the URL encoding that uses '%xx' to
represent special characters.
However, the html_escape function does
something completely different. It
takes a string and turns '&' into
'&' and '<' into '<', etc., so
that it can go into HTML without being
interpreted as literal '&'s and '<'s.
Escaping special characters in URLs
using the '%xx' scheme is mandatory,
otherwise they are not valid URLs.
I've realized that the 'escaping' that you see happening is url encoding, and it shouldn't affect your query/sorting, etc. You can test it out by taking the encoded url and typing it into your browser.
:escape => false disable html escaping, which means dangerous characters get converted to display codes, such as '&' into '&' and '<' into '<', etc.,
And the "?" in your append should be "&":
+ "&direction=desc"
Hope this helps. =)

Related

Encoding parameters with multiple duplicate keys

I seem to be having the same problem as this chap here
I want to encode some parameters (for the import.io api). Effectively:
params = {
:input => "webpage/url:http://www.example.com",
:input => "keywords:some+keywords"
}
But that won't work, so I think this is the right approach:
params = { :input => ["webpage/url:http://www.example.com", "keywords:some+keywords"] }
and I want it to output
params.to_query
=> "input=webpage%2Furl%3Ahttp%3A%2F%2Fwww.example.com%2Fsome-id&input=keywords%3Asome%2Bkeywords"
unfortunately, I get
"input%5B%5D=webpage%2Furl%3Ahttp%3A%2F%2Fwww.example.com%2Fsome-id&input%5B%5D=keywords%3Asome%2Bkeywords"
It's adding [] after the input, which I believe is standard behaviour. How can I stop it doing it?
To clarify, what is the ruby or 'rails way' of dealing with url parameters that require duplicate keys?
Ran into a similar issue, there's a helpful post here Ruby Hash with duplicate keys? but briefly
params = {}.compare_by_identity
params['input'] = "webpage/url:http://www.example.com"
params['input'.dup] = "keywords:some+keywords"
then
params.to_query
returns
"input=keywords%3Asome%2Bkeywords&input=webpage%2Furl%3Ahttp%3A%2F%2Fwww.example.com"
Some characters in a url have special importance to the processing of the url: they are reserved, like keywords in a programming language. See Which characters make a URL invalid?
If you try to use these as the name or value of a parameter, it will break the uri and you'll get hard to predict results like you're seeing.
The answer is to URI escape the string, which will replace special characters with their encoded version. Rails will automatically unescape them when it gets the the request, so you don't need to worry about it.
You can escape them manually, but the best way, if you have them as a hash already, is to call .to_param on the hash.
params = { :input => ["webpage/url:http://www.example.com", "keywords:some+keywords"] }
=> {:input=>["webpage/url:http://www.example.com", "keywords:some+keywords"]}
params.to_param
=> "input%5B%5D=webpage%2Furl%3Ahttp%3A%2F%2Fwww.example.com&input%5B%5D=keywords%3Asome%2Bkeywords"

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

How do I encode the & symbol for batch requests?

I have a Facebook batch request that looks like this:
https://graph.facebook.com/?access_token=ACCESS_TOKEN&batch=[{"method": "GET", "relative_url": "search?q=EMAIL#ADDRESS.COM&type=user"}]
Sending this across the wire returns:
{"error"=>0, "error_description"=>"batch parameter must be a JSON array"}
If I remove the &type=user, it works fine (sends back an empty data array). I am absolutely certain that Facebook is not parsing the & character correctly. I read online somewhere that I could try encoding the & symbol to %26, however using that replacement seems to instead do a query for "EMAIL#ADDRESS.COM%26type=user". If you reverse the order of the parameters, you will see what I mean.
Any ideas how I can get the batch request parser on Facebook to recognize the & symbol without filing a bug report that will never be fixed?
EDIT:
I am using URI.encode. Here is the exact code:
queries = email_array.map { |email| { :method => "GET", :relative_url => "search?q=#{email}&type=user" } }
route = "https://graph.facebook.com/?access_token=#{token}&batch=#{URI.encode(queries.to_json)}"
res = HTTParty.post(route)
After actually playing around with this some more, I managed to reproduce the same behavior, even with a careful check and double-check that I was following the api specs correctly. This looks like a bug in facebook's batch method -- it doesn't understand ampersands in param values correctly.
Don't use a string literal to construct the json. Use to_json, like below. (Also, as an aside, don't use {} notation across more than one line, use do/end).
queries = []
email_array.each do |email|
queries << {:method => 'GET', :relative_url => "search?q=#{email}&type=user"}
end
route = "https://graph.facebook.com/?access_token=#{token}&batch=#{URI.encode(queries.to_json)}"
res = HTTParty.post(route)
Also, you can use Array#map to simply the code, like this:
queries = email_array.map { |email| {:method => 'GET', :relative_url => "search?q=#{email}&type=user"} }
route = "https://graph.facebook.com/?access_token=#{token}&batch=#{URI.encode(queries.to_json)}"
res = HTTParty.post(route)
EDIT: below is my original answer before the question was edited, for reference.
Try properly url encoding the whole parameter:
https://graph.facebook.com/?access_token=ACCESS_TOKEN&batch=[%7B%22method%22:%20%22GET%22,%20%22relative_url%22:%20%22search?q=EMAIL#ADDRESS.COM&type=user%22%7D]
In practice, you'd use URI.encode from the uri library to do this. Example:
irb(main):001:0> require 'uri'
=> true
irb(main):002:0> URI.encode('[{"method": "GET", "relative_url": "search?q=EMAIL#ADDRESS.COM&type=user"}]')
=> "[%7B%22method%22:%20%22GET%22,%20%22relative_url%22:%20%22search?q=EMAIL#ADDRESS.COM&type=user%22%7D]"
Or even better, use to_json to create your json string in the first place. Example:
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'json'
=> true
irb(main):003:0> require 'uri'
=> true
irb(main):004:0> URI.encode([{:method => 'GET', :relative_url => 'search?q=EMAIL#ADDRESS.COM&type=user'}].to_json)
=> "[%7B%22method%22:%22GET%22,%22relative_url%22:%22search?q=EMAIL#ADDRESS.COM&type=user%22%7D]"
If this helps anyone, when my AdSet batch update failed because there was an "&" in one of the interests name:
{u'id': u'6003531450398', u'name': u'Dolce & Gabbana'}
I learned that the name can be anything, and as long as the id is correct, FB will populate the name itself.

Generating a form that passes multiple parameters

I have the following in my routes.rb
map.diff 'posts/:id/diff/:from/:to', :controller => "posts",
:action => "diff", :conditions => { :method => :get }
And I have the following in my view file.
- form_tag(diff_path(), :method => :get) do
= text_field_tag(:from, "")
= text_field_tag(:to, "")
= hidden_field_tag(:id, #post.id)
= submit_tag("Submit")
I would like to generate a form that submits something like "http://example.com/posts/3/diff/13/18", but it fails. How can I make such a form?
I need to pass parameters for diff_path(), but I don't know how to do that. I don't even know if this is possible with form_tag.
The error message:
diff_url failed to generate from {:action=>"diff", :controller=>"posts"} - you may have ambiguous routes, or you may need to supply additional parameters for this route. content_url has the following required parameters: ["posts", :id, "diff", :from, :to] - are they all satisfied?
To my knowledge, what you're trying to accomplish can't be done with just an HTML form. The reason being that the form will only know how to submit traditionally via GET and POST. It has no knowledge of the structure of the URL.
You get that error message because the :id, :from and :to parameters are required to form the both you want, so when you call diff_path() it freaks out.
Personally, I would advise you not to use the URL structure you're planning on - however I'm not totally clear on what this page is going to display. Regardless, if the :from and :to parameters are algorithmic input and not resource identifiers, I would avoid this structure.
That said, if you do want to implement this, you would either have to implement a redirect from rails or javascript.
Rails method
#some_controller.rb
redirect_to diff_path(:from => params[:from], :to => params[:to])
Javascript (jQuery) method
$(function() {
$("form#your_form_id_here").submit(function() {
window.location = "posts/" + this.id + "/diff/" + this.from + "/" + this.to;
return false;
});
});
I don't think that will work by specifying the url with that format in the form. Rails tries to create the URL to submit to when you render the page, not when you submit the form. So it is not seeing those parameters when you render the page. I would recommend creating another route
map.diff_form 'posts/:id/diff', :controller => :post, :action => :diff, :conditions => {:method => :post}
You could use the two routes side by side if you need to keep the current url format.
Why are you trying to do this in the first place? I really can't think of a good reason why this would be necessary.
Your "from" and "to" variables should probably just be normal URL parameters - i.e. /posts/:id/diff?from=X&to=Y, so that you can then retrieve them in your controller with params[:from] and params[:to]
There may be a way to make Rails work this way, but you're going to have issues with it, since Rails is emphatically not meant to work this way.
I think you can use like this
diff_path(#id,#from, #to)
where #id, #from, #to are instance variables. If dont, you can specify a hash also like
diff_path(:id=>#id,:from=>#from, :to=>#to)

javascript in a rails :url parameter

I want to create a drop_receiving_element where the :url contains javascript. Currently I just created a helper function and hard-coded the element in like so:
def create_classification_droppable(droppable_id, classification)
"<script type='text/javascript'>
//<![CDATA[
Droppables.add('#{droppable_id}', {accept:'lead', onDrop:function(element){new Ajax.Request('/leads/' + (element.id.split('_').last()) + '.js', {asynchronous:true, evalScripts:true, method:'put', parameters:'lead[classification]=#{classification}&authenticity_token=' + encodeURIComponent('#{form_authenticity_token}')})}})
//]]>
</script>"
end
That's pretty hackish and ugly, though. Ideally I'd like to do something like:
drop_receiving_element('some_class',
:accept => 'some_other_class',
:url => formatted_whatever_path( SOMETHING_BASED_ON_WHATEVER_IS_BEING_DROPPED ) )
Or
formatted_whatever_path(:id => "some_javascript", :js)
Unfortunately, this doesn't seem possible because the url is escaped further down the call chain (in url_for, I believe). What are the alternatives?
What version of rails are you using? If you're on 2.3.8 then your "escaping" may simply be because of the "string addition bug":
https://rails.lighthouseapp.com/projects/8994/tickets/4695-string-added-to-rails_helpers-gets-html-escaped
You can fix it either by finding ingenious hacks around it (eg encasing your string in "#{my_string}" to re-evalute it) or by downgrading to 2.3.5.

Resources