Undefined method permit for string - ruby-on-rails

I'm currently designing and implementing a RoR API.
I'm facing an issue concerning strong parameters.
I'm expecting to receive the following attributes:
{ analysis_data: { barcode: "some_string" } }
To ensure that, I simply use the require/permit combination:
params.require(:analysis_data).permit(:barcode)
This works well if analysis data is nil or an hash.
However, if the client submit a request where analysis_data is a string instead of an hash, the request returns an error 500.
For example:
{ analysis_data: "some_string" }
Produces the following 500 error:
NoMethodError (undefined method `permit' for "barcode":String):
Is there any clean workaround?

Try like this
params.fetch(:analysis_data, {}).permit(:barcode)

Not sure if you want to permit the string and hash or prevent the string without throwing the error. If it's the former you can use params.require(:analysis_data).permit! to allow any value as long as it's inside of analysis_data.

What about:
params[:analysis_data].is_a?(ActionController::Parameters) ? params.fetch(:analysis_data, {}).permit(:barcode) : {}
or more readable like this:
return {} unless params[:analysis_data].is_a? ActionController::Parameters
params.fetch(:analysis_data, {}).permit(:barcode)
That way, .permit will only be called if a ActionController::Parameters is submitted, and otherwise return an empty hash.

Related

Extracting hash key that may or may not be an array

I'm making an API call that returns XML (JSON also available) and the response body will show errors if any. There may be only one error or multiple errors. When the XML (or JSON) is parsed into a hash, the key that holds the errors will be an array when multiple errors are present but will be just a standard key when only one error is present. This makes parsing difficult as I can't seem to come up with one line of code that would fit both cases
The call to the API returns this when one error
<?xml version=\"1.0\" encoding=\"utf-8\"?><response><version>1.0</version><code>6</code><message>Data validation failed</message><errors><error><parameter>rptFilterValue1</parameter><message>Parameter is too small</message></error></errors></response>
And this when multiple errors
<?xml version=\"1.0\" encoding=\"utf-8\"?><response><version>1.0</version><code>6</code><message>Data validation failed</message><errors><error><parameter>rptFilterValue1</parameter><message>Parameter is too small</message></error><error><parameter>rptFilterValue2</parameter><message>Missing required parameter</message></error></errors></response>
I use the following to convert the XML to a Hash
Hash.from_xml(response.body).deep_symbolize_keys
This returns the following hash.
When there is only one error, the hash looks like this
{:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}}}}
When there are 2 errors, the hash looks like this
{:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>[{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}, {:parameter=>"rptFilterValue2", :message=>"Missing required parameter"}]}}}
When I first tested the API response, I had multiple errors so the way I went about getting the error message was like this
data = Hash.from_xml(response.body).deep_symbolize_keys
if data[:response].has_key?(:errors)
errors = data[:response][:errors][:error].map{|x| "#{x.values[0]} #{x.values[1]}"}
However when there is only one error, the code errors out with undefined method 'values' for parameter
The only actual workaround I found was to test the class of the error key. When Array I use one method for extracting and when Hash I use another method.
if data[:response][:errors][:error].class == Array
errors = data[:response][:errors][:error].map{|x| "#{x.values[0]} #{x.values[1]}"}
else
errors = data[:response][:errors][:error].map{|x| "#{x[1]}"}
end
But I just hate hate hate it. There has to be a way to extract xml/json data from a key that may or may not be an array. The solution may be in the conversion from xml to hash rather than when parsing the actual hash. I couldn't find anything online.
I'll appreciate any help or tip.
If you're using Rails, Array#wrap is available if you can do your .dig first:
single = {:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}}}}
Array.wrap(single.dig(:response, :errors, :error))
This returns an Array of size 1:
[
{
:message => "Parameter is too small",
:parameter => "rptFilterValue1"
}
]
For multiples:
multiple = {:response=>{:version=>"1.0", :code=>"6", :message=>"Data validation failed", :errors=>{:error=>[{:parameter=>"rptFilterValue1", :message=>"Parameter is too small"}, {:parameter=>"rptFilterValue2", :message=>"Missing required parameter"}]}}}
Array.wrap(multiple.dig(:response, :errors, :error))
This returns an Array of size 2:
[
{
:message => "Parameter is too small",
:parameter => "rptFilterValue1"
},
{
:message => "Missing required parameter",
:parameter => "rptFilterValue2"
}
]
You can parse XML with Nokogiri and xpath, which returns array even if selector points out single element
errors = Nokogiri::XML(xml_response).xpath('//error')
errors.map { |e| e.children.each_with_object({}) { |x, h| h[x.name] = x.content } }
Your API response with single error gives
=> [{"parameter"=>"rptFilterValue1", "message"=>"Parameter is too small"}]
and API result with multiple errors
=> [{"parameter"=>"rptFilterValue1", "message"=>"Parameter is too small"}, {"parameter"=>"rptFilterValue2", "message"=>"Missing required parameter"}]
If there's no error elements you'll get an empty array.

routing help in Txroutes library

I need to route the following to a function :
http://www.example.com/docs/?key1=value1&key2=value2
And the route code for it is
dispatcher.connect(name='xyz', route='/docs/{item}', controller=c, action='docs')
so, can you help me with the “route” part as in what should come there so for an incoming request the action gets called.
As in how to relate route='/docs/{item}' and /docs/?key1=value1&key2=value2
HTTP GET request (/docs/) followed by a query string (?key1=value1&key2=value2). You'd use Twisted's normal facilities for accessing the query string.
def docs(self, request):
return '<html><body>Got %s args</body></html>' % request.args
to retrieve from the array use request.args['key1'][0] to get 'value1'.

Jersey POST operation with PathParam and JSON Object

By design, GET operation should be used only for read Only operation. Howeevre,i am looking for a plausible way of implementaion of following.Implement a POST operation that can be called as it is mentioned below
POST /my-store/order/D : where D is the day the customer place an order
Request: POST /my-store/order/14
{
"customer" : "XYZ",
"order" : {
"item1" : 2
}
}
I tried implementing using below function
#Path("/D")
#POST
#Consumes({ MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public Response submitOrder(#PathParam("D") int elapsedDays, #Context UriInfo uriInfo, Order orderInfo){
..........
}
But the above implementation does not seem to working. When I try to test the implementation using MyEclipse REST explorer ,it does not offer option to pass in Order object but allow 'D' parameter only. However, if #PathParam and #Path is removed then it works perfectly fine i.e. allows to consume JSON Order object.
But,the requirement is to pass the days as Path parameter and Order object as JSON input in POST request.
Looking for suggestion on implementation approach and design approach.
Thanks in advance
For one thing, your path should be configured like this:
#Path("/{D}")
I assume your extended ellipses means you have some method parameter that represents the deserialization of your order.

Best way to handle errors in Grails domain class validation

We have an API command like "student/create" to create a new student object. The code looks like this:
def student = new Student(firstName: firstName, lastName: lastName, email: email)
if (! student.validate()) {
response.error = "UNKNOWN_ERROR" // in case we can't find anything better
student.errors.allErrors.each { error ->
// set response.error to an appropriate value
println error
}
} else {
student.save()
}
Our goal is to give a reasonable error message like "EMAIL_DUPLICATE" or "FIRSTNAME_LENGTH" on a validation fail, so we want to test the errors we get against a set of expected errors so we can respond like that.
Here's what we get from that println:
Field error in object 'com.example.Student' on field 'email': rejected value [student#example.com]; codes [com.example.Student.email.unique.error.com.example.Student.email,com.example.Student.email.unique.error.email,com.example.Student.email.unique.error.java.lang.String,com.example.Student.email.unique.error,student.email.unique.error.com.example.Student.email,student.email.unique.error.email,student.email.unique.error.java.lang.String,student.email.unique.error,com.example.Student.email.unique.com.example.Student.email,com.example.Student.email.unique.email,com.example.Student.email.unique.java.lang.String,com.example.Student.email.unique,student.email.unique.com.example.Student.email,student.email.unique.email,student.email.unique.java.lang.String,student.email.unique,unique.com.example.Student.email,unique.email,unique.java.lang.String,unique]; arguments [email,class com.example.Student,student#example.com.org]; default message [Property [{0}] of class [{1}] with value [{2}] must be unique]
How can I figure out that this means the email is already used in the database so that I can tell the API user that?
(to be clear, I want to give a computer-readable message like "EMAIL_DUPLICATE" instead of something like "Property email of class Student with value student#example.com must be unique")
Not sure it would work in more situations than just this one, but does:
println "${error.objectName}_${error.codes[-1]}".toUpperCase()
Get you anywhere near?

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