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.

geoff swartz
  • 5,437
  • 11
  • 51
  • 75

3 Answers3

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.
Community
  • 1
  • 1
twonegatives
  • 3,400
  • 19
  • 30
  • 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? – geoff swartz Oct 09 '14 at 18:35
  • @geoffswartz I edited answer to include reference to [date conversions topic](http://stackoverflow.com/questions/17563048/parse-a-date-in-rails), please check it out. – twonegatives Oct 09 '14 at 18:45
  • 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! – geoff swartz Oct 09 '14 at 18:53
  • @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. – twonegatives Oct 09 '14 at 18:56
  • 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. – geoff swartz Oct 09 '14 at 19:14
  • @geoffswartz does it allow you to create object of `Event` with desired date or not? what database state do you have when your `create` method is done? – twonegatives Oct 09 '14 at 19:19
  • No it won't create the event. Since it's not converting the date from 10/30/2014 to 30/10/2014 when it gets to the Event.new line it bombs with arguments out of range error. – geoff swartz Oct 09 '14 at 19:20
  • Ok, I don't know what happened, but after restarting the rails server now it's working. Thanks so much! – geoff swartz Oct 09 '14 at 19:24
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")
Jim
  • 475
  • 2
  • 7
  • 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 – geoff swartz Oct 09 '14 at 18:15
  • Ahh...strftime is a Date function, not a String function. I edited my answer to show how to do that kind of conversion. – Jim Oct 09 '14 at 18:39
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

Ryan Francis
  • 635
  • 6
  • 7