Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rails - left shift "<<" operator saves record automatically

Need help understanding this code, as what to my knowledge I know "<<" append to a collection but here it saves the record correctly, how come it does without calling .save method?

#user.rb
 has_many :saved_properties, through: :property_saves, source: :property

#users_controller.rb
def update
  if @user.saved_properties << Property.find(params[:saved_property_id])
        render plain: "Property saved"
end
like image 863
Code Tree Avatar asked Jan 01 '17 09:01

Code Tree


2 Answers

In the has_many documentation it says:

Adds one or more objects to the collection by setting their foreign keys to the collection's primary key. Note that this operation instantly fires update SQL without waiting for the save or update call on the parent object, unless the parent object is a new record.

like image 97
amrdruid Avatar answered Nov 15 '22 06:11

amrdruid


Maybe looking at the source code will help you. This is my trail of searches based on the << method in activerecord:

def <<(*records)
  proxy_association.concat(records) && self
end

rails/collection_proxy.rb at 5053d5251fb8c03e666f1f8b765464ec33e3066e · rails/rails · GitHub

def concat(*records)
  records = records.flatten
  if owner.new_record?
    load_target
    concat_records(records)
  else
    transaction { concat_records(records) }
  end
end

rails/collection_association.rb at 5053d5251fb8c03e666f1f8b765464ec33e3066e · rails/rails · GitHub

def concat_records(records, should_raise = false)
  result = true

  records.each do |record|
    raise_on_type_mismatch!(record)
    add_to_target(record) do |rec|
      result &&= insert_record(rec, true, should_raise) unless owner.new_record?
    end
  end

  result && records
end

rails/collection_association.rb at 5053d5251fb8c03e666f1f8b765464ec33e3066e · rails/rails · GitHub

  def insert_record(record, validate = true, raise = false)
    set_owner_attributes(record)
    set_inverse_instance(record)

    if raise
      record.save!(validate: validate)
    else
      record.save(validate: validate)
    end
  end

https://github.com/rails/rails/blob/5053d5251fb8c03e666f1f8b765464ec33e3066e/activerecord/lib/active_record/associations/has_many_association.rb#L32

  def insert_record(record, validate = true, raise = false)
    ensure_not_nested

    if record.new_record? || record.has_changes_to_save?
      if raise
        record.save!(validate: validate)
      else
        return unless record.save(validate: validate)
      end
    end

    save_through_record(record)

    record
  end

https://github.com/rails/rails/blob/5053d5251fb8c03e666f1f8b765464ec33e3066e/activerecord/lib/active_record/associations/has_many_through_association.rb#L38

As you can see, in the end, it does call the save method.

Disclaimer: I'm not that familiar with Rails souce code, but you have interesting question.

like image 36
Amree Avatar answered Nov 15 '22 06:11

Amree