14

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
2
  • 1
    For those looking for a solution and not the why: use build on the collection to create a model without saving it: api.rubyonrails.org/classes/ActiveRecord/Associations/… Commented Sep 9, 2014 at 17:07
  • I had the opposite problem. Using build in model specs, then adding items to a collection using << did not work. The answers to this question explain why not. Commented Nov 1, 2015 at 18:03

2 Answers 2

22

<< is an alias for push

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

which calls 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.

Sign up to request clarification or add additional context in comments.

Comments

4

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.

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.