How to accept multiple URL parameters in Rails with the same key? - ruby-on-rails

In Rails, when you want to send a list of values through a single array parameter, you usually do so by suffixing the URL parameter key with []. For example, the query string ?foo[]=1&foo[]=2&foo[]=3 is parsed as
params = { "foo" => [ 1, 2, 3 ] }
But for ?foo=1&foo=2&foo=3, only the last argument shows up in the params hash:
params = { "foo" => 3 }
Everything's fine while you can control the URL format; you can just use the [] syntax. But what if the URL is constructed by a remote system that you cannot influence, and that insists on the second format? How can one get the arguments unpacked properly?

Using the hint from #maxcal I came up with this solution (to avoid adding yet more gems to this completely bloated app that I'm working on):
current_query_string = URI(request.url).query
foo_values = URI::decode_www_form(current_query_string).
select { |pair| pair[0] == "foo" }.
collect { |pair| pair[1] }

Related

Why key of hash is not parsed

I'm working with hash like this, the first key is a hash
hash = { { a: 'a', b: 'b' } => { c: { d: 'd', e: 'e' } } }
when I convert it to json, I get this:
data_json = hash.to_json
# => "{\"{:a=\\u003e\\\"a\\\", :b=\\u003e\\\"b\\\"}\":{\"c\":{\"d\":\"d\",\"e\":\"e\"}}}"
But when I parse the data, the first key is not parsed:
JSON.parse data_json
# => {"{:a=>\"a\", :b=>\"b\"}"=>{"c"=>{"d"=>"d", "e"=>"e"}}}
Why JSON.parse acts like that? and How can I fix it ?
In your original data structure, you have a Hash containing a single key-value pair. However, the key for this pair is itself a hash. This is not allowed in JSON (as only string keys are allowed in JSON), resulting in Ruby's JSON library to trying to do something sensible here: it inspects the key and uses the resulting String as the key in the created JSON object.
Unfortunately, this operation is not reversible when parsing the JSON object again. To solve this, You should try to adapt your source data structure to match what is allowed in JSON, i.e. to only use String keys in your hashes.

Pass array of ids to custom get in Restangular

I'm trying to pass an array of ids to my server using Restangular
var params = { profile_ids: _.map(fof, 'id') }
Restangular.all('mutual_friends').customGET('', params)
however, this results in the following query string being passed the my rails server
Started GET "/mutual_friends?profile_ids=231&profile_ids=2&profile_ids=254
how can I send the ids as an array? so the query string looks like:
/mutual_friends?profile_ids=[231, 2, 354]

Print out only headers in Rails request

I know how to access a header in Rails
request.headers["HEADER_NAME"]
However, I want to get all headers passed by a browser. I see that I can enumerate it
request.headers.each { |header| ... }
However, this will spit out both headers and other environment variables. Is there a way to get only headers?
Update 1
My problem isn't interation. My problem is distinguising between environment variables and headers. Both of them will be reported while interation using each or keys.
Solution
By convention, headers do not usually contain dots. Nginx even rejected requests with dots in headers by default. So I think it's quite a safe assumption to go with.
On contrary, all rails environment garbage is namespaced e.g. action_dispatch.show_exceptions, rack.input etc.
These two facts conveniently suggest a way to distinguish external headers from internal variables:
request.headers.env.reject { |key| key.to_s.include?('.') }
Works neat.
Benchmarking a bit
Note, that include?('.') implementation works about 4 times faster than matching =~ /\./
Benchmark.measure { 500000.times { hsh.reject { |key| key.to_s =~ /\./ } } }
=> real=2.09
Benchmark.measure { 500000.times { hsh.reject { |key| key.to_s.include?('.') } } }
=> real=0.58
Hope that helps.
By using
request.headers.each { |key, value| }
This is iterating your requested header with (key+value), but if you want specific values you have to use key name like, HTTP_KEYNAME because whenever HTTP request come it will append HTTP to keys and be sure about uppercase because it is case sensitive.
for example:
if we have passed auth_token as header request parameter and want to access, we can use this.
request.headers["HTTP_AUTH_TOKEN"]
You can try this to get only headers list from request
request.headers.first(50).to_h.keys
It will convert request.headers object into array and then to hash to get list of all keys in request to be used as
request.headers["keyname"]
It might be not much efficient but I think it can do the job.
Hope this helps.
not sure if this is any helpful but I ended up using this brute force approach
request.env.select {|k,v|
k.match("^HTTP.*|^CONTENT.*|^REMOTE.*|^REQUEST.*|^AUTHORIZATION.*|^SCRIPT.*|^SERVER.*")
}
You might be probably looking for :
request.env
This will basically create a Ruby Hash of the whole request object.
For more details, check this question:
How do I see the whole HTTP request in Rails
If you just want headers:
request.headers.to_h.select { |k,v|
['HTTP','CONTENT','AUTHORIZATION'].any? { |s| k.to_s.starts_with? s }
}
If you want everything that's not an env var:
request.headers.to_h.select { |k,v|
['HTTP','CONTENT','REMOTE','REQUEST','AUTHORIZATION','SCRIPT','SERVER'].any? { |s|
k.to_s.starts_with? s
}
}
You should be able to do
request.headers.each { |key, value| }
In general when iterating over a hash ruby looks at the arity of your block and gives you either a pair (key + value) or separate variables. (The hash in this case is an object internal to the headers object)

Breeze.js Passthrough Predicate Odata Url

