Bizarrely unable to access elements in an ActionController::Parameters - ruby-on-rails

Making an extension to an already-written rails project. In an erb file I have:
<%= form_for #uploaded_planners[0], :url => method_path, :html => {
:multipart => true } do |form| %>
<%= form.file_field :pic %>
<%=form.submit 'Upload'%>
<% end %>
In the appropriate controller I have:
def create
p params
print "\n"
testing = params[:uploaded_planner]
print testing.class
print "\n"
print testing
print "\n"
print testing['#original_filename']
print testing[:original_filename]
print "\n"
end
and:
def plan
#uploaded_planners = UploadedPlanner.all #there are two i inserted via rails console..
end
Output of those prints are
{"utf8"=>"✓", "_method"=>"patch", "authenticity_token"=>"UxgdziS9PwY/SAaOMj4upXzSTf5brOT4R9x+paMNNlxehYwY34OT7hvdGJXwvK/cffPIjYOwdf1h+MndJh6LBg==", "uploaded_planner"=>{"pic"=>#<ActionDispatch::Http::UploadedFile:0x007f9b9b3bc5e8 #tempfile=#<Tempfile:/var/folders/hc/67y08p7s3ws6rsjl5rlxk3f00000gn/T/RackMultipart20170801-25372-godqfg.xlsx>, #original_filename="AF_CSX_DISTRIBUTE_NUMBERS.xlsx", #content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", #headers="Content-Disposition: form-data; name=\"uploaded_planner[pic]\"; filename=\"AF_CSX_DISTRIBUTE_NUMBERS.xlsx\"\r\nContent-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\r\n">}, "commit"=>"Upload", "controller"=>"viacom", "action"=>"create"}
ActionController::Parameters
{"pic"=>#<ActionDispatch::Http::UploadedFile:0x007f9b9d529190 #tempfile=#<Tempfile:/var/folders/hc/67y08p7s3ws6rsjl5rlxk3f00000gn/T/RackMultipart20170801-25372-baavaj.xlsx>, #original_filename="AF_CSX_DISTRIBUTE_NUMBERS.xlsx", #content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", #headers="Content-Disposition: form-data; name=\"uploaded_planner[pic]\"; filename=\"AF_CSX_DISTRIBUTE_NUMBERS.xlsx\"\r\nContent-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\r\n">}
The bottom two print statements are nil. My statements do not dig into the double-nested hash and retrieve the values I am trying to get. I tried every combination syntax-wise that I know of to try to get to those values out of the hash, but to no avail - every time I get nothing. I know the 'create' method in my controller is being invoked - it's how I am getting this print output - it's just I cannot get any value in the hash of "uploaded_planner" (aka can't index into the testing variable). I cannot figure out how to reference those members by any means, which sucks, as those are the members I really want to get to.
Here is the official ActionController doc:
http://api.rubyonrails.org/v4.2/classes/ActionController/Parameters.html#method-i-extract-21
It alleges that I can do:
params[:key]
And I can... the first time. But after that, the object that I have is still an ActionController::Parameters (as shown by my .class print statement) yet I cannot do that very same indexing to get members of the second hash. It is extremely frustrating because I see the data that I need in the print output, but cannot get that data and manipulate it (without going super jank and reading print output somehow... not a good way to do things). Any ideas on this?

params[:uploaded_planner][:pic].original_filename

Related

Saving json response in create

I want to save a json response but it is saving the title instead of the value. Also having issues with how the params are coming through with slashes instead of as a hash
I have the following response from form submit:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"2134", "shipping_rate"=>{..."shipping_rate"=>"{\"serviceName\"=>\"USPS First Class Mail - Package\", \"serviceCode\"=>\"usps_first_class_mail\", \"shipmentCost\"=>2.66, \"otherCost\"=>0.0}"}, "commit"=>"Create Shipping rate"}
form:
...
<% #rates.each do |rate| %>
<%= form.radio_button :shipping_rate, rate.as_json %> <%= rate %><br />
<% end %>
...
I am under the impression using as_json removes the "/" so it comes through as a hash parameters.
I also tried using #rates.as_json.each with same results
Create method in controller:
#shipping_rate.service_code = params["shipping_rate"]["shipping_rate"]["serviceCode"]
How the service_code saves is as "serviceCode" and not "usps_first_class_mail".
How can I:
Have the params come through as a hash, without the /'s
Save the value of params["shipping_rate"]["shipping_rate"]["serviceCode"] instead of the title
rate.as_json is correctly returning a hash, but when you pass data to form.radio_button it get's converted into something html friendly. Basically, form.radio_button calls .to_s (or some similar method that probably also escapes html) on that hash, which is why you're getting the hash as a string.
As a rule, individual HTML form elements should not contain hash data, and especially not ruby formatted hash data. Keep in mind that once the page is rendered in a browser, it knows nothing about the backend, let alone how to run ruby code.
You should take a step back and think of this RESTfully. If your users are meant to select a rate, and you have a number of rate records in your db, you really only need them to send back the id of the rate they would like. Without seeing your controller it's hard to tell exactly what else would need to change, but your radio buttons should probably look more like:
<%= form.radio_button :rate_id, rate.id %>
Your controller's create method would then be responsible for setting the user's selected rate, possibly requiring you to look up the rate again.

Rails params, params.merge error

In order to keep params filter and filter_type through a ajax form submit, I pass them in as hidden fields in my article form which as a result gives me this params hash:
{"utf8"=>"✓", "_method"=>"patch", "article"=>{"filter"=>"xxx", "filter_type"=>"xxxx", ....actual fields of the model that got updated...}, "commit"=>"Update Article", "controller"=>"articles", "action"=>"update", "id"=>"xxx"}
Which means I can access them through params[:article]["filter"].
When I, in my controller's update method, call params.merge(filter: params[:article]["filter"]) nothing gets appended. When I try params = params.merge(filter: params[:article]["filter"]) I get this error NoMethodError (undefined method '[]' for nil:NilClass):
and here comes the weird part: When I do #foo = params.merge(filter: params[:article]["filter"]) I also don't get anything added until I actually output #foo in the view. As soon as I have the <%= #foo %> in my view, the params get properly merged. Can somebody explain why that is?
I think it is better for you to not pass them as articles child. Instead of using f.hidden_field :filter you could use hidden_field_tag :filter, so you will receive the params like: { filter: "filter", article: {}}. This way there is no need to merge.

What is a right way to update a boolean field using link_to. Getting undefined method `model_name' for TrueClass:Class

I have the below link_to in a loop in my rails app
<%= link_to 'Up', Product.find(n.id).update_attribute(:opinion, true)%>
But I am unable to update it. Because it gives me the error
undefined method `model_name' for TrueClass:Class
So, I am wondering in this case what is the right way to update a boolean field?
You are not updating the opinion after click the link, you are updating it when the page render.
Lets say your page has only the following code:
<%= link_to 'Up', Product.find(n.id).update_attribute(:opinion, true)%>
What actually happens when you hit that page is that the code wrapped on <%= %> is executed and used to generate an string, that will become your HTML. So while simplifying your expression, in one step of the execution you will have the following code:
<%= link_to 'Up', true %>
And this will generate the HTML
Up
Which is not what you intend of.
Solution
Create an action on your product controller that receives the product id, updates the product and redirects back. So, something like this:
On your route.rb
resource :products do
member do
post 'update_opinion'
end
end
On your products_controller.rb:
def update_opinion
Product.find(params[:id]).update_attribute(:opinion, true)
redirect_to :back
end
On your view:
<%= link_to 'up', update_opinion_project_path(n) %>
Your code does something very different from what you seem to be pursuing.
The helper method link_to accepts two parameters: the link text and the link target, which can be:
a string, like: 'http://stackoverflow.com';
a hash referring all the single components of an internal URL, as defined by routes;
an object, or an array of objects.
As second parameter, you are passing an expression:
Product.find(n.id).update_attribute(:opinion, true)
which is evaluated the first time the code runs, that is, during rendering.
The expression tries to update the attribute immediately and then returns a boolean that says if the operation succeeded or not: true if it successfully updated the attribute, false otherwise.
In your case, it appears to be successfully: it returns true.
Now, true is neighther a string nor an hash, so it is treated as an object, whose class is TrueClass. So it treats it as a model, but not being a model, it does not define the models methods; thus the error.
What you want to do is to put the code in a controller, and put the URL relative to the right controller action as second parameter of link_to.

Rails - escaping backslashes in params causing havoc?

I have a form that passes the same parameters as the form before it:
<%= form_tag({:controller => "workouts", :action => "random"}) do %>
<%= hidden_field_tag :workout, params[:workout] %>
<%= hidden_field_tag :time, params[:time] %>
<%= submit_tag "Get Another", :class => 'btn' %>
The first form works fine, the second form to "get another" gives me the error can't convert Symbol into Integer for this line:
#equipment_ids = params[:workout][:equipment_ids].collect{|s| s.to_i}
The params of the first and second form being passed are:
{"utf8"=>"✓",
"authenticity_token"=>"qj/Q/YWvLKK3A3paAnEom4oTFtq44daX6dvEb8qmgtE=",
"workout"=>{"equipment_ids"=>["",
"508",
"518"]},
"time"=>"25",
"commit"=>"Get Workout"}
{"utf8"=>"✓",
"authenticity_token"=>"qj/Q/YWvLKK3A3paAnEom4oTFtq44daX6dvEb8qmgtE=",
"workout"=>"{\"equipment_ids\"=>[\"\",
\"508\",
\"518\"]}",
"time"=>"25",
"commit"=>"Get Another"}
The only difference is the escaping backslashes. I'm not sure why these would cause a problem?
Changed the hidden field tag to:
<%= hidden_field_tag "workout[equipment_ids][]", params[:workout][:equipment_ids] %>
I just went into the same problem when trying to manually submit a form with a custom POST request. The problem seems to be that net/http post_form method can only handle a single hash where all the values are Strings. If you have hash inside hash (like in the form that scaffold generates), it treats the inner hash as a String, and adds the nasty backslashes that, as you just saw cause havoc :)
The solution for me was to use the lower level "post" method, and to manually encode the hash. Define this module:
module HashToHttpParams
def to_http_params
map do |k, v|
if v.is_a?(Hash)
v.map do |kk, vv|
"#{k}[#{kk}]=#{vv}"
end.join('&')
else
"#{k}=#{v}"
end
end.join('&')
end
end
And then add it to the Hash class in your code:
Hash.send(:include, HashToHttpParams)
Finally encode your params hash before using it. In my code this looks like:
Net::HTTP.start("localhost",3000) do |http|
http.post("/tests", params.to_http_params)
end
Don't know if there's a better solution, but this worked for me.
Source: http://porras.lacoctelera.net/post/2007/10/08/enviando-formularios-con-parametros-compuestos-con-ruby-y-net#c4300080
As Hallucynogenyc pointed out, this is caused by the .post_form (docs) method only wanting a non-nested hash that is strings. I had this same problem, and solved it by switching to use the .post method.
require "net/http"
uri = URI('http://www.yoururl.com')
http = Net::HTTP.new(uri.host)
response = http.post(uri.path, params.to_query)
The .to_query method is also useful for converting hashes.
Another way to solve it is to not use the rails form method to create your params. If you just use straight html, for some reason the .post_form method likes it better.
Email <input name="student_email" type="email" autofocus="true">

each_element not behaving as expected

When I'm in irb and I do something like this:
node_list.each_element { |e| puts e.text }
It works and prints one line of text per element returned (plus I think it returns the xml object). However, when I head over to rails and have things moving between controllers, helpers, views and layouts it just dumps the xml object.
I should mention that for good reasons I'm using rails 1.2.3 and ruby 1.8.7.
Gratzi!
So the issue your having is that puts writes to console and not the template. Also, in ruby the a method will return by default its last object. So your method as written will loop through #child1, print each's text to the console, and then return the #child1 object at which point your erb tags of <%= %> tells it print the object (#child1 in this case)
You have two options, either you can move it out into the template:
<% tol_get_names(#child1) do |e| %> #note just <% and not <%=
<%= e.text %>
<% end %>
Or build your method so that it loops through building a string and then returns that string instead of the original object:
def tol_get_names(child)
texts = [] #empty array for all our values
child.each_element { |e|
texts << e.text #add the text string to our array
end
texts.join(", ") #build a string and seperate it with a comma
end
Several ways you could write this type of method, but this is my usual way.

Resources