memento 0.3.7 → 0.4.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.
- data/.rbenv-version +1 -1
- data/CHANGES.md +10 -1
- data/README.rdoc +9 -9
- data/generators/memento_migration/templates/migration.rb +4 -4
- data/lib/memento.rb +72 -54
- data/lib/memento/action.rb +7 -7
- data/lib/memento/action/create.rb +35 -33
- data/lib/memento/action/destroy.rb +26 -25
- data/lib/memento/action/update.rb +49 -48
- data/lib/memento/action_controller_methods.rb +3 -3
- data/lib/memento/active_record_methods.rb +21 -21
- data/lib/memento/result.rb +35 -34
- data/lib/memento/session.rb +25 -24
- data/lib/memento/state.rb +45 -44
- data/lib/memento/version.rb +2 -2
- data/memento.gemspec +2 -2
- data/spec/memento/action/create_spec.rb +14 -14
- data/spec/memento/action/destroy_spec.rb +9 -9
- data/spec/memento/action/update_spec.rb +34 -34
- data/spec/memento/action_controller_methods_spec.rb +9 -9
- data/spec/memento/active_record_methods_spec.rb +17 -17
- data/spec/memento/result_spec.rb +12 -12
- data/spec/memento/session_spec.rb +21 -21
- data/spec/memento/state_spec.rb +12 -12
- data/spec/memento_spec.rb +109 -68
- data/spec/spec_helper.rb +9 -9
- metadata +2 -2
@@ -9,19 +9,19 @@ describe Memento::Action::Update do
|
|
9
9
|
@project = Project.create!(:name => "P1", :closed_at => @time1, :notes => "Bla bla").reload
|
10
10
|
@customer = Customer.create!(:name => "C1")
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
after do
|
14
14
|
shutdown_db
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
describe "when object gets updated" do
|
18
|
-
|
18
|
+
|
19
19
|
before do
|
20
|
-
Memento
|
20
|
+
Memento(@user) do
|
21
21
|
@project.update_attributes(:name => "P2", :closed_at => @time2, :customer => @customer, :notes => "Bla bla")
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
it "should create memento_state for ar-object from changes_for_memento" do
|
26
26
|
Memento::State.count.should eql(1)
|
27
27
|
Memento::State.first.action_type.should eql("update")
|
@@ -32,7 +32,7 @@ describe Memento::Action::Update do
|
|
32
32
|
Memento::State.first.record_data["closed_at"][0].utc.to_s.should eql(@time1.utc.to_s)
|
33
33
|
Memento::State.first.record_data["closed_at"][1].utc.to_s.should eql(@time2.utc.to_s)
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
it "should update object" do
|
37
37
|
@project.reload.name.should eql("P2")
|
38
38
|
@project.customer.should eql(@customer)
|
@@ -40,7 +40,7 @@ describe Memento::Action::Update do
|
|
40
40
|
@project.should_not be_changed
|
41
41
|
Project.count.should eql(1)
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
it "should allow undoing the update" do
|
45
45
|
undone = Memento::Session.last.undo.first
|
46
46
|
undone.should be_success
|
@@ -49,12 +49,12 @@ describe Memento::Action::Update do
|
|
49
49
|
undone.object.customer.should be_nil
|
50
50
|
undone.object.closed_at.to_s.should eql(@time1.to_s)
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
describe "when record was destroyed before undo" do
|
54
54
|
before do
|
55
55
|
@project.destroy
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
it "should return fake object with error" do
|
59
59
|
undone = Memento::Session.last.undo.first
|
60
60
|
undone.should_not be_success
|
@@ -63,20 +63,20 @@ describe Memento::Action::Update do
|
|
63
63
|
undone.object.id.should eql(1)
|
64
64
|
end
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
describe "when record was changed before undo" do
|
68
|
-
|
68
|
+
|
69
69
|
describe "with mergeable unrecorded changes" do
|
70
70
|
before do
|
71
71
|
@project.update_attributes({:notes => "Bla!"})
|
72
72
|
@result = Memento::Session.first.undo.first
|
73
73
|
@object = @result.object
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
it "should be success" do
|
77
77
|
@result.should be_success
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
it "should return correctly updated object" do
|
81
81
|
@object.class.should eql(Project)
|
82
82
|
@object.name.should eql("P1")
|
@@ -85,21 +85,21 @@ describe Memento::Action::Update do
|
|
85
85
|
@object.notes.should eql("Bla!")
|
86
86
|
end
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
describe "with mergeable recorded changes" do
|
90
90
|
before do
|
91
|
-
Memento
|
91
|
+
Memento(@user) do
|
92
92
|
@project.update_attributes({:notes => "Bla\nBla!"})
|
93
93
|
end
|
94
94
|
Memento::State.last.update_attributes(:created_at => 1.minute.from_now)
|
95
95
|
@result = Memento::Session.first.undo.first
|
96
96
|
@object = @result.object
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
it "should be success" do
|
100
100
|
@result.should be_success
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
it "should return correctly updated object" do
|
104
104
|
@object.class.should eql(Project)
|
105
105
|
@object.name.should eql("P1")
|
@@ -107,13 +107,13 @@ describe Memento::Action::Update do
|
|
107
107
|
@object.closed_at.to_s.should eql(@time1.to_s)
|
108
108
|
@object.notes.should eql("Bla\nBla!")
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
111
|
describe "when second state is undone" do
|
112
112
|
before do
|
113
113
|
@result = Memento::Session.first.undo.first
|
114
114
|
@object = @result.object
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
it "should be success" do
|
118
118
|
@result.should be_success
|
119
119
|
end
|
@@ -138,11 +138,11 @@ describe Memento::Action::Update do
|
|
138
138
|
it "should fail" do
|
139
139
|
@result.should be_failed
|
140
140
|
end
|
141
|
-
|
141
|
+
|
142
142
|
it "should set error" do
|
143
143
|
@result.error.first.should be_was_changed
|
144
144
|
end
|
145
|
-
|
145
|
+
|
146
146
|
it "should return not undone object" do
|
147
147
|
@object.name.should eql("P3")
|
148
148
|
@object.customer.should eql(@customer)
|
@@ -150,34 +150,34 @@ describe Memento::Action::Update do
|
|
150
150
|
@object.should_not be_changed
|
151
151
|
end
|
152
152
|
end
|
153
|
-
|
153
|
+
|
154
154
|
describe "with unmergeable recorded changes" do
|
155
155
|
before do
|
156
|
-
Memento
|
156
|
+
Memento(@user) do
|
157
157
|
@project.update_attributes!({:name => "P3"})
|
158
158
|
end
|
159
159
|
Memento::State.last.update_attributes(:created_at => 1.minute.from_now)
|
160
160
|
@result = Memento::Session.first.undo.first
|
161
161
|
@object = @result.object
|
162
162
|
end
|
163
|
-
|
163
|
+
|
164
164
|
it "should fail" do
|
165
165
|
@result.should be_failed
|
166
166
|
end
|
167
|
-
|
167
|
+
|
168
168
|
it "should return not undone object" do
|
169
169
|
@object.name.should eql("P3")
|
170
170
|
@object.customer.should eql(@customer)
|
171
171
|
@object.closed_at.to_s.should eql(@time2.to_s)
|
172
172
|
@object.should_not be_changed
|
173
173
|
end
|
174
|
-
|
174
|
+
|
175
175
|
describe "when second state is undone" do
|
176
176
|
before do
|
177
177
|
@result = Memento::Session.last.undo.first
|
178
178
|
@object = @result.object
|
179
179
|
end
|
180
|
-
|
180
|
+
|
181
181
|
it "should be success" do
|
182
182
|
@result.should be_success
|
183
183
|
end
|
@@ -192,22 +192,22 @@ describe Memento::Action::Update do
|
|
192
192
|
end
|
193
193
|
end
|
194
194
|
end
|
195
|
-
|
195
|
+
|
196
196
|
end
|
197
|
-
|
197
|
+
|
198
198
|
describe "when object gets updated with no changes" do
|
199
|
-
|
199
|
+
|
200
200
|
before do
|
201
|
-
Memento
|
201
|
+
Memento(@user) do
|
202
202
|
@project.update_attributes(:name => "P1", :customer => nil, :notes => "Bla bla")
|
203
203
|
end
|
204
204
|
end
|
205
|
-
|
205
|
+
|
206
206
|
it "should not create session/state" do
|
207
207
|
Memento::Session.count.should eql(0)
|
208
208
|
Memento::State.count.should eql(0)
|
209
209
|
end
|
210
|
-
|
210
|
+
|
211
211
|
end
|
212
|
-
|
212
|
+
|
213
213
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
2
|
|
3
3
|
class FooController < ActionController::Base
|
4
|
-
|
4
|
+
|
5
5
|
end
|
6
6
|
|
7
7
|
describe Memento::ActionControllerMethods do
|
8
|
-
|
8
|
+
|
9
9
|
before do
|
10
10
|
setup_db
|
11
11
|
setup_data
|
@@ -14,15 +14,15 @@ describe Memento::ActionControllerMethods do
|
|
14
14
|
@headers = {}
|
15
15
|
@controller.stub!(:response).and_return(mock("response", :headers => @headers))
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
after do
|
19
19
|
shutdown_db
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
it "should add memento-method to ActionController::Base" do
|
23
23
|
FooController.private_instance_methods.map(&:to_sym).should include(:memento)
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
it "should call memento#memento with user and block" do
|
27
27
|
project = Project.create!
|
28
28
|
@controller.send(:memento) do
|
@@ -32,22 +32,22 @@ describe Memento::ActionControllerMethods do
|
|
32
32
|
project.memento_states.count.should eql(1)
|
33
33
|
Memento::Session.count.should eql(1)
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
it "should set header X-MementoSessionId" do
|
37
37
|
@controller.send(:memento) { Project.create!.update_attributes(:name => "P7") }
|
38
38
|
@headers.should == {'X-Memento-Session-Id' => Memento::Session.last.id.to_s }
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
it "should return result of given block" do
|
42
42
|
@controller.send(:memento) do
|
43
43
|
1 + 2
|
44
44
|
end.should eql(3)
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
it "should not set header when no session stored" do
|
48
48
|
@controller.send(:memento) { Customer.create! } # not stored
|
49
49
|
@headers['X-MementoSessionId'].should be_nil
|
50
50
|
@headers.should_not have_key('X-Memento-Session-Id')
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
end
|
@@ -1,44 +1,44 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
2
|
|
3
3
|
describe Memento::ActiveRecordMethods do
|
4
|
-
|
4
|
+
|
5
5
|
before do
|
6
6
|
setup_db
|
7
7
|
setup_data
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
it "should declare protected methods on Project" do
|
11
11
|
Project.private_instance_methods.map(&:to_sym).should include(:record_destroy, :record_update, :record_create)
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
it "should set hook on create to call Memento" do
|
15
15
|
project = Project.new(:name => "Project X")
|
16
|
-
Memento.
|
16
|
+
Memento.should_receive(:add_state).once().with("create", project)
|
17
17
|
project.save!
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
it "should set hook on update to call Memento" do
|
21
21
|
project = Project.create!(:name => "Project X")
|
22
|
-
Memento.
|
22
|
+
Memento.should_receive(:add_state).once().with("update", project)
|
23
23
|
project.update_attributes(:name => "Project XY")
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
it "should set hook on destroy to call Memento" do
|
27
27
|
project = Project.create!(:name => "Project X")
|
28
|
-
Memento.
|
28
|
+
Memento.should_receive(:add_state).once().with("destroy", project)
|
29
29
|
project.destroy
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
it "should define attributes_for_memento and ignore attributes given by options" do
|
33
33
|
Project.create!(:name => "Project X").attributes_for_memento.should == {
|
34
34
|
"id"=>1, "name"=>"Project X", "notes"=>nil, "customer_id"=>nil, "closed_at"=>nil
|
35
35
|
}
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
it "should set memento_options" do
|
39
39
|
Project.memento_options.should eql({:ignore=>[:ignore_this]})
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
it "should define changes_for_memento and ignore attributes given by options" do
|
43
43
|
project = Project.create!(:name => "Project X")
|
44
44
|
project.name = "A Project"
|
@@ -48,22 +48,22 @@ describe Memento::ActiveRecordMethods do
|
|
48
48
|
project.changes_for_memento.should_not == project.changes
|
49
49
|
project.changes_for_memento.should == {"name"=>["Project X", "A Project"], "notes"=>[nil, "new"]}
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
it "should define has_many association to memento_states" do
|
53
53
|
project = Project.create!(:name => "Project X")
|
54
54
|
project.memento_states.should be_empty
|
55
|
-
Memento
|
55
|
+
Memento(@user) { project.update_attributes(:name => "Project Y") }
|
56
56
|
project.memento_states.count.should eql(1)
|
57
|
-
Memento
|
57
|
+
Memento(@user) { project.update_attributes(:name => "Project Y") }
|
58
58
|
project.memento_states.count.should eql(1)
|
59
|
-
Memento
|
59
|
+
Memento(@user) { Project.create!.update_attributes(:name => "Project X") }
|
60
60
|
project.memento_states.count.should eql(1)
|
61
61
|
Project.last.memento_states.count.should eql(2)
|
62
62
|
Memento::State.count.should eql(3)
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
after do
|
66
66
|
shutdown_db
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
end
|
data/spec/memento/result_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
2
|
|
3
3
|
describe Memento::Result do
|
4
|
-
|
4
|
+
|
5
5
|
describe "when initalized with valid object" do
|
6
6
|
before do
|
7
7
|
@object = mock("object", :errors => {})
|
@@ -12,7 +12,7 @@ describe Memento::Result do
|
|
12
12
|
it "should have an object attribute" do
|
13
13
|
@result.object.should eql(@object)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
it "should have an state attribute" do
|
17
17
|
@result.state.should eql(@state)
|
18
18
|
end
|
@@ -26,13 +26,13 @@ describe Memento::Result do
|
|
26
26
|
@result.should_not be_failed
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
describe "when initalized with object with errors" do
|
31
31
|
before do
|
32
32
|
@object = mock("object", :errors => {:memento_undo => "123"})
|
33
33
|
@result = Memento::Result.new(@object, mock("state1"))
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
it "should have an object attribute" do
|
37
37
|
@result.object.should eql(@object)
|
38
38
|
end
|
@@ -50,40 +50,40 @@ describe Memento::Result do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
describe Memento::ResultArray do
|
53
|
-
|
53
|
+
|
54
54
|
before do
|
55
55
|
@results = Memento::ResultArray.new()
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
it "should have an empty errors array" do
|
59
59
|
@results.errors.should eql([])
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
it "should have no errors" do
|
63
63
|
@results.should be_success
|
64
64
|
@results.should_not be_failed
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
describe "when Memento::Result without errors added" do
|
68
68
|
before do
|
69
69
|
@object = mock("object", :errors => {:memento_undo => "123"})
|
70
70
|
@results << Memento::Result.new(mock("object2", :errors => {}), mock("state1"))
|
71
71
|
@results << (@with_error = Memento::Result.new(@object, mock("state2")))
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
it "should have two entrys" do
|
75
75
|
@results.size.should eql(2)
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
it "should have one error" do
|
79
79
|
@results.errors.size.should eql(1)
|
80
80
|
@results.errors.should eql([@with_error])
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
it "should have an error" do
|
84
84
|
@results.should_not be_success
|
85
85
|
@results.should be_failed
|
86
86
|
end
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
end
|
@@ -1,50 +1,50 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
2
|
|
3
3
|
describe Memento::Session do
|
4
|
-
|
4
|
+
|
5
5
|
before do
|
6
6
|
setup_db
|
7
7
|
setup_data
|
8
8
|
@session = Memento::Session.create(:user => @user)
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
it "should belong to user" do
|
12
12
|
@session.user.should eql(@user)
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
it "should require user" do
|
16
16
|
Memento::Session.create.errors[:user].should eql(["can't be blank"])
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
it "should have_many states" do
|
20
20
|
@session.states.should eql([])
|
21
21
|
@session.states.create!(:action_type => "destroy", :record => Project.create!)
|
22
22
|
@session.states.count.should eql(1)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
context "undo" do
|
26
26
|
before do
|
27
27
|
@state1 = @session.states.create!(:action_type => "update", :record => @p1 = Project.create!)
|
28
28
|
@other = Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => Project.create!)
|
29
29
|
@state2 = @session.states.create!(:action_type => "update", :record => @p2 = Project.create!)
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
describe "and all states succeed" do
|
33
33
|
it "should return ResultsArray" do
|
34
34
|
@session.undo.should be_a(Memento::ResultArray)
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
it "should remove all states" do
|
38
38
|
@session.undo
|
39
39
|
Memento::State.count.should eql(1)
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
it "should remove itself" do
|
43
43
|
@session.undo
|
44
44
|
Memento::Session.find_by_id(@session.id).should be_nil
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
describe "and all states fail" do
|
49
49
|
before do
|
50
50
|
@state1.update_attributes(:record_data => {:name => ["A", "B"]})
|
@@ -52,22 +52,22 @@ describe Memento::Session do
|
|
52
52
|
@state2.update_attributes(:record_data => {:name => ["A", "B"]})
|
53
53
|
@p2.update_attributes(:name => "C")
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
it "should keep all states" do
|
57
57
|
@session.undo
|
58
58
|
Memento::State.count.should eql(3)
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
it "should keep itself" do
|
62
62
|
@session.undo
|
63
63
|
@session.reload
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
it "should raise Memento::ErrorOnRewind on undo!" do
|
67
67
|
lambda{ @session.undo! }.should raise_error(Memento::ErrorOnRewind)
|
68
68
|
end
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
describe "and some states succeed, some fail" do
|
72
72
|
before do
|
73
73
|
@state1.update_attributes(:record_data => {:name => ["A", "B"]})
|
@@ -78,7 +78,7 @@ describe Memento::Session do
|
|
78
78
|
@session.undo! rescue
|
79
79
|
Memento::State.count.should eql(3)
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
it "should NOT keep all states when using undo" do
|
83
83
|
@session.undo
|
84
84
|
Memento::State.count.should eql(2)
|
@@ -88,34 +88,34 @@ describe Memento::Session do
|
|
88
88
|
@session.undo rescue nil
|
89
89
|
@session.reload
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
it "should raise Memento::ErrorOnRewind on undo!" do
|
93
93
|
lambda{ @session.undo! }.should raise_error(Memento::ErrorOnRewind)
|
94
94
|
end
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
it "should undo states in reverse order of creation (newest first)" do
|
98
98
|
@session.undo.map(&:state).map(&:id).should eql([@state2.id, @state1.id])
|
99
99
|
end
|
100
100
|
end
|
101
|
-
|
101
|
+
|
102
102
|
describe "with states" do
|
103
103
|
before do
|
104
104
|
@session.states.create!(:action_type => "destroy", :record => Project.create!)
|
105
105
|
Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => Project.create!)
|
106
106
|
@state2 = @session.states.create!(:action_type => "update", :record => Project.create!)
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
it "should destroy all states when destroyed" do
|
110
110
|
Memento::State.count.should eql(3)
|
111
111
|
@session.destroy
|
112
112
|
Memento::State.count.should eql(1)
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
after do
|
118
118
|
shutdown_db
|
119
119
|
end
|
120
|
-
|
120
|
+
|
121
121
|
end
|