module ActiveType::Util

Public Instance Methods

cast(object, klass, force: false) click to toggle source
# File lib/active_type/util.rb, line 7
def cast(object, klass, force: false)
  if object.is_a?(ActiveRecord::Relation)
    cast_relation(object, klass)
  elsif object.is_a?(ActiveRecord::Base)
    cast_record(object, klass, force: force)
  else
    raise ArgumentError, "Don't know how to cast #{object.inspect}"
  end
end
scoped(klass_or_relation) click to toggle source
# File lib/active_type/util.rb, line 17
def scoped(klass_or_relation)
  klass_or_relation.where(nil)
end

Private Instance Methods

associations_touched?(record) click to toggle source
# File lib/active_type/util.rb, line 76
def associations_touched?(record)
  return false unless record.instance_variable_get(:@association_cache)

  !!record.instance_variable_get(:@association_cache)[:associated_records]&.target&.any? do |target|
    target.changed?
  end
end
cast_record(record, klass, force: false) click to toggle source
# File lib/active_type/util.rb, line 23
def cast_record(record, klass, force: false)
  if associations_touched?(record) && !force
    raise NotCastableError, 'Record has changes in its loaded associations!'
  end

  # record.becomes(klass).dup
  klass.new do |casted|
    using_single_table_inheritance = using_single_table_inheritance?(klass, casted)

    # Rails 3.2, 4.2
    casted.instance_variable_set(:@attributes, record.instance_variable_get(:@attributes))
    # Rails 3.2
    casted.instance_variable_set(:@attributes_cache, record.instance_variable_get(:@attributes_cache))
    # Rails 4.2
    casted.instance_variable_set(:@changed_attributes, record.instance_variable_get(:@changed_attributes))
    # Rails 5.0
    casted.instance_variable_set(:@mutation_tracker, record.instance_variable_get(:@mutation_tracker))
    # Rails 3.2, 4.2
    casted.instance_variable_set(:@new_record, record.new_record?)
    # Rails 3.2, 4.2
    casted.instance_variable_set(:@destroyed, record.destroyed?)
    # Rails 5.2+
    casted.instance_variable_set(:@mutations_from_database, record.instance_variable_get(:@mutations_from_database))

    # Rails 3.2, 4.2
    errors = record.errors
    if errors.kind_of? ActiveModel::Errors
      errors = errors.dup
      # otherwise attributes defined in ActiveType::Record
      # won't be visible to `errors.add`
      errors.instance_variable_set(:@base, casted)
    end
    casted.instance_variable_set(:@errors, errors)

    casted[klass.inheritance_column] = klass.sti_name if using_single_table_inheritance

    if !force
      make_record_unusable(record)
    end
    casted
  end
end
cast_relation(relation, klass) click to toggle source
# File lib/active_type/util.rb, line 72
def cast_relation(relation, klass)
  scoped(klass).merge(scoped(relation))
end
make_record_unusable(record) click to toggle source
# File lib/active_type/util.rb, line 84
def make_record_unusable(record)
  # Changing and saving the base record may lead to unexpected behaviour,
  # since the casted record may have different changes in its autosave
  # associations and will be saved to the same record in the database as
  # the casted record. Therefore we prevent that.
  original_attributes = record.instance_variable_get(:@attributes)
  record.instance_variable_set(:@attributes, UnmutableAttributes.new(original_attributes) )
end
using_single_table_inheritance?(klass, record) click to toggle source

Backport for Rails 3.2

# File lib/active_type/util.rb, line 67
def using_single_table_inheritance?(klass, record)
  inheritance_column = klass.inheritance_column
  record[inheritance_column].present? && record.has_attribute?(inheritance_column)
end