Module: TableDiffer::ClassMethods

Defined in:
lib/table_differ.rb

Instance Method Summary collapse

Instance Method Details

#create_snapshot(suggestion = Time.now) ⇒ Object

creates a new snapshot



35
36
37
38
39
# File 'lib/table_differ.rb', line 35

def create_snapshot suggestion=Time.now
  name = snapshot_name(suggestion)
  connection.execute("CREATE TABLE #{name} AS SELECT * FROM #{table_name}")
  name
end

#delete_snapshot(name) ⇒ Object

deletes the named snapshot



42
43
44
# File 'lib/table_differ.rb', line 42

def delete_snapshot name
  connection.execute("DROP TABLE #{snapshot_name(name)}")
end

#delete_snapshots(snaps) ⇒ Object

deletes every snapshot named in the array Model.delete_snapshots(:all) deletes all snapshots



48
49
50
51
# File 'lib/table_differ.rb', line 48

def delete_snapshots snaps
  snaps = self.snapshots if snaps == :all
  snaps.each { |name| delete_snapshot(name) }
end

#diff_snapshot(options = {}) ⇒ Object

ignore: %w[ created_at updated_at id ]



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/table_differ.rb', line 79

def diff_snapshot options={}
  oldtable = snapshot_name(options[:old]) || snapshots.last
  newtable = snapshot_name(options[:new]) || table_name

  ignore = []
  if options[:ignore]
    ignore = Array(options[:ignore]).map(&:to_s)
  end

  columns = column_names - ignore
  cols = columns.map { |c| "#{c} as #{c}" }.join(", ")

  added =   find_by_sql("SELECT #{cols} FROM #{newtable} EXCEPT SELECT #{cols} FROM #{oldtable}")
  removed = find_by_sql("SELECT #{cols} from #{oldtable} EXCEPT SELECT #{cols} FROM #{newtable}")

  # hm, none of this seems to matter...  TODO: mark appropriate objects read-only: obj.readonly!
  # AR always thinks the record is persisted in the db, even when it obviously isn't
  # added.each   { |o| o.instance_variable_set("@new_record", true) } unless table_name == oldtable
  # removed.each { |o| o.instance_variable_set("@new_record", true) } unless table_name == newtable
  # actually, it's probably more reliable just to use the presence of an id to determine if the record can be saved
  # [*added, *removed].select { |o| !o.id }.each { |o| o.instance_variable_set("@new_record", true) }

  if options[:unique_by]
    added = table_differ_remap_objects(options[:unique_by], added, newtable)
    removed = table_differ_remap_objects(options[:unique_by], removed, oldtable)
  end

  changed = added & removed
  changed.each do |obj|
    orig = removed.find { |r| r == obj }
    raise "this is impossible" if orig.nil?
    obj.original_attributes = (orig.original_attributes || orig.attributes).except(*ignore)
  end

  added -= changed
  removed -= changed
  [*added, *removed].each { |o| o.original_attributes = nil }

  [added, removed, changed]
end

#snapshot_name(name) ⇒ Object

pass a date or name fragment, receive the full snapshot name. it’s ok to pass a snapshot name; it will be returned unchaged.



15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/table_differ.rb', line 15

def snapshot_name name
  return nil if name.nil?

  if name.kind_of?(Date) || name.kind_of?(Time)
    name = name.strftime("%Y%m%d_%H%M%S")
  end

  unless name.index(table_name) == 0
    name = "#{table_name}_#{name}"
  end

  name
end

#snapshotsObject

returns an array of the snapshot names that currently exist



30
31
32
# File 'lib/table_differ.rb', line 30

def snapshots
  connection.tables.grep(/^#{table_name}_/).sort
end

#table_differ_remap_objects(params, records, table) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/table_differ.rb', line 53

def table_differ_remap_objects params, records, table
  model = self
  if table != table_name
    model = Class.new(self)
    model.table_name = table
  end

  params = Array(params)
  records.map do |record|
    result = record
    if record.id.nil?   # don't look up real ActiveRecord object if we already have one
      args = params.inject({}) { |hash,key| hash[key] = record[key]; hash }
      real_record = model.where(args).first
      if real_record
        if model != self
          real_record = self.new(real_record.attributes)  # convert fake model to real model
        end
        real_record.original_attributes = record.attributes
        result = real_record
      end
    end
    result
  end
end