Last active
August 16, 2022 08:15
-
-
Save p8/8ed895c2de3cff57bcb5ae9f256f5268 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "bundler/setup" | |
require "active_record" | |
begin | |
require "benchmark/ips" | |
rescue LoadError | |
raise LoadError, "Run install `gem install benchmark-ips`" | |
end | |
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") | |
ActiveRecord::Base.connection.create_table(:orders) { |t| } | |
ActiveRecord::Base.connection.create_table(:items) { |t| t.references :order, null: false } | |
class Order < ActiveRecord::Base | |
has_many :items | |
accepts_nested_attributes_for :items | |
end | |
class Item < ActiveRecord::Base | |
end | |
class Order2 < ActiveRecord::Base | |
self.table_name = :orders | |
has_many :items, foreign_key: :order_id | |
accepts_nested_attributes_for :items | |
def assign_nested_attributes_for_collection_association(association_name, attributes_collection) | |
options = nested_attributes_options[association_name] | |
if attributes_collection.respond_to?(:permitted?) | |
attributes_collection = attributes_collection.to_h | |
end | |
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) | |
raise ArgumentError, "Hash or Array expected for attribute `#{association_name}`, got #{attributes_collection.class.name} (#{attributes_collection.inspect})" | |
end | |
check_record_limit!(options[:limit], attributes_collection) | |
if attributes_collection.is_a? Hash | |
keys = attributes_collection.keys | |
attributes_collection = if keys.include?("id") || keys.include?(:id) | |
[attributes_collection] | |
else | |
attributes_collection.values | |
end | |
end | |
association = association(association_name) | |
existing_records = if association.loaded? | |
association.target | |
else | |
attribute_ids = attributes_collection.filter_map { |a| a["id"] || a[:id] } | |
attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids) | |
end | |
existing_records = existing_records.index_by { |record| record.id.to_s } | |
attributes_collection.each do |attributes| | |
if attributes.respond_to?(:permitted?) | |
attributes = attributes.to_h | |
end | |
attributes = attributes.with_indifferent_access | |
if attributes["id"].blank? | |
unless reject_new_record?(association_name, attributes) | |
association.reader.build(attributes.except(*UNASSIGNABLE_KEYS)) | |
end | |
elsif existing_record = existing_records[attributes["id"].to_s] | |
unless call_reject_if(association_name, attributes) | |
# Make sure we are operating on the actual object which is in the association's | |
# proxy_target array (either by finding it, or adding it if not found) | |
# Take into account that the proxy_target may have changed due to callbacks | |
target_record = association.target.detect { |record| record.id.to_s == attributes["id"].to_s } | |
if target_record | |
existing_record = target_record | |
else | |
association.add_to_target(existing_record, skip_callbacks: true) | |
end | |
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) | |
end | |
else | |
raise_nested_attributes_record_not_found!(association_name, attributes["id"]) | |
end | |
end | |
end | |
end | |
Benchmark.ips do |x| | |
order = Order.create | |
Item.insert_all Array.new(2000, { order_id: order.id }) | |
items_attributes = order.items.map { |item| { id: item.id } } | |
order = Order.first | |
order.reload | |
x.report("Order") do |times| | |
order.update(items_attributes: items_attributes) | |
end | |
order2 = Order2.first | |
order2.reload | |
x.report("Order2") do |times| | |
order2.update(items_attributes: items_attributes) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment