memento 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,9 @@
1
- class Memento
1
+ module Memento
2
2
  module ActionControllerMethods
3
-
3
+
4
4
  def memento
5
5
  block_result = nil
6
- memento_session = Memento.instance.memento(current_user) do
6
+ memento_session = Memento(current_user) do
7
7
  block_result = yield
8
8
  end
9
9
  if memento_session
@@ -1,82 +1,82 @@
1
1
  require 'active_support/core_ext/class/attribute'
2
2
 
3
3
  module Memento::ActiveRecordMethods
4
-
4
+
5
5
  IGNORE_ATTRIBUTES = [:updated_at, :created_at]
6
-
6
+
7
7
  def self.included(base)
8
8
  base.class_attribute(:memento_options_store, :instance_reader => false, :instance_writer => false)
9
9
  base.memento_options_store = {}
10
10
  base.extend ClassMethods
11
11
  end
12
-
12
+
13
13
  module ClassMethods
14
-
14
+
15
15
  def memento_changes(*action_types)
16
16
  if defined?(@memento_initialized) && @memento_initialized
17
17
  raise "Memento initialized twice. Use memento_changes only once per model"
18
18
  end
19
-
19
+
20
20
  @memento_initialized = true
21
-
21
+
22
22
  include InstanceMethods
23
-
23
+
24
24
  self.memento_options = action_types.last.is_a?(Hash) ? action_types.pop : {}
25
-
25
+
26
26
  action_types = Memento::Action::Base.action_types if action_types.empty?
27
27
  action_types.map!(&:to_s).uniq!
28
28
  unless (invalid = action_types - Memento::Action::Base.action_types).empty?
29
29
  raise ArgumentError.new("Invalid memento_changes: #{invalid.to_sentence}; allowed are only #{Memento::Action::Base.action_types.to_sentence}")
30
30
  end
31
-
31
+
32
32
  action_types.each do |action_type|
33
33
  send :"after_#{action_type}", :"record_#{action_type}"
34
34
  end
35
-
35
+
36
36
  has_many :memento_states, :class_name => "Memento::State", :as => :record
37
37
  end
38
-
38
+
39
39
  def memento_options
40
40
  self.memento_options_store ||= {}
41
41
  end
42
-
42
+
43
43
  def memento_options=(options)
44
44
  options.symbolize_keys!
45
45
  options[:ignore] = [options[:ignore]].flatten.map(&:to_sym) if options[:ignore]
46
46
  self.memento_options_store = memento_options_store.merge(options)
47
47
  end
48
48
  end
49
-
49
+
50
50
  module InstanceMethods
51
-
51
+
52
52
  def attributes_for_memento
53
53
  filter_attributes_for_memento(attributes)
54
54
  end
55
-
55
+
56
56
  def changes_for_memento
57
57
  filter_attributes_for_memento(changes)
58
58
  end
59
-
59
+
60
60
  def filter_attributes_for_memento(hash)
61
- hash.delete_if do |key, value|
61
+ hash.delete_if do |key, value|
62
62
  ignore_attributes_for_memento.include?(key.to_sym)
63
63
  end
64
64
  end
65
65
  private :filter_attributes_for_memento
66
-
66
+
67
67
  def ignore_attributes_for_memento
68
68
  Memento::ActiveRecordMethods::IGNORE_ATTRIBUTES + (self.class.memento_options[:ignore] || [])
69
69
  end
70
70
  private :ignore_attributes_for_memento
71
-
71
+
72
72
  Memento::Action::Base.action_types.each do |action_type|
73
73
  define_method :"record_#{action_type}" do
74
- Memento.instance.add_state(action_type, self)
74
+ Memento.add_state(action_type, self)
75
75
  end
76
76
  private :"record_#{action_type}"
77
77
  end
78
78
  end
79
-
79
+
80
80
  end
81
81
 
82
82
  ActiveRecord::Base.send(:include, Memento::ActiveRecordMethods) if defined?(ActiveRecord::Base)
@@ -1,37 +1,38 @@
1
- class Memento::ResultArray < Array
2
-
3
- def errors
4
- self.find_all{ |result| result.failed? }
5
- end
6
-
7
- def failed?
8
- self.any?{ |result| result.failed? }
9
- end
10
-
11
- def success?
12
- !failed?
13
- end
14
-
15
- end
16
-
17
- class Memento::Result
18
-
19
- attr_reader :object, :state
20
-
21
- def initialize(object, state)
22
- @object, @state = object, state
23
- end
24
-
25
- def error
26
- error = @object.errors[:memento_undo]
27
- error.present? ? error : nil
28
- end
29
-
30
- def failed?
31
- !!error
1
+ module Memento
2
+ class ResultArray < Array
3
+ def errors
4
+ self.find_all{ |result| result.failed? }
5
+ end
6
+
7
+ def failed?
8
+ self.any?{ |result| result.failed? }
9
+ end
10
+
11
+ def success?
12
+ !failed?
13
+ end
14
+
32
15
  end
