Module: ActiveRecord::Persistence::ClassMethods

Defined in:
lib/active_record/persistence.rb

Instance Method Summary collapse

Instance Method Details

#_delete_record(constraints) ⇒ Object

:nodoc:



388
389
390
391
392
393
394
395
396
# File 'lib/active_record/persistence.rb', line 388

def _delete_record(constraints) # :nodoc:
  constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }

  dm = Arel::DeleteManager.new
  dm.from(arel_table)
  dm.wheres = constraints

  connection.delete(dm, "#{self} Destroy")
end

#_insert_record(values) ⇒ Object

:nodoc:



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/active_record/persistence.rb', line 355

def _insert_record(values) # :nodoc:
  primary_key = self.primary_key
  primary_key_value = nil

  if primary_key && Hash === values
    primary_key_value = values[primary_key]

    if !primary_key_value && prefetch_primary_key?
      primary_key_value = next_sequence_value
      values[primary_key] = primary_key_value
    end
  end

  if values.empty?
    im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
    im.into arel_table
  else
    im = arel_table.compile_insert(_substitute_values(values))
  end

  connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
end

#_update_record(values, constraints) ⇒ Object

:nodoc:



378
379
380
381
382
383
384
385
386
# File 'lib/active_record/persistence.rb', line 378

def _update_record(values, constraints) # :nodoc:
  constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }

  um = arel_table.where(
    constraints.reduce(&:and)
  ).compile_update(_substitute_values(values), primary_key)

  connection.update(um, "#{self} Update")
end

#create(attributes = nil, &block) ⇒ Object

Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.

The attributes parameter can be either a Hash or an Array of Hashes. These Hashes describe the attributes on the objects that are to be created.

Examples

# Create a single new object
User.create(first_name: 'Jamie')

# Create an Array of new objects
User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])

# Create a single object and pass it into a block to set other attributes.
User.create(first_name: 'Jamie') do |u|
  u.is_admin = false
end

# Creating an Array of new objects using a block, where the block is executed for each object:
User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
  u.is_admin = false
end


33
34
35
36
37
38
39
40
41
# File 'lib/active_record/persistence.rb', line 33

def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    object = new(attributes, &block)
    object.save
    object
  end
end

#create!(attributes = nil, &block) ⇒ Object

Creates an object (or multiple objects) and saves it to the database, if validations pass. Raises a RecordInvalid error if validations fail, unlike Base#create.

The attributes parameter can be either a Hash or an Array of Hashes. These describe which attributes to be created on the object, or multiple objects when given an Array of Hashes.



50
51
52
53
54
55
56
57
58
# File 'lib/active_record/persistence.rb', line 50

def create!(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create!(attr, &block) }
  else
    object = new(attributes, &block)
    object.save!
    object
  end
end

#delete(id_or_array) ⇒ Object

Deletes the row with a primary key matching the id argument, using an SQL DELETE statement, and returns the number of rows deleted. Active Record objects are not instantiated, so the object's callbacks are not executed, including any :dependent association options.

You can delete multiple rows at once by passing an Array of ids.

Note: Although it is often much faster than the alternative, #destroy, skipping callbacks might bypass business logic in your application that ensures referential integrity or performs other essential jobs.

Examples

# Delete a single row
Todo.delete(1)

# Delete multiple rows
Todo.delete([2,3,4])


351
352
353
# File 'lib/active_record/persistence.rb', line 351

def delete(id_or_array)
  delete_by(primary_key => id_or_array)
end

#destroy(id) ⇒ Object

Destroy an object (or multiple objects) that has the given id. The object is instantiated first, therefore all callbacks and filters are fired off before the object is deleted. This method is less efficient than #delete but allows cleanup methods and other actions to be run.

This essentially finds the object (or multiple objects) with the given id, creates a new object from the attributes, and then calls destroy on it.

Parameters

  • id - This should be the id or an array of ids to be destroyed.

Examples

# Destroy a single object
Todo.destroy(1)

# Destroy multiple objects
todos = [1,2,3]
Todo.destroy(todos)


325
326
327
328
329
330
331
# File 'lib/active_record/persistence.rb', line 325

def destroy(id)
  if id.is_a?(Array)
    find(id).each(&:destroy)
  else
    find(id).destroy
  end
