memento 0.3.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.
@@ -0,0 +1,55 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ class FooController < ActionController::Base
4
+
5
+ private
6
+
7
+ end
8
+
9
+ describe Memento::ActionControllerMethods do
10
+
11
+ before do
12
+ setup_db
13
+ setup_data
14
+ @controller = FooController.new
15
+ @controller.stub!(:current_user).and_return(@user)
16
+ @headers = {}
17
+ @controller.stub!(:response).and_return(mock("response", :headers => @headers))
18
+ end
19
+
20
+ after do
21
+ shutdown_db
22
+ end
23
+
24
+ it "should add memento-method to ActionController::Base" do
25
+ FooController.private_instance_methods.should include("memento")
26
+ end
27
+
28
+ it "should call memento#memento with user and block" do
29
+ project = Project.create!
30
+ @controller.send(:memento) do
31
+ project.update_attribute(:name, "P7")
32
+ end
33
+ project.reload.name.should eql("P7")
34
+ project.memento_states.count.should eql(1)
35
+ Memento::Session.count.should eql(1)
36
+ end
37
+
38
+ it "should set header X-MementoSessionId" do
39
+ @controller.send(:memento) { Project.create!.update_attribute(:name, "P7") }
40
+ @headers.should == {'X-Memento-Session-Id' => Memento::Session.last.id.to_s }
41
+ end
42
+
43
+ it "should return result of given block" do
44
+ @controller.send(:memento) do
45
+ 1 + 2
46
+ end.should eql(3)
47
+ end
48
+
49
+ it "should not set header when no session stored" do
50
+ @controller.send(:memento) { Customer.create! } # not stored
51
+ @headers['X-MementoSessionId'].should be_nil
52
+ @headers.should_not have_key('X-Memento-Session-Id')
53
+ end
54
+
55
+ end
@@ -0,0 +1,65 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Memento::ActiveRecordMethods do
4
+
5
+ before do
6
+ setup_db
7
+ setup_data
8
+ end
9
+
10
+ it "should declare private methods on Project" do
11
+ Project.private_instance_methods.should include("record_destroy", "record_update", "record_create")
12
+ end
13
+
14
+ it "should set hook on create to call Memento" do
15
+ project = Project.new(:name => "Project X")
16
+ Memento.instance.should_receive(:add_state).once().with("create", project)
17
+ project.save!
18
+ end
19
+
20
+ it "should set hook on update to call Memento" do
21
+ project = Project.create!(:name => "Project X")
22
+ Memento.instance.should_receive(:add_state).once().with("update", project)
23
+ project.update_attribute(:name, "Project XY")
24
+ end
25
+
26
+ it "should set hook on destroy to call Memento" do
27
+ project = Project.create!(:name => "Project X")
28
+ Memento.instance.should_receive(:add_state).once().with("destroy", project)
29
+ project.destroy
30
+ end
31
+
32
+ it "should define attributes_for_memento and ignore attributes given by options" do
33
+ Project.create!(:name => "Project X").attributes_for_memento.should == {
34
+ "id"=>1, "name"=>"Project X", "notes"=>nil, "customer_id"=>nil, "closed_at"=>nil
35
+ }
36
+ end
37
+
38
+ it "should define changes_for_memento and ignore attributes given by options" do
39
+ project = Project.create!(:name => "Project X")
40
+ project.name = "A Project"
41
+ project.updated_at = 5.minutes.ago
42
+ project.notes = "new"
43
+ project.ignore_this = 2
44
+ project.changes_for_memento.should_not == project.changes
45
+ project.changes_for_memento.should == {"name"=>["Project X", "A Project"], "notes"=>[nil, "new"]}
46
+ end
47
+
48
+ it "should define has_many association to memento_states" do
49
+ project = Project.create!(:name => "Project X")
50
+ project.memento_states.should be_empty
51
+ Memento.instance.memento(@user) { project.update_attribute(:name, "Project Y") }
52
+ project.memento_states.count.should eql(1)
53
+ Memento.instance.memento(@user) { project.update_attribute(:name, "Project Y") }
54
+ project.memento_states.count.should eql(1)
55
+ Memento.instance.memento(@user) { Project.create!.update_attribute(:name, "Project X") }
56
+ project.memento_states.count.should eql(1)
57
+ Project.last.memento_states.count.should eql(2)
58
+ Memento::State.count.should eql(3)
59
+ end
60
+
61
+ after do
62
+ shutdown_db
63
+ end
64
+
65
+ end
@@ -0,0 +1,89 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Memento::Result do
4
+
5
+ describe "when initalized with valid object" do
6
+ before do
7
+ @object = mock("object", :errors => {})
8
+ @state = mock("state1")
9
+ @result = Memento::Result.new(@object, @state)
10
+ end
11
+
12
+ it "should have an object attribute" do
13
+ @result.object.should eql(@object)
14
+ end
15
+
16
+ it "should have an state attribute" do
17
+ @result.state.should eql(@state)
18
+ end
19
+
20
+ it "should have an error attribute" do
21
+ @result.error.should be_nil
22
+ end
23
+
24
+ it "should be valid" do
25
+ @result.should be_success
26
+ @result.should_not be_failed
27
+ end
28
+ end
29
+
30
+ describe "when initalized with object with errors" do
31
+ before do
32
+ @object = mock("object", :errors => {:memento_undo => "123"})
33
+ @result = Memento::Result.new(@object, mock("state1"))
34
+ end
35
+
36
+ it "should have an object attribute" do
37
+ @result.object.should eql(@object)
38
+ end
39
+
40
+ it "should return error" do
41
+ @result.error.should eql("123")
42
+ end
43
+
44
+ it "should be invalid" do
45
+ @result.should be_failed
46
+ @result.should_not be_success
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ describe Memento::ResultArray do
53
+
54
+ before do
55
+ @results = Memento::ResultArray.new()
56
+ end
57
+
58
+ it "should have an empty errors array" do
59
+ @results.errors.should eql([])
60
+ end
61
+
62
+ it "should have no errors" do
63
+ @results.should be_success
64
+ @results.should_not be_failed
65
+ end
66
+
67
+ describe "when Memento::Result without errors added" do
68
+ before do
69
+ @object = mock("object", :errors => {:memento_undo => "123"})
70
+ @results << Memento::Result.new(mock("object2", :errors => {}), mock("state1"))
71
+ @results << (@with_error = Memento::Result.new(@object, mock("state2")))
72
+ end
73
+
74
+ it "should have two entrys" do
75
+ @results.size.should eql(2)
76
+ end
77
+
78
+ it "should have one error" do
79
+ @results.errors.size.should eql(1)
80
+ @results.errors.should eql([@with_error])
81
+ end
82
+
83
+ it "should have an error" do
84
+ @results.should_not be_success
85
+ @results.should be_failed
86
+ end
87
+ end
88
+
89
+ end
@@ -0,0 +1,117 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Memento::Session do
4
+
5
+ before do
6
+ setup_db
7
+ setup_data
8
+ @session = Memento::Session.create(:user => @user)
9
+ end
10
+
11
+ it "should belong to user" do
12
+ @session.user.should eql(@user)
13
+ end
14
+
15
+ it "should require user" do
16
+ Memento::Session.create.errors[:user].should eql("can't be blank")
17
+ end
18
+
19
+ it "should have_many states" do
20
+ @session.states.should eql([])
21
+ @session.states.create!(:action_type => "destroy", :record => Project.create!)
22
+ @session.states.count.should eql(1)
23
+ end
24
+
25
+ context "undo" do
26
+ before do
27
+ @state1 = @session.states.create!(:action_type => "update", :record => @p1 = Project.create!)
28
+ @other = Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => Project.create!)
29
+ @state2 = @session.states.create!(:action_type => "update", :record => @p2 = Project.create!)
30
+ end
31
+
32
+ describe "and all states succeed" do
33
+ it "should return ResultsArray" do
34
+ @session.undo.should be_a(Memento::ResultArray)
35
+ end
36
+
37
+ it "should remove all states" do
38
+ @session.undo
39
+ Memento::State.count.should eql(1)
40
+ end
41
+
42
+ it "should remove itself" do
43
+ @session.undo
44
+ Memento::Session.find_by_id(@session.id).should be_nil
45
+ end
46
+ end
47
+
48
+ describe "and all states fail" do
49
+ before do
50
+ @state1.update_attribute(:record_data, {:name => ["A", "B"]})
51
+ @p1.update_attribute(:name, "C")
52
+ @state2.update_attribute(:record_data, {:name => ["A", "B"]})
53
+ @p2.update_attribute(:name, "C")
54
+ end
55
+
56
+ it "should keep all states" do
57
+ @session.undo
58
+ Memento::State.count.should eql(3)
59
+ end
60
+
61
+ it "should keep itself" do
62
+ @session.undo
63
+ @session.reload
64
+ end
65
+
66
+ it "should raise Memento::ErrorOnRewind on undo!" do
67
+ lambda{ @session.undo! }.should raise_error(Memento::ErrorOnRewind)
68
+ end
69
+ end
70
+
71
+ describe "and some states succeed, some fail" do
72
+ before do
73
+ @state1.update_attribute(:record_data, {:name => ["A", "B"]})
74
+ @p1.update_attribute(:name, "C")
75
+ end
76
+
77
+ it "should keep all states when using undo!" do
78
+ @session.undo! rescue
79
+ Memento::State.count.should eql(3)
80
+ end
81
+
82
+ it "should NOT keep all states when using undo" do
83
+ @session.undo
84
+ Memento::State.count.should eql(2)
85
+ end
86
+
87
+ it "should keep itself" do
88
+ @session.undo rescue nil
89
+ @session.reload
90
+ end
91
+
92
+ it "should raise Memento::ErrorOnRewind on undo!" do
93
+ lambda{ @session.undo! }.should raise_error(Memento::ErrorOnRewind)
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "with states" do
99
+ before do
100
+ @session.states.create!(:action_type => "destroy", :record => Project.create!)
101
+ Memento::Session.create!(:user => @user).states.create!(:action_type => "destroy", :record => Project.create!)
102
+ @state2 = @session.states.create!(:action_type => "update", :record => Project.create!)
103
+ end
104
+
105
+ it "should destroy all states when destroyed" do
106
+ Memento::State.count.should eql(3)
107
+ @session.destroy
108
+ Memento::State.count.should eql(1)
109
+ end
110
+
111
+ end
112
+
113
+ after do
114
+ shutdown_db
115
+ end
116
+
117
+ end
@@ -0,0 +1,55 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Memento::State do
4
+
5
+ before do
6
+ setup_db
7
+ setup_data
8
+ @session = Memento::Session.create(:user => @user)
9
+ end
10
+
11
+ it "should belong to session" do
12
+ Memento::State.new(:session => @session).session.should eql(@session)
13
+ end
14
+
15
+ it "should require session" do
16
+ Memento::State.create.errors[:session].should eql("can't be blank")
17
+ end
18
+
19
+ it "should require action_type to be one of Memento::State::RECORD_CAUSES" do
20
+ Memento::State.create.errors[:action_type].should eql("can't be blank")
21
+ Memento::State.create(:action_type => "move").errors[:action_type].should eql("is not included in the list")
22
+ end
23
+
24
+ it "should belong to polymorphic record" do
25
+ Memento::State.new(:record => @user).record.should eql(@user)
26
+ Memento::State.new(:record => @session).record.should eql(@session)
27
+ end
28
+
29
+ it "should require record" do
30
+ Memento::State.create.errors[:record].should eql("can't be blank")
31
+ end
32
+
33
+
34
+ describe "valid State" do
35
+ before do
36
+ @state = @session.states.create!(:action_type => "destroy", :record => @project = Project.create(:name => "A") )
37
+ end
38
+
39
+ it "should give back Memento::Result on undo" do
40
+ result = @state.undo
41
+ result.should be_a(Memento::Result)
42
+ result.object.should be_a(Project)
43
+ result.state.should eql(@state)
44
+ end
45
+
46
+ it "should give back old data on record_data" do
47
+ @state.record_data.should == (@project.attributes_for_memento)
48
+ end
49
+ end
50
+
51
+ after do
52
+ shutdown_db
53
+ end
54
+
55
+ end
@@ -0,0 +1,169 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Memento do
4
+ before do
5
+ setup_db
6
+ setup_data
7
+ end
8
+
9
+ after do
10
+ shutdown_db
11
+ end
12
+
13
+ it "should be a singleton" do
14
+ Memento.instance.should be_kind_of(Singleton)
15
+ Memento.instance.should eql(Memento.instance)
16
+ lambda{ Memento.new }.should raise_error(NoMethodError)
17
+ end
18
+
19
+ it "should not be memento by default" do
20
+ Memento.instance.should_not be_active
21
+ end
22
+
23
+ describe "start" do
24
+
25
+ before do
26
+ Memento.instance.start(@user)
27
+ @session = Memento.instance.instance_variable_get("@session")
28
+ end
29
+
30
+ it "should require user or user_id on start" do
31
+ lambda{ Memento.instance.start }.should raise_error(ArgumentError)
32
+ end
33
+
34
+ it "should set an unsaved memento_session when starting" do
35
+ Memento::Session.count.should eql(0)
36
+ @session.should be_kind_of(Memento::Session)
37
+ @session.should be_new_record
38
+ end
39
+
40
+ it "should set user on session" do
41
+ @session.user.should eql(User.first)
42
+ end
43
+
44
+ it "should set user when passing in id as integer" do
45
+ Memento.instance.start(User.create(:name => "MyUser2").id)
46
+ Memento.instance.instance_variable_get("@session").user.should eql(User.last)
47
+ end
48
+
49
+ it "should not start memento when user does not exists/is invalid" do
50
+ Memento.instance.stop
51
+ Memento.instance.start(123333)
52
+ Memento.instance.should_not be_active
53
+ Memento.instance.start("123")
54
+ Memento.instance.should_not be_active
55
+ end
56
+
57
+ it "should be memento" do
58
+ Memento.instance.should be_active
59
+ end
60
+
61
+ end
62
+
63
+ describe "stop" do
64
+ before do
65
+ Memento.instance.start(@user)
66
+ Memento.instance.stop
67
+ end
68
+
69
+ it "should not be memento" do
70
+ Memento.instance.should_not be_active
71
+ end
72
+
73
+ it "should remove session if no states created" do
74
+ Memento::Session.count.should eql(0)
75
+ end
76
+ end
77
+
78
+ describe "memento block" do
79
+
80
+ it "should record inside of block and stop after" do
81
+ Memento.instance.should_not be_active
82
+ Memento.instance.memento(@user) do
83
+ Memento.instance.should be_active
84
+ end
85
+ Memento.instance.should_not be_active
86
+ end
87
+
88
+ it "should give back session when states created" do
89
+ Memento.instance.memento(@user) do
90
+ Project.create!
91
+ end.should be_a(Memento::Session)
92
+ end
93
+
94
+ it "should give back false when no states created" do
95
+ Memento.instance.memento(@user) do
96
+ 1 + 1
97
+ end.should be_false
98
+ end
99
+
100
+ it "should raise error in block and stop session" do
101
+ lambda {
102
+ Memento.instance.memento(@user) do
103
+ raise StandardError
104
+ end.should be_nil
105
+ }.should raise_error(StandardError)
106
+ Memento.instance.should_not be_active
107
+ end
108
+
109
+ end
110
+
111
+ describe "when active" do
112
+ before do
113
+ @project = Project.create(:name => "P1")
114
+ Memento.instance.start(@user)
115
+ end
116
+
117
+ after do
118
+ Memento.instance.stop
119
+ end
120
+
121
+ it "should create memento_state for ar-object with action_type" do
122
+ Memento::State.count.should eql(0)
123
+ Memento.instance.add_state :destroy, @project
124
+ Memento::State.count.should eql(1)
125
+ Memento::State.first.action_type.should eql("destroy")
126
+ Memento::State.first.record.should eql(Project.last)
127
+ end
128
+
129
+ it "should save session on first added state" do
130
+ Memento::Session.count.should eql(0)
131
+ Memento.instance.add_state :destroy, @project
132
+ Memento::Session.count.should eql(1)
133
+ end
134
+
135
+ describe "when ignoring" do
136
+ it "should NOT create memento_state for ar-object with action_type" do
137
+ Memento.instance.ignore do
138
+ Memento.instance.add_state :destroy, Project.create(:name => "P1")
139
+ end
140
+
141
+ Memento::State.count.should eql(0)
142
+ end
143
+ end
144
+
145
+ end
146
+
147
+ describe "when not active" do
148
+
149
+ it "should NOT create memento_state for ar-object with action_type" do
150
+ Memento.instance.add_state :destroy, Project.create(:name => "P1")
151
+ Memento::State.count.should eql(0)
152
+ end
153
+
154
+ end
155
+
156
+ context "serializer" do
157
+
158
+ it "should default to yaml" do
159
+ Memento.serializer.should eql(YAML)
160
+ end
161
+
162
+ it "should be changeable" do
163
+ Memento.serializer = Marshal
164
+ Memento.serializer.should eql(Marshal)
165
+ end
166
+
167
+ end
168
+
169
+ end