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.
@@ -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