yesterday 0.2
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.
- data/.gitignore +5 -0
- data/Gemfile +8 -0
- data/README.rdoc +86 -0
- data/Rakefile +2 -0
- data/lib/generators/yesterday/install_generator.rb +18 -0
- data/lib/generators/yesterday/templates/create_changesets.rb +17 -0
- data/lib/yesterday/changeset.rb +40 -0
- data/lib/yesterday/differ.rb +99 -0
- data/lib/yesterday/historical_value.rb +0 -0
- data/lib/yesterday/model.rb +100 -0
- data/lib/yesterday/serializer.rb +88 -0
- data/lib/yesterday/version.rb +3 -0
- data/lib/yesterday/versioned_attribute.rb +21 -0
- data/lib/yesterday/versioned_object.rb +18 -0
- data/lib/yesterday/versioned_object_creator.rb +33 -0
- data/lib/yesterday/versioning.rb +40 -0
- data/lib/yesterday.rb +17 -0
- data/spec/database.yml +3 -0
- data/spec/differ_spec.rb +206 -0
- data/spec/models.rb +12 -0
- data/spec/schema.rb +41 -0
- data/spec/serializer_spec.rb +66 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/versioned_object_creator_spec.rb +41 -0
- data/spec/yesterday_model_spec.rb +195 -0
- data/yesterday.gemspec +25 -0
- metadata +140 -0
data/spec/differ_spec.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'yesterday'
|
2
|
+
|
3
|
+
describe Yesterday::Differ do
|
4
|
+
it 'should detect changes' do
|
5
|
+
from = {
|
6
|
+
'id' => 1,
|
7
|
+
'description' => 'Some description',
|
8
|
+
'contacts' => [
|
9
|
+
{
|
10
|
+
'id' => 1,
|
11
|
+
'name' => 'Harold',
|
12
|
+
'addresses' => [
|
13
|
+
{ 'id' => 1,
|
14
|
+
'address' => 'Sesamstreet 1' },
|
15
|
+
{ 'id' => 2,
|
16
|
+
'address' => 'Sesamstreet 2' }
|
17
|
+
]
|
18
|
+
}
|
19
|
+
]
|
20
|
+
}
|
21
|
+
|
22
|
+
to = {
|
23
|
+
'id' => 1,
|
24
|
+
'description' => 'Other description',
|
25
|
+
'contacts' => [
|
26
|
+
{
|
27
|
+
'id' => 1,
|
28
|
+
'name' => 'Peter',
|
29
|
+
'addresses' => [
|
30
|
+
{ 'id' => 1,
|
31
|
+
'address' => 'Foobar 1' },
|
32
|
+
{ 'id' => 2,
|
33
|
+
'address' => 'Foobar 2' }
|
34
|
+
]
|
35
|
+
}
|
36
|
+
]
|
37
|
+
}
|
38
|
+
|
39
|
+
Yesterday::Differ.new(from, to).diff.should == {
|
40
|
+
'id' => 1,
|
41
|
+
'description' => ['Some description', 'Other description'],
|
42
|
+
'contacts' => [
|
43
|
+
{
|
44
|
+
'id' => 1,
|
45
|
+
'name' => ['Harold', 'Peter'],
|
46
|
+
'addresses' => [
|
47
|
+
{ 'id' => 1,
|
48
|
+
'address' => ['Sesamstreet 1', 'Foobar 1'] },
|
49
|
+
{ 'id' => 2,
|
50
|
+
'address' => ['Sesamstreet 2', 'Foobar 2'] }
|
51
|
+
]
|
52
|
+
}
|
53
|
+
]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should detect destroyed items' do
|
58
|
+
from = {
|
59
|
+
'id' => 1,
|
60
|
+
'description' => 'Some description',
|
61
|
+
'contacts' => [
|
62
|
+
{
|
63
|
+
'id' => 1,
|
64
|
+
'name' => 'Harold',
|
65
|
+
'addresses' => [
|
66
|
+
{ 'id' => 1,
|
67
|
+
'address' => 'Sesamstreet 1' },
|
68
|
+
{ 'id' => 2,
|
69
|
+
'address' => 'Sesamstreet 2' }
|
70
|
+
]
|
71
|
+
},
|
72
|
+
{
|
73
|
+
'id' => 2,
|
74
|
+
'name' => 'Peter',
|
75
|
+
'addresses' => [
|
76
|
+
{ 'id' => 1,
|
77
|
+
'address' => 'Sesamstreet 1' },
|
78
|
+
{ 'id' => 2,
|
79
|
+
'address' => 'Sesamstreet 2' }
|
80
|
+
]
|
81
|
+
}
|
82
|
+
]
|
83
|
+
}
|
84
|
+
|
85
|
+
to = {
|
86
|
+
'id' => 1,
|
87
|
+
'description' => 'Some description',
|
88
|
+
'contacts' => [
|
89
|
+
{
|
90
|
+
'id' => 1,
|
91
|
+
'name' => 'Harold',
|
92
|
+
'addresses' => [
|
93
|
+
{ 'id' => 2,
|
94
|
+
'address' => 'Sesamstreet 2' }
|
95
|
+
]
|
96
|
+
}
|
97
|
+
]
|
98
|
+
}
|
99
|
+
|
100
|
+
Yesterday::Differ.new(from, to).diff.should == {
|
101
|
+
'id' => 1,
|
102
|
+
'description' => ['Some description', 'Some description'],
|
103
|
+
'contacts' => [
|
104
|
+
{
|
105
|
+
'id' => 1,
|
106
|
+
'name' => ['Harold', 'Harold'],
|
107
|
+
'addresses' => [
|
108
|
+
{ 'id' => 2,
|
109
|
+
'address' => ['Sesamstreet 2', 'Sesamstreet 2'] }
|
110
|
+
],
|
111
|
+
'destroyed_addresses' => [
|
112
|
+
{ 'id' => 1,
|
113
|
+
'address' => 'Sesamstreet 1' }
|
114
|
+
]
|
115
|
+
}
|
116
|
+
],
|
117
|
+
'destroyed_contacts' => [
|
118
|
+
{
|
119
|
+
'id' => 2,
|
120
|
+
'name' => 'Peter',
|
121
|
+
'addresses' => [
|
122
|
+
{ 'id' => 1,
|
123
|
+
'address' => 'Sesamstreet 1' },
|
124
|
+
{ 'id' => 2,
|
125
|
+
'address' => 'Sesamstreet 2' }
|
126
|
+
]
|
127
|
+
}
|
128
|
+
]
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should detect created items' do
|
133
|
+
from = {
|
134
|
+
'id' => 1,
|
135
|
+
'description' => 'Some description',
|
136
|
+
'contacts' => [
|
137
|
+
{
|
138
|
+
'id' => 1,
|
139
|
+
'name' => 'Harold',
|
140
|
+
'addresses' => [
|
141
|
+
{ 'id' => 2,
|
142
|
+
'address' => 'Sesamstreet 2' }
|
143
|
+
]
|
144
|
+
}
|
145
|
+
]
|
146
|
+
}
|
147
|
+
|
148
|
+
to = {
|
149
|
+
'id' => 1,
|
150
|
+
'description' => 'Some description',
|
151
|
+
'contacts' => [
|
152
|
+
{
|
153
|
+
'id' => 1,
|
154
|
+
'name' => 'Harold',
|
155
|
+
'addresses' => [
|
156
|
+
{ 'id' => 1,
|
157
|
+
'address' => 'Sesamstreet 1' },
|
158
|
+
{ 'id' => 2,
|
159
|
+
'address' => 'Sesamstreet 2' }
|
160
|
+
]
|
161
|
+
},
|
162
|
+
{
|
163
|
+
'id' => 2,
|
164
|
+
'name' => 'Peter',
|
165
|
+
'addresses' => [
|
166
|
+
{ 'id' => 1,
|
167
|
+
'address' => 'Sesamstreet 1' },
|
168
|
+
{ 'id' => 2,
|
169
|
+
'address' => 'Sesamstreet 2' }
|
170
|
+
]
|
171
|
+
}
|
172
|
+
]
|
173
|
+
}
|
174
|
+
|
175
|
+
Yesterday::Differ.new(from, to).diff.should == {
|
176
|
+
'id' => 1,
|
177
|
+
'description' => ['Some description', 'Some description'],
|
178
|
+
'contacts' => [
|
179
|
+
{
|
180
|
+
'id' => 1,
|
181
|
+
'name' => ['Harold', 'Harold'],
|
182
|
+
'addresses' => [
|
183
|
+
{ 'id' => 2,
|
184
|
+
'address' => ['Sesamstreet 2', 'Sesamstreet 2'] }
|
185
|
+
],
|
186
|
+
'created_addresses' => [
|
187
|
+
{ 'id' => 1,
|
188
|
+
'address' => 'Sesamstreet 1' }
|
189
|
+
]
|
190
|
+
}
|
191
|
+
],
|
192
|
+
'created_contacts' => [
|
193
|
+
{
|
194
|
+
'id' => 2,
|
195
|
+
'name' => 'Peter',
|
196
|
+
'addresses' => [
|
197
|
+
{ 'id' => 1,
|
198
|
+
'address' => 'Sesamstreet 1' },
|
199
|
+
{ 'id' => 2,
|
200
|
+
'address' => 'Sesamstreet 2' }
|
201
|
+
]
|
202
|
+
}
|
203
|
+
]
|
204
|
+
}
|
205
|
+
end
|
206
|
+
end
|
data/spec/models.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
class Contact < ActiveRecord::Base
|
2
|
+
has_and_belongs_to_many :addresses
|
3
|
+
tracks_changes
|
4
|
+
end
|
5
|
+
|
6
|
+
class Address < ActiveRecord::Base
|
7
|
+
end
|
8
|
+
|
9
|
+
class Company < ActiveRecord::Base
|
10
|
+
tracks_changes
|
11
|
+
exclude_tracking_for :attributes => [:created_at, :updated_at]
|
12
|
+
end
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 1) do
|
2
|
+
|
3
|
+
create_table 'contacts', :force => true do |t|
|
4
|
+
t.string 'first_name'
|
5
|
+
t.string 'middle_name'
|
6
|
+
t.string 'last_name'
|
7
|
+
t.datetime 'created_at'
|
8
|
+
t.datetime 'updated_at'
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table 'addresses', :force => true do |t|
|
12
|
+
t.string 'street'
|
13
|
+
t.string 'house_number'
|
14
|
+
t.string 'zipcode'
|
15
|
+
t.string 'city'
|
16
|
+
t.string 'country'
|
17
|
+
t.datetime 'created_at'
|
18
|
+
t.datetime 'updated_at'
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table 'addresses_contacts', :id => false, :force => true do |t|
|
22
|
+
t.integer 'contact_id'
|
23
|
+
t.integer 'address_id'
|
24
|
+
end
|
25
|
+
|
26
|
+
create_table 'changesets', :force => true do |t|
|
27
|
+
t.integer 'changed_object_id'
|
28
|
+
t.string 'changed_object_type'
|
29
|
+
t.text 'object_attributes'
|
30
|
+
t.integer 'version_number'
|
31
|
+
t.datetime 'created_at'
|
32
|
+
t.datetime 'updated_at'
|
33
|
+
end
|
34
|
+
|
35
|
+
create_table 'companies', :force => true do |t|
|
36
|
+
t.string 'name'
|
37
|
+
t.datetime 'created_at'
|
38
|
+
t.datetime 'updated_at'
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'yesterday'
|
2
|
+
|
3
|
+
describe Yesterday::Serializer do
|
4
|
+
it 'should serialize an activerecord object to a hash' do
|
5
|
+
contact = Contact.create(:first_name => 'foo', :last_name => 'bar')
|
6
|
+
|
7
|
+
address1 = Address.create(
|
8
|
+
:street => 'street',
|
9
|
+
:house_number => 1,
|
10
|
+
:zipcode => '1234AA',
|
11
|
+
:city => 'Armadillo',
|
12
|
+
:country => 'FooCountry'
|
13
|
+
)
|
14
|
+
|
15
|
+
address2 = Address.create(
|
16
|
+
:street => 'lane',
|
17
|
+
:house_number => 1337,
|
18
|
+
:zipcode => '2211AB',
|
19
|
+
:city => 'Cougar town',
|
20
|
+
:country => 'BarCountry'
|
21
|
+
)
|
22
|
+
|
23
|
+
contact.addresses << address1
|
24
|
+
contact.addresses << address2
|
25
|
+
|
26
|
+
Yesterday::Serializer.new(contact).to_hash.should == {
|
27
|
+
'id' => contact.id,
|
28
|
+
'first_name' => 'foo',
|
29
|
+
'middle_name' => nil,
|
30
|
+
'last_name' => 'bar',
|
31
|
+
'created_at' => contact.created_at,
|
32
|
+
'updated_at' => contact.updated_at,
|
33
|
+
'addresses' => [
|
34
|
+
{
|
35
|
+
'id' => address1.id,
|
36
|
+
'street' => 'street',
|
37
|
+
'house_number' => 1,
|
38
|
+
'zipcode' => '1234AA',
|
39
|
+
'city' => 'Armadillo',
|
40
|
+
'country' => 'FooCountry',
|
41
|
+
'created_at' => address1.created_at,
|
42
|
+
'updated_at' => address1.updated_at
|
43
|
+
},
|
44
|
+
{
|
45
|
+
'id' => address2.id,
|
46
|
+
'street' => 'lane',
|
47
|
+
'house_number' => 1337,
|
48
|
+
'zipcode' => '2211AB',
|
49
|
+
'city' => 'Cougar town',
|
50
|
+
'country' => 'BarCountry',
|
51
|
+
'created_at' => address2.created_at,
|
52
|
+
'updated_at' => address2.updated_at
|
53
|
+
}
|
54
|
+
]
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should not serialize every attribute or association if exclude_tracking_for is used' do
|
59
|
+
company = Company.create :name => 'foobar'
|
60
|
+
|
61
|
+
Yesterday::Serializer.new(company).to_hash.should == {
|
62
|
+
'id' => company.id,
|
63
|
+
'name' => 'foobar'
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_record'
|
5
|
+
require 'yaml'
|
6
|
+
require 'rspec'
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.before(:each) do
|
10
|
+
Contact.delete_all
|
11
|
+
Address.delete_all
|
12
|
+
Yesterday::Changeset.delete_all
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
$enabled = false
|
17
|
+
|
18
|
+
ActiveSupport::Notifications.subscribe(/sql/i) do |*args|
|
19
|
+
if $enabled
|
20
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
21
|
+
puts "SQL #{event.duration}: #{event.payload[:sql]}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect(environment)
|
26
|
+
conf = YAML::load(File.open(File.dirname(__FILE__) + '/database.yml'))
|
27
|
+
ActiveRecord::Base.establish_connection(conf[environment])
|
28
|
+
end
|
29
|
+
|
30
|
+
# Open ActiveRecord connection
|
31
|
+
connect('test')
|
32
|
+
|
33
|
+
original_stdout = $stdout
|
34
|
+
$stdout = StringIO.new
|
35
|
+
|
36
|
+
begin
|
37
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
38
|
+
ensure
|
39
|
+
$stdout = original_stdout
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'yesterday'
|
2
|
+
|
3
|
+
describe Yesterday::VersionedObjectCreator do
|
4
|
+
it 'should deserialize a hash into a structured set of historical item classes' do
|
5
|
+
from = {
|
6
|
+
'id' => 1,
|
7
|
+
'description' => ['Old description', 'New description'],
|
8
|
+
'contacts' => [
|
9
|
+
{
|
10
|
+
'id' => 1,
|
11
|
+
'name' => ['Peter', 'Harold'],
|
12
|
+
'addresses' => [
|
13
|
+
{ 'id' => 1,
|
14
|
+
'address' => ['Sesamstreet 1', 'Sesamstreet 1'] },
|
15
|
+
{ 'id' => 2,
|
16
|
+
'address' => ['Sesamstreet 2', 'Sesamstreet 2'] }
|
17
|
+
]
|
18
|
+
}
|
19
|
+
]
|
20
|
+
}
|
21
|
+
|
22
|
+
object = Yesterday::VersionedObjectCreator.new(from).to_object
|
23
|
+
|
24
|
+
require 'pp'
|
25
|
+
|
26
|
+
object.description.current.should == 'New description'
|
27
|
+
object.id.should == 1
|
28
|
+
|
29
|
+
object.contacts.should be_a(Array)
|
30
|
+
object.contacts.size.should == 1
|
31
|
+
|
32
|
+
object.contacts.first.id.should == 1
|
33
|
+
object.contacts.first.name.to_s.should == 'Harold'
|
34
|
+
object.contacts.first.name.previous.should == 'Peter'
|
35
|
+
object.contacts.first.addresses.size.should == 2
|
36
|
+
object.contacts.first.addresses.first.address.previous.should == 'Sesamstreet 1'
|
37
|
+
object.contacts.first.addresses.first.address.current.should == 'Sesamstreet 1'
|
38
|
+
object.contacts.first.addresses.last.address.previous.should == 'Sesamstreet 2'
|
39
|
+
object.contacts.first.addresses.last.address.current.should == 'Sesamstreet 2'
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'yesterday'
|
3
|
+
require 'models'
|
4
|
+
|
5
|
+
describe Yesterday::Model do
|
6
|
+
|
7
|
+
it 'should have mixed in yesterday::model' do
|
8
|
+
Contact.new.should respond_to :version_number
|
9
|
+
Address.new.should_not respond_to :version_number
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should create a new changeset when saved' do
|
13
|
+
Yesterday::Changeset.count.should == 0
|
14
|
+
Contact.create :first_name => 'foo', :last_name => 'bar'
|
15
|
+
Yesterday::Changeset.count.should == 1
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should return all related changesets' do
|
19
|
+
contact = Contact.create(:first_name => 'foo', :last_name => 'bar')
|
20
|
+
contact.changesets.size.should == 1
|
21
|
+
contact.changesets.first.changed_object_type.should == 'Contact'
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'returning version numbers' do
|
25
|
+
it 'should return version number when new record' do
|
26
|
+
contact = Contact.new(:first_name => 'foo', :last_name => 'bar')
|
27
|
+
contact.version_number.should == 0
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should return version number after first save' do
|
31
|
+
contact = Contact.create(:first_name => 'foo', :last_name => 'bar')
|
32
|
+
contact.version_number.should == 1
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should return version number after update' do
|
36
|
+
contact = Contact.create(:first_name => 'foo', :last_name => 'bar')
|
37
|
+
contact.last_name = 'baz'
|
38
|
+
contact.save!
|
39
|
+
|
40
|
+
contact.version_number.should == 2
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe' returning older versions' do
|
45
|
+
it 'should not return and old version when there is no history' do
|
46
|
+
Contact.new
|
47
|
+
Contact.version(0).should == nil
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return a historical item' do
|
51
|
+
contact = Contact.create!(:first_name => 'foo', :last_name => 'bar')
|
52
|
+
Contact.where(:id => contact.id).version(0).should == nil
|
53
|
+
Contact.where(:id => contact.id).version(1).should be_a(Yesterday::VersionedObject)
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'return a historical view of a version' do
|
57
|
+
before do
|
58
|
+
@contact = Contact.create!(:first_name => 'foo', :last_name => 'bar')
|
59
|
+
@contact.update_attributes :first_name => 'john', :last_name => 'dough'
|
60
|
+
@contact.update_attributes :first_name => 'ronald', :last_name => 'mcdonald'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'using scopes' do
|
64
|
+
@contact.version_number.should == 3
|
65
|
+
|
66
|
+
Contact.where(:id => @contact.id).version(2).first_name.should == 'john'
|
67
|
+
Contact.where(:id => @contact.id).version(2).last_name.should == 'dough'
|
68
|
+
|
69
|
+
Contact.where(:id => @contact.id).version(1).first_name.should == 'foo'
|
70
|
+
Contact.where(:id => @contact.id).version(1).last_name.should == 'bar'
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'using instance method' do
|
74
|
+
@contact.version(2).first_name.should == 'john'
|
75
|
+
@contact.version(2).last_name.should == 'dough'
|
76
|
+
|
77
|
+
@contact.version(1).first_name.should == 'foo'
|
78
|
+
@contact.version(1).last_name.should == 'bar'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'diffing two versions' do
|
84
|
+
it 'should diff version 1 with version 2' do
|
85
|
+
contact = Contact.create(:first_name => 'foo', :last_name => 'bar')
|
86
|
+
contact.update_attributes :first_name => 'john', :last_name => 'do'
|
87
|
+
|
88
|
+
diff = contact.diff_version(1, 2)
|
89
|
+
diff.should be_a(Yesterday::VersionedObject)
|
90
|
+
|
91
|
+
diff.first_name.current.should == 'john'
|
92
|
+
diff.first_name.previous.should == 'foo'
|
93
|
+
|
94
|
+
diff.last_name.current.should == 'do'
|
95
|
+
diff.last_name.previous.should == 'bar'
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should diff version 1 with version 3' do
|
99
|
+
contact = Contact.create(:first_name => 'foo', :last_name => 'bar')
|
100
|
+
contact.update_attributes :first_name => 'john', :last_name => 'do'
|
101
|
+
contact.update_attributes :first_name => 'edward', :last_name => 'foo'
|
102
|
+
|
103
|
+
diff = contact.diff_version(1, 3)
|
104
|
+
diff.should be_a(Yesterday::VersionedObject)
|
105
|
+
|
106
|
+
diff.first_name.current.should == 'edward'
|
107
|
+
diff.first_name.previous.should == 'foo'
|
108
|
+
|
109
|
+
diff.last_name.current.should == 'foo'
|
110
|
+
diff.last_name.previous.should == 'bar'
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should detect that new associations are created' do
|
114
|
+
contact = Contact.create(:first_name => 'foo', :last_name => 'bar')
|
115
|
+
|
116
|
+
contact.version_number.should == 1
|
117
|
+
|
118
|
+
address1 = Address.create(
|
119
|
+
:street => 'street',
|
120
|
+
:house_number => 1,
|
121
|
+
:zipcode => '1234AA',
|
122
|
+
:city => 'Armadillo',
|
123
|
+
:country => 'FooCountry'
|
124
|
+
)
|
125
|
+
|
126
|
+
address2 = Address.create(
|
127
|
+
:street => 'lane',
|
128
|
+
:house_number => 1337,
|
129
|
+
:zipcode => '2211AB',
|
130
|
+
:city => 'Cougar town',
|
131
|
+
:country => 'BarCountry'
|
132
|
+
)
|
133
|
+
|
134
|
+
contact.addresses = [address1, address2]
|
135
|
+
contact.save!
|
136
|
+
|
137
|
+
contact.version_number.should == 2
|
138
|
+
|
139
|
+
diff = contact.diff_version(1, 2)
|
140
|
+
diff.created_addresses.count.should == 2
|
141
|
+
diff.created_addresses.first.id.should == address1.id
|
142
|
+
diff.created_addresses.last.id.should == address2.id
|
143
|
+
end
|
144
|
+
|
145
|
+
describe 'association removal' do
|
146
|
+
before do
|
147
|
+
@contact = Contact.create(:first_name => 'foo', :last_name => 'bar')
|
148
|
+
|
149
|
+
@address1 = Address.create(
|
150
|
+
:street => 'street',
|
151
|
+
:house_number => 1,
|
152
|
+
:zipcode => '1234AA',
|
153
|
+
:city => 'Armadillo',
|
154
|
+
:country => 'FooCountry'
|
155
|
+
)
|
156
|
+
|
157
|
+
@address2 = Address.create(
|
158
|
+
:street => 'lane',
|
159
|
+
:house_number => 1337,
|
160
|
+
:zipcode => '2211AB',
|
161
|
+
:city => 'Cougar town',
|
162
|
+
:country => 'BarCountry'
|
163
|
+
)
|
164
|
+
|
165
|
+
@contact.addresses = [@address1, @address2]
|
166
|
+
@contact.save!
|
167
|
+
|
168
|
+
@contact.addresses = [@address1]
|
169
|
+
@address2.destroy
|
170
|
+
|
171
|
+
@contact.save!
|
172
|
+
|
173
|
+
@contact.version_number.should == 3
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should detect that associations are destroyed when diffing version 2 with 3' do
|
177
|
+
|
178
|
+
diff = @contact.diff_version(2, 3)
|
179
|
+
diff.addresses.count.should == 1
|
180
|
+
diff.destroyed_addresses.count.should == 1
|
181
|
+
|
182
|
+
diff.destroyed_addresses.first.id.should == @address2.id
|
183
|
+
diff.addresses.first.id.should == @address1.id
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should detect that associations are created when diffing version 1 with 3' do
|
187
|
+
diff = @contact.diff_version(1, 3)
|
188
|
+
diff.should_not respond_to(:addresses)
|
189
|
+
diff.created_addresses.count.should == 1
|
190
|
+
diff.created_addresses.first.id.should == @address1.id
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
data/yesterday.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "yesterday/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "yesterday"
|
7
|
+
s.version = Yesterday::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Diederick Lawson"]
|
10
|
+
s.email = ["webmaster@altovista.nl"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = "Track history in your active record models"
|
13
|
+
s.description = "Track history and view any made changes in your active record models"
|
14
|
+
|
15
|
+
s.rubyforge_project = "yesterday"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency 'rspec'
|
23
|
+
s.add_development_dependency 'sqlite3'
|
24
|
+
s.add_dependency 'rails'
|
25
|
+
end
|