end

#insert(attributes, returning: nil, unique_by: nil) ⇒ Object

Inserts a single record into the database in a single SQL INSERT statement. It does not instantiate any models nor does it trigger Active Record callbacks or validations. Though passed values go through Active Record's type casting and serialization.

See ActiveRecord::Persistence#insert_all for documentation.



66
67
68
# File 'lib/active_record/persistence.rb', line 66

def insert(attributes, returning: nil, unique_by: nil)
  insert_all([ attributes ], returning: returning, unique_by: unique_by)
end

#insert!(attributes, returning: nil) ⇒ Object

Inserts a single record into the database in a single SQL INSERT statement. It does not instantiate any models nor does it trigger Active Record callbacks or validations. Though passed values go through Active Record's type casting and serialization.

See ActiveRecord::Persistence#insert_all! for more.



133
134
135
# File 'lib/active_record/persistence.rb', line 133

def insert!(attributes, returning: nil)
  insert_all!([ attributes ], returning: returning)
end

#insert_all(attributes, returning: nil, unique_by: nil) ⇒ Object

Inserts multiple records into the database in a single SQL INSERT statement. It does not instantiate any models nor does it trigger Active Record callbacks or validations. Though passed values go through Active Record's type casting and serialization.

The attributes parameter is an Array of Hashes. Every Hash determines the attributes for a single row and must have the same keys.

Rows are considered to be unique by every unique index on the table. Any duplicate rows are skipped. Override with :unique_by (see below).

Returns an ActiveRecord::Result with its contents based on :returning (see below).

Options

:returning

(PostgreSQL only) An array of attributes to return for all successfully inserted records, which by default is the primary key. Pass returning: %w[ id name ] for both id and name or returning: false to omit the underlying RETURNING SQL clause entirely.

:unique_by

(PostgreSQL and SQLite only) By default rows are considered to be unique by every unique index on the table. Any duplicate rows are skipped.

To skip rows according to just one unique index pass :unique_by.

Consider a Book model where no duplicate ISBNs make sense, but if any row has an existing id, or is not unique by another unique index, ActiveRecord::RecordNotUnique is raised.

Unique indexes can be identified by columns or name:

unique_by: :isbn
unique_by: %i[ author_id name ]
unique_by: :index_books_on_isbn

Because it relies on the index information from the database :unique_by is recommended to be paired with Active Record's schema_cache.

Example

# Insert records and skip inserting any duplicates.
# Here "Eloquent Ruby" is skipped because its id is not unique.

Book.insert_all([
  { id: 1, title: "Rework", author: "David" },
  { id: 1, title: "Eloquent Ruby", author: "Russ" }
])


123
124
125
# File 'lib/active_record/persistence.rb', line 123

def insert_all(attributes, returning: nil, unique_by: nil)
  InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
end

#insert_all!(attributes, returning: nil) ⇒ Object

Inserts multiple records into the database in a single SQL INSERT statement. It does not instantiate any models nor does it trigger Active Record callbacks or validations. Though passed values go through Active Record's type casting and serialization.

The attributes parameter is an Array of Hashes. Every Hash determines the attributes for a single row and must have the same keys.

Raises ActiveRecord::RecordNotUnique if any rows violate a unique index on the table. In that case, no rows are inserted.

To skip duplicate rows, see ActiveRecord::Persistence#insert_all. To replace them, see ActiveRecord::Persistence#upsert_all.

Returns an ActiveRecord::Result with its contents based on :returning (see below).

Options

:returning

(PostgreSQL only) An array of attributes to return for all successfully inserted records, which by default is the primary key. Pass returning: %w[ id name ] for both id and name or returning: false to omit the underlying RETURNING SQL clause entirely.

Examples

# Insert multiple records
Book.insert_all!([
  { title: "Rework", author: "David" },
  { title: "Eloquent Ruby", author: "Russ" }
])

# Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
# does not have a unique id.
Book.insert_all!([
  { id: 1, title: "Rework", author: "David" },
  { id: 1, title: "Eloquent Ruby", author: "Russ" }
])


177
178
179
# File 'lib/active_record/persistence.rb', line 177

def insert_all!(attributes, returning: nil)
  InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
end

