In Rails, ActiveRecord::Base.new
is used for instantiating new records that aren't yet saved to the database:
new_user = User.new(name: "Bob")
new_user.new_record? # => true
So how does Rails instantiate records that are retrieved from the database? Does it use the same new method and then change values like @new_record
after the fact? Or does it use some sort of special instantiation method for records retrieved from the database?
The new_record? method can be found in active_record/persistence.rb in the ActiveRecord framework and it looks like this:
def new_record?
@new_record
end
Then if you look at active_record/core.rb in the constructor you will see this:
def initialize(attributes = nil, options = {})
@attributes = self.class.initialize_attributes(self.class.column_defaults.deep_dup)
@columns_hash = self.class.column_types.dup
init_internals # here
ensure_proper_type
populate_with_current_scope_attributes
assign_attributes(attributes, options) if attributes
yield self if block_given?
run_callbacks :initialize if _initialize_callbacks.any?
end
And if we dig a little bit deeper in the code:
def init_internals
pk = self.class.primary_key
@attributes[pk] = nil unless @attributes.key?(pk)
@aggregation_cache = {}
@association_cache = {}
@attributes_cache = {}
@previously_changed = {}
@changed_attributes = {}
@readonly = false
@destroyed = false
@marked_for_destruction = false
@new_record = true # here
@mass_assignment_options = nil
end
As you can see, the @new_record is initialized to true by default.
However there is some case where the @new_record attribute is set to true like when you clone a record:
user = User.first
new_user = user.clone
This will call the initialize_dup method that looks like:
def initialize_dup(other) # :nodoc:
# Code removed
@new_record = true
# Code removed
super
end
Or of course when ActiveRecord pulls records from database. I am not sure about this part, but I think that this method is called:
def init_with(coder)
@attributes = self.class.initialize_attributes(coder['attributes'])
@columns_hash = self.class.column_types.merge(coder['column_types'] || {})
init_internals
@new_record = false
run_callbacks :find
run_callbacks :initialize
self
end
Which could be done like this:
post = Post.allocate
post.init_with('attributes' => { 'title' => 'hello world' })
In the first statement, it allocates memory space on the heap without calling the constructor like new would do. Then it calls the special constructor init_with.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With