Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails push into array saves object

I have an interesting problem. I'm using Ruby 1.9.2 and Rails 3.1.3.

I have 2 models, for simplification let's say customers and stores. Stores have many customers, and a customer belongs to a store. I'm trying to collect all customers for a store, and create a place for a few more that I can populate with values later. Instead, customer.save is called when I don't expect it.

store = Store.find(1)
customers_array = store.customers
random_array = Array.new
customers_count = customers_array.count + 1 

(customers_count..2).each do |i|
  customer = Customer.new
  c.id = "#{i}000000000000"
  random_array << customer # this line doesn't call customer.save
  customers_array << customer # this line calls customer.save when store has customers
end

For some reason when the customer is pushed into the array, customer.save is called. It doesn't happen if you push to an array is a plain array and not a relation.

I found a workaround, but I'm still wondering why that happens. The workaround:

store = Store.find(1)
initial_customers_array = store.customers
additional_customers_array = Array.new
customers_count = initial_customers_array.count + 1 

(customers_count..2).each do |i|
  customer = Customer.new
  c.id = "#{i}000000000000"
  additional_customers_array << customer 
end
customers_array = initial_customers_array + additional_customers_array
like image 587
user1457495 Avatar asked Jun 15 '12 00:06

user1457495


2 Answers

Another way around this would be to change your second line (of your original code) to:

customers_array = store.customers.to_a

That casts the active record association to a real array object, so the << method will be the normal Array#push method.

like image 62
Jeff Gran Avatar answered Nov 16 '22 03:11

Jeff Gran


<< is an alias for push

  • http://apidock.com/rails/ActiveRecord/Associations/CollectionProxy/%3C%3C
  • http://apidock.com/rails/ActiveRecord/Associations/CollectionProxy/push

which in the ActiveRecord::Associations::CollectionProxy calls concat

  • http://apidock.com/rails/ActiveRecord/Associations/CollectionAssociation/concat (view the source of the method)
  • https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/collection_proxy.rb#L283

which calls concat_records

  • http://apidock.com/rails/v3.2.3/ActiveRecord/Associations/CollectionAssociation/concat_records

where you can see the insert taking place.

So, with an existing record (persisted into the database), running << or .push will insert records into the collection, persisting them to the database if necessary. Calling << on an Array, not the record collection, as you're doing in

random_array << customer

calls Ruby's << Array method, not the AR equivalent (as you found, no save takes place in this case).

Edit: To be clear, the workaround you found is more or less how I typically handle the situation you're dealing with; my answer focuses more on why << has this behavior.

like image 33
deefour Avatar answered Nov 16 '22 04:11

deefour