#instantiate(attributes, column_types = {}, &block) ⇒ Object

Given an attributes hash, instantiate returns a new instance of the appropriate class. Accepts only keys as strings.

For example, Post.all may return Comments, Messages, and Emails by storing the record's subclass in a type attribute. By calling instantiate instead of new, finder methods ensure they get new instances of the appropriate class for each record.

See ActiveRecord::Inheritance#discriminate_class_for_record to see how this “single-table” inheritance mapping is implemented.



256
257
258
259
# File 'lib/active_record/persistence.rb', line 256

def instantiate(attributes, column_types = {}, &block)
  klass = discriminate_class_for_record(attributes)
  instantiate_instance_of(klass, attributes, column_types, &block)
end

#update(id = :all, attributes) ⇒ Object

Updates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.

Parameters

  • id - This should be the id or an array of ids to be updated.

  • attributes - This should be a hash of attributes or an array of hashes.

Examples

# Updates one record
Person.update(15, user_name: "Samuel", group: "expert")

# Updates multiple records
people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
Person.update(people.keys, people.values)

# Updates multiple records from the result of a relation
people = Person.where(group: "expert")
people.update(group: "masters")

Note: Updating a large number of records will run an UPDATE query for each record, which may cause a performance issue. When running callbacks is not needed for each record update, it is preferred to use update_all for updating all records in a single query.



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/active_record/persistence.rb', line 287

def update(id = :all, attributes)
  if id.is_a?(Array)
    id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
      object.update(attributes[idx])
    }
  elsif id == :all
    all.each { |record| record.update(attributes) }
  else
    if ActiveRecord::Base === id
      raise ArgumentError,
        "You are passing an instance of ActiveRecord::Base to `update`. " \
        "Please pass the id of the object by calling `.id`."
    end
    object = find(id)
    object.update(attributes)
    object
  end
end

#upsert(attributes, returning: nil, unique_by: nil) ⇒ Object

Updates or inserts (upserts) a single record into the database in a single SQL INSERT statement. It does not instantiate any models nor does it trigger Active Record callbacks or validations. Though passed values go through Active Record's type casting and serialization.

See ActiveRecord::Persistence#upsert_all for documentation.



187
188
189
# File 'lib/active_record/persistence.rb', line 187

def upsert(attributes, returning: nil, unique_by: nil)
  upsert_all([ attributes ], returning: returning, unique_by: unique_by)
end

#upsert_all(attributes, returning: nil, unique_by: nil) ⇒ Object

Updates or inserts (upserts) multiple records into the database in a single SQL INSERT statement. It does not instantiate any models nor does it trigger Active Record callbacks or validations. Though passed values go through Active Record's type casting and serialization.

The attributes parameter is an Array of Hashes. Every Hash determines the attributes for a single row and must have the same keys.

Returns an ActiveRecord::Result with its contents based on :returning (see below).

Options

:returning

(PostgreSQL only) An array of attributes to return for all successfully inserted records, which by default is the primary key. Pass returning: %w[ id name ] for both id and name or returning: false to omit the underlying RETURNING SQL clause entirely.

:unique_by

(PostgreSQL and SQLite only) By default rows are considered to be unique by every unique index on the table. Any duplicate rows are skipped.

To skip rows according to just one unique index pass :unique_by.

Consider a Book model where no duplicate ISBNs make sense, but if any row has an existing id, or is not unique by another unique index, ActiveRecord::RecordNotUnique is raised.

Unique indexes can be identified by columns or name:

unique_by: :isbn
unique_by: %i[ author_id name ]
unique_by: :index_books_on_isbn

Because it relies on the index information from the database :unique_by is recommended to be paired with Active Record's schema_cache.

Examples

# Inserts multiple records, performing an upsert when records have duplicate ISBNs.
# Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.

Book.upsert_all([
  { title: "Rework", author: "David", isbn: "1" },
  { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
], unique_by: :isbn)

Book.find_by(isbn: "1").title # => "Eloquent Ruby"


242
243
244
# File 'lib/active_record/persistence.rb', line 242

def upsert_all(attributes, returning: nil, unique_by: nil)
  InsertAll.new(self, attributes, on_duplicate: :update, returning: returning, unique_by: unique_by).execute
end