1

I have a controller with the following methods

 def create
    @todo = Event.new(event_params)
    ...
 end

 def event_params
   form_params = [:title, :type, :owner_id, :note, :occurred, :due]
   form_params[:due].strftime!("%Y-%m-%d")
   params.permit(form_params)
 end

I found that when I post a date (:due or :occurred) to the create method with a format of mm/dd/yyyy instead of dd/mm/yyyy I'm getting an argument out of range for a date such as 10/30/2014. So I'm trying to convert the date values in the parameters before it calls Event.new

The line - form_params[:due].strftime!("%Y-%m-%d") - is not working and after quite a bit of googling, I can't seem to figure out the right way to modify the value of a parameter. The error I get currently for that line is "no implicit conversion of Symbol into Integer". How can I do this? Thanks.

3 Answers 3

2

Well, let's discuss each line of your code to make it clear where the mistake is.

form_params = [:title, :type, :owner_id, :note, :occurred, :due]

First, you declare an array of symbols and assign that array to form_params variable. It's just a simple plain array, not an associative one, so you can access its values using indexes only, e.g. form_params[0] would return :title, but form_params[:due] is a runtime error as :due is not an integer index. Actually, the purpose of event_params method should be as simple as stating which parameters you allow to be mass-assigned to your objects, so it should look like

params.permit([:title, :type, :owner_id, :note, :occurred, :due])

Getting further, your aim is to edit parameters which came from client-side. As of various dates formats parsing, please refer to this topic.


Disclaimer: It could be much more elegant to force users to pass due date in conventional %Y-%m-%d format. Please give a look to rails dates helper methods.


That parameters reside in params hash, so you could rewrite appropriate key:

def event_params
  params["due"] = Date.strptime(params["due"], "%m/%d/%Y").to_s
  # here we've converted date format to standard %Y-%m-%d
  params.permit([:title, :type, :owner_id, :note, :occurred, :due])
end

This should do two things:

  1. Replace user's due with appropriate date format
  2. Allow enumerated keys to be mass-assigned for your object

And the rest of your code should work fine with this correction:

def create
  @todo = Event.new(event_params)
  ...

However, beware of side effects:

  1. Using event_params restricts you from getting actual %m/%d/%Y user input now.
  2. Malefactor would be able to pass some unparsable string instead of %m/%d/%Y formatted date and it would end in runtime error.
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you for the explanation. However when I leave the event_params method to not change the value and instead do it after Event.new as you've shown, I again get the argument out of range error on that line so it never gets to the next line to change the value. If I do it with strftime in the event_params method, I get the error I mentioned above - undefined method `strftime!' for "10/29/2014":String. Is there a usual way to have your controller accept dates in a mm/dd/yyyy format without having to jump through these kind of hoops?
@geoffswartz I edited answer to include reference to date conversions topic, please check it out.
Thanks again. Based on the article, it "sounds" like params["due"] = Date.parse(params["due"]) should work. But I get an invalid date error and I've confirmed I'm passing in a string of "10/30/2014". Am I misunderstanding how it is to work? Again, thanks for your help!
@geoffswartz Date.parse accepts date in %d/%m/%Y format, but you pass %m/%d/%Y. strptime should work for this case. I've updated my answer a little bit again, please check code snippets once again.
When I try params["due"] = Date.strptime(params["due"], "%m/%d/%Y").to_s I don't get an error but then when I logger.info params["due"] it writes out 10/30/2014 to the console instead of 30/10/2014 So it's not actually changing the value of the parameter.
|
1

You are assigning form_params an array of symbols, and you can't reference an element of an array by symbol, thus your error. You probably want that to look like this:

form_params = params.permit(:title, :type, :owner_id, :note, :occurred, :due)

which will give you the hash you are looking for.

Edit: at first I didn't check the date manipulation you were attempting. You would need to use Date.strptime to convert your string to a date, but you need to know the date format. Unfortunately, you cannot accurately guess that type of thing. If you know the date will be mm/dd/yyyy, you could convert formats like this:

params[:due] = Date.strptime(params[:due], '%m/%d/%Y').strftime("%Y-%m-%d")

2 Comments

ok, thanks, that gets me further. so on to the next problem. how do I convert it to proper date format? when I use what I have above I get this error - undefined method `strftime!' for "10/29/2014":String
Ahh...strftime is a Date function, not a String function. I edited my answer to show how to do that kind of conversion.
0

Super late to the game but you may consider using this gem: github.com/launchpadlab/decanter

It allows you to easily define how incoming params should be parsed.

More on it here: https://medium.com/launchpad-lab/the-missing-step-in-rails-controllers-82aaa9172165

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.