table_differ 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +24 -8
- data/lib/table_differ/version.rb +1 -1
- data/lib/table_differ.rb +29 -9
- data/spec/diff_spec.rb +2 -3
- data/spec/remap_spec.rb +41 -0
- data/spec/snapshot_spec.rb +7 -0
- data/spec/spec_helper.rb +12 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cca324c658aebd498e3a150ef613027b416f434
|
4
|
+
data.tar.gz: 78b5e10edf2b2da4ffdc209ca8ce4d8d62418db9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6238ba387974306c3e18deeac1cfebb5da47c5565e51f6905423584878068e8ebf0b61a446ccabd93cf9a91b7e53bb18bdf7084b64f5f1d586b09b5cc5dc409c
|
7
|
+
data.tar.gz: c1351cd32f9e19f439bd40f47273d5405c1b24dc0184941c54d004b93294ab48905fa273c690276e38d691b48634a9a741a9b708432b1a11c2cad5e97179a943
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
Take snapshots of database tables and compute the differences between two snapshots.
|
4
4
|
|
5
5
|
[](http://travis-ci.org/bronson/table_differ)
|
6
|
+
[](http://badge.fury.io/rb/table_differ)
|
6
7
|
|
7
8
|
## Installation
|
8
9
|
|
@@ -17,12 +18,11 @@ gem 'table_differ'
|
|
17
18
|
```ruby
|
18
19
|
Attachment.create_snapshot
|
19
20
|
=> "attachments_20140626_233336"
|
20
|
-
Attachment.first.
|
21
|
-
|
22
|
-
added,removed,changed = Attachment.diff_snapshot
|
21
|
+
Attachment.first.update_attributes!(name: 'newname')
|
22
|
+
added,removed,changed = Attachment.diff_snapshot # diffs against most recent snapshot
|
23
23
|
=> [[], [], [<Attachment 1>]]
|
24
|
-
changed.first.original_attributes # returns
|
25
|
-
=> {"
|
24
|
+
changed.first.original_attributes # returns original value for each field
|
25
|
+
=> {"name" => 'oldname'}
|
26
26
|
Attachment.delete_snapshot "attachments_20140626_233336"
|
27
27
|
```
|
28
28
|
|
@@ -85,7 +85,7 @@ made. For example, if you changed the name column from 'Nexus' to 'Nexii':
|
|
85
85
|
record.attributes
|
86
86
|
=> { 'id' => 1, 'name' => 'Nexus' }
|
87
87
|
record.original_attributes
|
88
|
-
=> { '
|
88
|
+
=> { 'id' => 1, 'name' => 'Nexii' }
|
89
89
|
```
|
90
90
|
|
91
91
|
Single-Table Inheritance (STI) appears to work correctly (TODO: add this to tests!)
|
@@ -108,6 +108,20 @@ so you can ignore the empty third array.
|
|
108
108
|
added,removed = Attachment.diff_snapshot(ignore: 'id')
|
109
109
|
```
|
110
110
|
|
111
|
+
If there are other fields that you can use to uniquely identify the records,
|
112
|
+
you can specify them in the unique_by option. This will ensure that changes
|
113
|
+
are returned (not just adds/removes), and the ActiveRecord objects returned are
|
114
|
+
complete with IDs. This requires one database lookup per returned object,
|
115
|
+
however so, if your results sets are huge, this might not be a good idea.
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
# Normally ingoring the ID prevents diff from being able to compute the changed records.
|
119
|
+
# If we can tell it that one or more fields can be used to uniquely identify the object,
|
120
|
+
# then it can compute the changed records and return full ActiveRecord objects.
|
121
|
+
added,removed,changed = Contact.diff_snapshot(ignore: 'id', unique_by: [:property_id, :contact_id])
|
122
|
+
|
123
|
+
```
|
124
|
+
|
111
125
|
Also, if you ignore the ID, you won't be able to update or save any models directly.
|
112
126
|
You must copy the attributes to another model, one that was loaded from the database
|
113
127
|
normally and still knows its ID.
|
@@ -124,10 +138,12 @@ a,r,c = Property.diff_snapshot('cc', 'cd') # difference between the tw
|
|
124
138
|
### Delete Snapshots
|
125
139
|
|
126
140
|
delete_snapshot gets rid of unwanted snapshots.
|
127
|
-
|
141
|
+
Pass an array of names or a proc to specify which snapshots should be deleted,
|
142
|
+
or `:all`.
|
128
143
|
|
129
144
|
```ruby
|
130
|
-
Property.delete_snapshot
|
145
|
+
Property.delete_snapshot 'import_0012'
|
146
|
+
Property.delete_snapshots :all
|
131
147
|
|
132
148
|
week_old_name = Property.snapshot_name(1.week.ago)
|
133
149
|
old_snapshots = Property.snapshots.select { |name| name < week_old_name }
|
data/lib/table_differ/version.rb
CHANGED
data/lib/table_differ.rb
CHANGED
@@ -44,8 +44,24 @@ module TableDiffer
|
|
44
44
|
end
|
45
45
|
|
46
46
|
# deletes every snapshot named in the array
|
47
|
-
|
48
|
-
|
47
|
+
# Model.delete_snapshots(:all) deletes all snapshots
|
48
|
+
def delete_snapshots snaps
|
49
|
+
snaps = self.snapshots if snaps == :all
|
50
|
+
snaps.each { |name| delete_snapshot(name) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def table_differ_remap_objects params, records
|
54
|
+
params = Array(params)
|
55
|
+
records.map do |record|
|
56
|
+
args = params.inject({}) { |hash,key| hash[key] = record[key]; hash }
|
57
|
+
real_record = where(args).first
|
58
|
+
if real_record
|
59
|
+
real_record.original_attributes = record.attributes
|
60
|
+
real_record
|
61
|
+
else
|
62
|
+
record
|
63
|
+
end
|
64
|
+
end
|
49
65
|
end
|
50
66
|
|
51
67
|
# ignore: %w[ created_at updated_at id ]
|
@@ -71,19 +87,23 @@ module TableDiffer
|
|
71
87
|
# actually, it's probably more reliable just to use the presence of an id to determine if the record can be saved
|
72
88
|
# [*added, *removed].select { |o| !o.id }.each { |o| o.instance_variable_set("@new_record", true) }
|
73
89
|
|
90
|
+
if options[:unique_by]
|
91
|
+
added = table_differ_remap_objects(options[:unique_by], added)
|
92
|
+
removed = table_differ_remap_objects(options[:unique_by], removed)
|
93
|
+
end
|
94
|
+
|
74
95
|
changed = added & removed
|
75
96
|
changed.each do |obj|
|
76
97
|
orig = removed.find { |r| r == obj }
|
77
98
|
raise "this is impossible" if orig.nil?
|
78
|
-
|
79
|
-
nattrs = obj.attributes
|
80
|
-
# remove all unchanged elements -- original_attributes only contains changed values
|
81
|
-
oattrs = orig.attributes.reject { |k,v| nattrs.include?(k) && nattrs[k] == v }
|
82
|
-
|
83
|
-
obj.original_attributes = HashWithIndifferentAccess.new(oattrs)
|
99
|
+
obj.original_attributes = orig.original_attributes || orig.attributes
|
84
100
|
end
|
85
101
|
|
86
|
-
|
102
|
+
added -= changed
|
103
|
+
removed -= changed
|
104
|
+
[*added, *removed].each { |o| o.original_attributes = nil }
|
105
|
+
|
106
|
+
[added, removed, changed]
|
87
107
|
end
|
88
108
|
end
|
89
109
|
end
|
data/spec/diff_spec.rb
CHANGED
@@ -63,8 +63,7 @@ describe "diffing a model" do
|
|
63
63
|
expect(changed.first.name).to eq 'uno'
|
64
64
|
|
65
65
|
# ensure we can access the previous value, with indifferent access
|
66
|
-
expect(changed.first.original_attributes
|
67
|
-
expect(changed.first.original_attributes).to eq({'name' => 'one'})
|
66
|
+
expect(changed.first.original_attributes).to eq({'id' => one.id, 'name' => 'one'})
|
68
67
|
|
69
68
|
# changed records are normal AR objects, try using it
|
70
69
|
changed.first.update_attributes!(name: 'nuevo')
|
@@ -123,7 +122,7 @@ describe "diffing a model" do
|
|
123
122
|
end
|
124
123
|
|
125
124
|
# without an ID, we can't tell if anything changed
|
126
|
-
it "detects a changed field" do
|
125
|
+
it "detects a changed field by add/remove" do
|
127
126
|
one = Model.where(name: 'one').first
|
128
127
|
one.update_attributes!(name: 'uno')
|
129
128
|
added,removed,changed = Model.diff_snapshot(ignore: :id)
|
data/spec/remap_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
describe "diffing a model" do
|
2
|
+
include_context "surrogate_model"
|
3
|
+
|
4
|
+
it "detects a changed field using a single surrogate" do
|
5
|
+
first = SurrogateModel.create!(name: 'one', original_name: 'one')
|
6
|
+
second = SurrogateModel.create!(name: 'two', original_name: 'two')
|
7
|
+
|
8
|
+
SurrogateModel.create_snapshot('original')
|
9
|
+
|
10
|
+
first.update_attributes!(name: 'uno')
|
11
|
+
third = SurrogateModel.create!(name: 'three', original_name: 'three')
|
12
|
+
second.destroy!
|
13
|
+
|
14
|
+
added,removed,changed = SurrogateModel.diff_snapshot(ignore: :id, unique_by: :original_name)
|
15
|
+
|
16
|
+
# we can find added and changed records by surrogate IDs but, of course, can't find removed ones
|
17
|
+
expect(added).to eq [third]
|
18
|
+
expect(added.first.original_attributes).to eq nil
|
19
|
+
expect(removed.map(&:attributes)).to eq [{"id" => nil, "name" => "two", "original_name" => "two", "alternate_value" => nil}]
|
20
|
+
expect(removed.first.original_attributes).to eq nil
|
21
|
+
expect(changed).to eq [first]
|
22
|
+
expect(changed.first.name).to eq 'uno'
|
23
|
+
expect(changed.first.original_attributes).to eq({"id" => nil, "name" => 'one', "original_name" => 'one', "alternate_value" => nil})
|
24
|
+
end
|
25
|
+
|
26
|
+
it "detects a changed field using a composite surrogate" do
|
27
|
+
first = SurrogateModel.create!(name: 'one', original_name: 'one', alternate_value: 1)
|
28
|
+
second = SurrogateModel.create!(name: 'one', original_name: 'one', alternate_value: 2)
|
29
|
+
third = SurrogateModel.create!(name: 'one', original_name: 'one', alternate_value: 3)
|
30
|
+
|
31
|
+
SurrogateModel.create_snapshot('original')
|
32
|
+
|
33
|
+
second.update_attributes!(name: 'uno')
|
34
|
+
|
35
|
+
added,removed,changed = SurrogateModel.diff_snapshot(ignore: :id, unique_by: [:original_name, 'alternate_value'])
|
36
|
+
|
37
|
+
expect(added).to eq []
|
38
|
+
expect(removed).to eq []
|
39
|
+
expect(changed).to eq [second] # the alternate value should ensure we pick up the correct record
|
40
|
+
end
|
41
|
+
end
|
data/spec/snapshot_spec.rb
CHANGED
@@ -47,4 +47,11 @@ describe TableDiffer do
|
|
47
47
|
Model.delete_snapshots(to_delete)
|
48
48
|
expect(Model.snapshots.sort).to eq ['models_21']
|
49
49
|
end
|
50
|
+
|
51
|
+
it "deletes all snapshots" do
|
52
|
+
Model.create_snapshot('snapname')
|
53
|
+
expect(Model.snapshots.size).to eq 1
|
54
|
+
Model.delete_snapshots(:all)
|
55
|
+
expect(Model.snapshots.size).to eq 0
|
56
|
+
end
|
50
57
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -23,6 +23,12 @@ RSpec.configure do |config|
|
|
23
23
|
create_table :models do |table|
|
24
24
|
table.column :name, :string
|
25
25
|
end
|
26
|
+
|
27
|
+
create_table :surrogate_models do |table|
|
28
|
+
table.column :name, :string
|
29
|
+
table.column :original_name, :string
|
30
|
+
table.column :alternate_value, :string
|
31
|
+
end
|
26
32
|
end
|
27
33
|
|
28
34
|
# need to explicitly specify active_record since we don't have a database.yml?
|
@@ -44,6 +50,12 @@ RSpec.shared_context "model" do
|
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
53
|
+
RSpec.shared_context "surrogate_model" do
|
54
|
+
class SurrogateModel < ActiveRecord::Base
|
55
|
+
include TableDiffer
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
47
59
|
=begin
|
48
60
|
maybe later...
|
49
61
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: table_differ
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Bronson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -100,6 +100,7 @@ files:
|
|
100
100
|
- lib/table_differ.rb
|
101
101
|
- lib/table_differ/version.rb
|
102
102
|
- spec/diff_spec.rb
|
103
|
+
- spec/remap_spec.rb
|
103
104
|
- spec/snapshot_spec.rb
|
104
105
|
- spec/spec_helper.rb
|
105
106
|
- tablediffer.gemspec
|
@@ -130,5 +131,6 @@ summary: Take snapshots of database tables and compute the differences between t
|
|
130
131
|
snapshots.
|
131
132
|
test_files:
|
132
133
|
- spec/diff_spec.rb
|
134
|
+
- spec/remap_spec.rb
|
133
135
|
- spec/snapshot_spec.rb
|
134
136
|
- spec/spec_helper.rb
|