I am attempting to create an odata url with multiple breeze.js passthrough predicates using documentation from the folowing link: http://www.getbreezenow.com/documentation/query-using-json.
However the generated url looks nothing like an odata url eg:
var query = breeze.EntityQuery.from('User').using(this.manager).where("{ { 'userName': { '=': '123456' } } }");
var url = query._toUri(this.manager);
url is "User?$filter=%7B%20%7B%20'userName'%3A%20%7B%20'%3D'%3A%20'123456'%20%7D%20%7D%20%7D&$orderby=UserName" rather than "User?$filter=(UserName eq '123456')&$orderby=UserName".
I don't think you want a passthru query because this just passes your where clause thru intact without any processing. This is what happens when you quote the entire where clause.
If you want your query converted to 'odata' syntax then try the following:
var query = breeze.EntityQuery.from('Customers').using(em)
.where({ 'userName': { '==': '123456' } });
Note that the 'where' argument is NOT in quotes ( it is a standard javascript object), and the operator is '==', not '=';
or even simpler
var query = breeze.EntityQuery.from('Customers').using(em)
.where( { userName: '123456' });
Further info:
There are two forms of urls that can be generated from any breeze query. An OData form and a JSON form. If you want OData, (the default) then you either do nothing because it is the default or you can tell breeze explicitly with:
breeze.core.config.initializeAdapterInstance("uriBuilder", "odata");
If you want the json form, you would use
breeze.core.config.initializeAdapterInstance("uriBuilder", "json");
It also possible that you added a line to use the 'json' uriBuilder. Just omit this line if you want OData urls. You can still construct the query via the json syntax, but the URL will be output using OData syntax.
The Json form ( or uri) is useful for non OData servers.

form serialize problem

I have a form. I am trying to validate it through AJAX GET requests.
So i am trying to send the field values in the GET request data.
$('#uxMyForm').serialize();
the problem it is returning something undecipherable. I have used serialize before. This is totally bizzare.
the return value of serialize is
actionsign_upcontrollersitedataauthenticity_token=oRKIDOlPRqfnRehedcRRD7WXt6%2FQ0zLeQqwIahJZJfE%3D&customer%5BuxName%5D=&customer%5BuxEmail%5D=&customer%5BuxResidentialPhone%5D=&customer%5BuxMobilePhone%5D=&customer%5BuxDateOfBirth%5D=&customer%5BuxAddress%5D=&customer%5BuxResidentialStatus%5D=
i have no idea how to use this.
Thanks
update:
My question is how do i process such a request? like this?
puts params[:data][:customer][:uxName]
my GET request trigger looks like this
$.get('/site/sign_up',{data : $('#uxMyForm').serialize() }, function(data){
alert(data);
});
The above jquery lines generate the request.. on the action method i do the following
render :text => params
when i observe what is sent in the GET,in firebug PARAMS
**data** authenticity_token=oRKIDOlPRqfnRehedcRRD7WXt6%2FQ0zLeQqwIahJZJfE%3D&direct_customer%5BuxName%5D=&direct_customer%5BuxEmail%5D=&direct_customer%5BuxResidentialPhone%5D=&direct_customer%5BuxMobilePhone%5D=&direct_customer%5BuxDateOfBirth%5D=&direct_customer%5BuxAddress%5D=&direct_customer%5BuxResidentialStatus%5D=
the return value that i print in alert has
actionsign_upcontrollersitedataauthenticity_token=oRKIDOlPRqfnRehedcRRD7WXt6%2FQ0zLeQqwIahJZJfE%3D&direct_customer%5BuxName%5D=&direct_customer%5BuxEmail%5D=&direct_customer%5BuxResidentialPhone%5D=&direct_customer%5BuxMobilePhone%5D=&direct_customer%5BuxDateOfBirth%5D=&direct_customer%5BuxAddress%5D=&direct_customer%5BuxResidentialStatus%5D=
How does the form itself look. I have no experience with Ruby on rails - and if it builds the form in a new exciting way - but it looks as if there's only two form elements: authenticity_token and customer - where customer is an array of items. This is the data you posted, but I urldecoded it and put in some linebreaks:
authenticity_token=oRKIDOlPRqfnRehedcRRD7WXt6/Q0zLeQqwIahJZJfE=
&customer[uxName]=
&customer[uxEmail]=
&customer[uxResidentialPhone]=
&customer[uxMobilePhone]=
&customer[uxDateOfBirth]=
&customer[uxAddress]=
&customer[uxResidentialStatus]=
What you could do is to serialize the form to an array and clean it up before sending it using jQuery ajax request. I did something similar once when I had to serialize .net runat-server form elements:
var serializedData = $(form).serializeArray();
for( i=0; i < serializedData.length; i++)
{
// For each item in the array I get the input-field name after the last $ character
var name = serializedData[i].name;
var value = serializedData[i].value;
if( name.indexOf('$') != -1)
name = name.substr( name.lastIndexOf('$')+1 );
serializedData[i].name = name;
}
var ajaxPostData = $.param(serializedData);
So instad of blabla$ABCPlaceHolder$tbxEmail I got tbxEmail in the array. You could do the same to get uxName, uxEmail etc instead of the customer array.
Note then again, however, due to my inexperience with ruby that this may not be the best solution - maybe there's a setting you can change to build the HTML form differently?
Updated:
I'm not sure how ruby works, but after a googling I found you should be able to receive your values using params:customer as an array.
params:customer should contain an array of the values
{:uxName => "", :uxEmail => "" }
I hope that tells you something on how to receive the data. Maybe (warning - wild guess!) like this?
params[:customer][:uxName]

Resources