module Softwear::Library::SpecDump

Constants

Record

Public Instance Methods

expand_spec_dump(dump, use_outside_of_test = false) click to toggle source
# File lib/softwear/library/spec_dump.rb, line 98
def expand_spec_dump(dump, use_outside_of_test = false)
  if !Rails.env.test? && !use_outside_of_test
    raise "Tried to call expand_spec_dump outside of test environment. "\
          "If you really want to do this, pass `true` as the second parameter."
  end

  if ActiveRecord::Base.configurations[Rails.env]['adapter'].include?('sqlite')
    insert_cmd = lambda do |model|
      "INSERT OR REPLACE INTO #{model.table_name} (#{model.column_names.map { |c| "`#{c}`" }.join(', ')}) VALUES\n"
    end
    cmd_suffix = ->_{ "" }
  else
    insert_cmd = lambda do |model|
      "INSERT INTO #{model.table_name} (#{model.column_names.map { |c| "`#{c}`" }.join(', ')}) VALUES\n"
    end
    cmd_suffix = lambda do |model|
      "\nON DUPLICATE KEY UPDATE\n" +
        model.column_names
          .map { |col| "`#{col}` = VALUES(`#{col}`)" }
          .join(",\n")
    end
  end

  dump.each do |class_name, entries|
    model    = class_name.constantize
    sql      = insert_cmd[model]
    sanitize = model.connection.method(:quote)

    sql += entries.map do |entry|
      _id, attributes = entry

      '(' +
        model.column_names.map { |col| sanitize[attributes[col]] }.join(', ') +
      ')'
    end.join(",\n")

    sql += cmd_suffix[model]

    model.connection.execute sql
  end
end
spec_dump(root, ignored_models = [], whitelist_array = nil) { |record, types_recorded| ... } click to toggle source
# File lib/softwear/library/spec_dump.rb, line 8
def spec_dump(root, ignored_models = [], whitelist_array = nil)
  types_recorded = {}
  records_by_type = {}
  record_queue = Queue.new
  whitelist = whitelist_array ? whitelist_array.reduce({}) { |h,n| h.merge(n => true) } : Hash.new(true)

  is_ignored = lambda do |name|
    next true if ignored_models.any? { |i| name =~ i }
    next true if !whitelist[name]
    false
  end

  # Begin dump routine. This will return true when we added a new entry.
  dump = lambda do |record|
    cache = (records_by_type[record.model.name] ||= {})
    identifier = record.object.id

    next false if cache[identifier].present?

    attributes = {}
    record.model.column_names.each do |col|
      value = record.object[col]
      if value.respond_to?(:iso8601)
        # DateTimes don't serialize properly in attributes_before_type_cast
        # for some reason, so we explicitly call to_s(:db) to make sure
        # they can be loaded again correctly.
        raw_value = value.to_s(:db)
      else
        raw_value = record.object.attributes_before_type_cast[col]
      end

      attributes[col] = raw_value
    end

    cache[identifier] = attributes
    true
  end
  # end dump routine

  # Begin actual dumping of records
  Array(root).each do |record|
    record_queue << Record.new(record, ["#{record.class.name}##{record.id}"])
  end

  while record_queue.present?
    record = record_queue.pop
    next if record.object.nil?
    next unless record.model.respond_to?(:column_names)

    # If dump returns false, that means we've already dumped this record
    next unless dump.(record)
    types_recorded[record.model.name] = true

    yield record, types_recorded if block_given?

    record.model.reflect_on_all_associations.each do |assoc|
      next if assoc.is_a?(ActiveRecord::Reflection::ThroughReflection)

      next if is_ignored["#{record.model.name}##{assoc.name}"]

      case assoc
      when ActiveRecord::Reflection::BelongsToReflection
        # A belongs_to association will never cause an infinite loop
        record_queue << Record.new(
          record.object.send(assoc.name),
          record.history + ["#{record.model.name}##{record.object.id}##{assoc.name}"]
        )

      when ActiveRecord::Reflection::HasManyReflection
        # A has_many association can cause an infinite loop, so we only
        # process these if we've never seen the record type before.
        #
        # If there's a whitelist, no need to care about that
        next if whitelist_array.blank? && types_recorded[assoc.klass.name]

        record.object.send(assoc.name).each_with_index do |child, i|
          next if child.nil?
          record_queue << Record.new(
            child,
            record.history + ["#{record.model.name}##{record.object.id}##{assoc.name}[#{i}]"]
          )
        end
      end
    end
  end
  # end actual dumping of records

  records_by_type
end