Module: Softwear::Library::SpecDump

Extended by:
SpecDump
Included in:
SpecDump, SpecDumpController
Defined in:
lib/softwear/library/spec_dump.rb

Defined Under Namespace

Classes: Record

Instance Method Summary collapse

Instance Method Details

#expand_spec_dump(dump, use_outside_of_test = false) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# 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) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# 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