33
-
34
- def success?
35
- !failed?
16
+
17
+ class Result
18
+
19
+ attr_reader :object, :state
20
+
21
+ def initialize(object, state)
22
+ @object, @state = object, state
23
+ end
24
+
25
+ def error
26
+ error = @object.errors[:memento_undo]
27
+ error.present? ? error : nil
28
+ end
29
+
30
+ def failed?
31
+ !!error
32
+ end
33
+
34
+ def success?
35
+ !failed?
36
+ end
36
37
  end
37
38
  end
@@ -1,29 +1,30 @@
1
- class Memento::Session < ActiveRecord::Base
2
- self.table_name = "memento_sessions"
3
-
4
- has_many :states, :class_name => "Memento::State", :dependent => :delete_all, :order => "id DESC"
5
- belongs_to :user
6
- validates_presence_of :user
7
-
8
- def add_state(action_type, record)
9
- states.store(action_type, record)
10
- end
11
-
12
- def undo
13
- states.map(&:undo).inject(Memento::ResultArray.new) do |results, result|
14
- result.state.destroy if result.success?
15
- results << result
1
+ module Memento
2
+ class Session < ActiveRecord::Base
3
+ self.table_name = "memento_sessions"
4
+
5
+ has_many :states, :class_name => "Memento::State", :dependent => :delete_all, :order => "id DESC"
6
+ belongs_to :user
7
+ validates_presence_of :user
8
+
9
+ def add_state(action_type, record)
10
+ states.store(action_type, record)
16
11
  end
17
- ensure
18
- destroy if states.count.zero?
19
- end
20
-
21
- def undo!
22
- transaction do
23
- undo.tap do |results|
24
- raise Memento::ErrorOnRewind if results.failed?
12
+
13
+ def undo
14
+ states.map(&:undo).inject(Memento::ResultArray.new) do |results, result|
15
+ result.state.destroy if result.success?
16
+ results << result
17
+ end
18
+ ensure
19
+ destroy if states.count.zero?
20
+ end
21
+
22
+ def undo!
23
+ transaction do
24
+ undo.tap do |results|
25
+ raise Memento::ErrorOnRewind if results.failed?
26
+ end
25
27
  end
26
28
  end
27
29
  end
28
-
29
30
  end
data/lib/memento/state.rb CHANGED
@@ -1,47 +1,48 @@
1
- class Memento::State < ActiveRecord::Base
2
- self.table_name = "memento_states"
3
-
4
- belongs_to :session, :class_name => "Memento::Session"
5
- belongs_to :record, :polymorphic => true
6
-
7
- validates_presence_of :session
8
- validates_presence_of :record
9
- validates_presence_of :action_type
10
- validates_inclusion_of :action_type, :in => Memento::Action::Base.action_types, :allow_blank => true
11
-
12
- before_create :set_record_data
13
-
14
- def self.store(action_type, record)
15
- self.new(:action_type => action_type.to_s, :record => record) do |state|
16
- state.save if state.fetch?
1
+ module Memento
2
+ class State < ActiveRecord::Base
3
+ self.table_name = "memento_states"
4
+
5
+ belongs_to :session, :class_name => "Memento::Session"
6
+ belongs_to :record, :polymorphic => true
7
+
8
+ validates_presence_of :session
9
+ validates_presence_of :record
10
+ validates_presence_of :action_type
11
+ validates_inclusion_of :action_type, :in => Memento::Action::Base.action_types, :allow_blank => true
12
+
13
+ before_create :set_record_data
14
+
15
+ def self.store(action_type, record)
16
+ self.new(:action_type => action_type.to_s, :record => record) do |state|
17
+ state.save if state.fetch?
18
+ end
19
+ end
20
+
21
+ def undo
22
+ Memento::Result.new(action.undo, self)
23
+ end
24
+
25
+ def record_data
26
+ @record_data ||= Memento.serializer.load(read_attribute(:record_data))
27
+ end
28
+
29
+ def record_data=(data)
30
+ @record_data = nil
31
+ write_attribute(:record_data, data.is_a?(String) ? data : Memento.serializer.dump(data))
32
+ end
33
+
34
+ def fetch?
35
+ action.fetch?
36
+ end
37
+
38
+ private
39
+
40
+ def set_record_data
41
+ self.record_data = action.fetch
42
+ end
43
+
44
+ def action
45
+ "memento/action/#{action_type}".classify.constantize.new(self)
17
46
  end
18
47
  end
19
-
20
- def undo
21
- Memento::Result.new(action.undo, self)
22
- end
23
-
24
- def record_data
25
- @record_data ||= Memento.serializer.load(read_attribute(:record_data))
26
- end
27
-
28
- def record_data=(data)
29
- @record_data = nil
30
- write_attribute(:record_data, data.is_a?(String) ? data : Memento.serializer.dump(data))
31
- end
32
-
33
- def fetch?
34
- action.fetch?
35
- end
36
-
37
- private
38
-
39
- def set_record_data
40
- self.record_data = action.fetch
41
- end
42
-
43
- def action
44
- "memento/action/#{action_type}".classify.constantize.new(self)
45
- end
46
-
47
48
  end
@@ -1,3 +1,3 @@
1
- class Memento
2
- VERSION = "0.3.7"
1
+ module Memento
2
+ VERSION = "0.4.0"
3
3
  end
