1

I have a controller action somewhat similar to this:

def reports
  puts params
  @stats = Client.stats(params)
  puts params
end

The initial params might look like this:

{ end: '2012-01-01 21:00:19' }

And in my Client model, I have this:

def self.stats(opts)
  opts[:start] = (Time.now - 30.days).to_i
  ...do some calculations..
  return stats
end

If I inspect the params object that was sent before and after the function runs, I can see it's been modified by the self.stats method.

In the example above, I'm not sending 'start' in the initial params, the method adds it for the calculations - as expected.

What I was not expecting was that the function would modify the original hash!

Can someone explain why this is happening?

--EDIT--

I forgot to say I tried to create a copy of the params and use that instead, same issue.

def reports
  a = params
  @stats = Client.stats(a)
  puts params
end

The params are still updated?!

Jenny Blunt
  • 1,576
  • 1
  • 18
  • 41
  • Related: http://stackoverflow.com/questions/22827566/ruby-parameters-by-reference-or-by-value/22827949#22827949 – nathanvda Mar 15 '16 at 10:56
  • Just adding that it is probably a good practice to never alter parameters to a method. It will confuse the caller of that method. – froderik Mar 15 '16 at 11:07
  • Why *wouldn't* it modify what you're passing in? Creating a new reference to the same object doesn't create a new object, it creates a new reference. Perhaps you want to clone or deep-clone the object? – Dave Newton Mar 15 '16 at 11:51
  • Because we have to normalise the date into a standard format which we then modify in a number of queries across different databases - mysql, mongo, a redis query, elasticsearch and big query. It's easier for us to start with a standard time and modify as we go depending on the query we're using. – Jenny Blunt Mar 15 '16 at 12:27

2 Answers2

4

That's, because your function call gets a reference to the params not a copy. If you do something like opts[:start] = (Time.now - 30.days).to_i you are editing the params object.

a = params: now both variables point to the same place in the memory. You copied the pointer only.

Google for ruby object copy or ruby deep copy or search at stackoverflow for it. At a first try you could try params.clone.

guitarman
  • 3,290
  • 1
  • 17
  • 27
  • I tried to make a copy, see edit, and that's updated too. How can I made a copy of the object then?? – Jenny Blunt Mar 15 '16 at 10:32
  • 3
    No, you didn't. `params` and `a` points to the same place in the memory. You copied the pointer only. Google for `ruby object copy` or `ruby deep copy` or search at stackoverflow for it. At a first try you could try `params.clone`. – guitarman Mar 15 '16 at 10:43
  • I was unaware of this! Great, can you add to your answer and I'll accept. – Jenny Blunt Mar 15 '16 at 11:41
1

Whenever you are updating any value of params, take a copy of params like this

a = params.clone

It will create a new element in memory

if you do like this it wont create a new element in memory it will point the same memory

a = params

Try this

Veeru
  • 348
  • 4
  • 13