data/memento.gemspec CHANGED
@@ -11,12 +11,12 @@ Gem::Specification.new do |s|
11
11
  s.homepage = %q{http://github.com/yolk/memento}
12
12
  s.summary = %q{Undo for Rails/ActiveRecord - covers destroy, update and create}
13
13
  s.description = %q{Undo for Rails/ActiveRecord - covers destroy, update and create}
14
-
14
+
15
15
  s.files = `git ls-files`.split("\n")
16
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
18
  s.require_paths = ["lib"]
19
-
19
+
20
20
  s.add_dependency 'activerecord', '~> 3.2.5'
21
21
  s.add_dependency 'actionpack', '~> 3.2.5'
22
22
 
@@ -4,32 +4,32 @@ describe Memento::Action::Create, "when object is created" do
4
4
  before do
5
5
  setup_db
6
6
  setup_data
7
- Memento.instance.start(@user)
7
+ Memento.start(@user)
8
8
  @project = Project.create!(:name => "P1", :closed_at => 3.days.ago).reload
9
- Memento.instance.stop
9
+ Memento.stop
10
10
  end
11
-
11
+
12
12
  after do
13
13
  shutdown_db
14
14
  end
15
-
15
+
16
16
  it "should create memento_state for ar-object with no data" do
17
17
  Memento::State.count.should eql(1)
18
18
  Memento::State.first.action_type.should eql("create")
19
19
  Memento::State.first.record.should eql(@project) # it was destroyed, remember?
20
20
  Memento::State.first.reload.record_data.should eql(nil)
21
21
  end
22
-
22
+
23
23
  it "should create object" do
24
24
  Project.find_by_id(@project.id).should_not be_nil
25
25
  Project.count.should eql(1)
26
26
  end
27
-
27
+
28
28
  it "should allow undoing the creation" do
29
29
  Memento::Session.last.undo
30
30
  Project.count.should eql(0)
31
31
  end
32
-
32
+
33
33
  describe "when undoing the creation" do
34
34
  it "should give back undone_object" do
35
35
  Memento::Session.last.undo.map{|e| e.object.class }.should eql([Project])
@@ -42,7 +42,7 @@ describe Memento::Action::Create, "when object is created" do
42
42
  undone.first.should_not be_success
43
43
  undone.first.error.first.should be_was_changed
44
44
  end
45
-
45
+
46
46
  describe "when record was already destroyed" do
47
47
  it "should give back fake unsaved record with id set" do
48
48
  Project.last.destroy
@@ -56,24 +56,24 @@ describe Memento::Action::Create, "when object is created" do
56
56
  end
57
57
  end
58
58
  end
59
-
60
-
61
-
59
+
60
+
61
+
62
62
  end
63
63
 
64
64
  describe Memento::Action::Create, "when object without timestamp is created" do
65
65
  before do
66
66
  setup_db
67
67
  setup_data
68
- Memento.instance.memento(@user) do
68
+ Memento(@user) do
69
69
  @obj = TimestamplessObject.create!(:name => "O1").reload
70
70
  end
71
71
  end
72
-
72
+
73
73
  after do
74
74
  shutdown_db
75
75
  end
76
-
76
+
77
77
  describe "when undoing the creation" do
78
78
  it "should give back undone_object" do
79
79
  Memento::Session.last.undo.map{|e| e.object.class }.should eql([TimestamplessObject])
@@ -5,27 +5,27 @@ describe Memento::Action::Destroy, "when object gets destroyed" do
5
5
  setup_db
6
6
  setup_data
7
7
  @project = Project.create!(:name => "P1", :closed_at => 3.days.ago).reload
8
- Memento.instance.start(@user)
8
+ Memento.start(@user)
9
9
  @project.destroy
10
10
  end
11
-
11
+
12
12
  after do
13
- Memento.instance.stop
13
+ Memento.stop
14
14
  shutdown_db
15
15
  end
16
-
16
+
17
17
  it "should create memento_state for ar-object with full attributes_for_memento" do
18
18
  Memento::State.count.should eql(1)
19
19
  Memento::State.first.action_type.should eql("destroy")
20
20
  Memento::State.first.record.should be_nil # it was destroyed, remember?
21
21
  Memento::State.first.reload.record_data.should == @project.attributes_for_memento
22
22
  end
23
-
23
+
24
24
  it "should destroy object" do
25
25
  Project.find_by_id(@project.id).should be_nil
26
26
  Project.count.should be_zero
27
27
  end
28
-
28
+
29
29
  it "should allow undoing the destruction" do
30
30
  Project.count.should be_zero
31
31
  Memento::Session.last.undo
@@ -34,10 +34,10 @@ describe Memento::Action::Destroy, "when object gets destroyed" do
34
34
  @project.attributes_for_memento.reject{|k, v| k.to_sym == :id }
35
35
  )
36
36
  end
37
-
37
+
38
38
  it "should give back undone_object on undoing the destruction" do
39
39
  Memento::Session.last.undo.map{|e| e.object.class }.should eql([Project])
40
40
  end
41
-
42
-
41
+
42
+
43
43
  end