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 CHANGED
@@ -1 +1 @@
1
- 1.9.3-p0
1
+ 1.9.3-p194
data/CHANGES.md CHANGED
@@ -1,6 +1,15 @@
1
1
  ### dev
2
2
 
3
- [full changelog](http://github.com/yolk/valvat/compare/v0.3.7...master)
3
+ [full changelog](http://github.com/yolk/valvat/compare/v0.4.0...master)
4
+
5
+ ### 0.4.0 / 2012-10-29
6
+
7
+ [full changelog](http://github.com/yolk/valvat/compare/v0.3.7...v0.4.0)
8
+
9
+ * Memento is a Module now, not a Singleton: Use Memento directly and not Memento.instance
10
+ * Memento module is threadsafe now
11
+ * Changed main api: instead of Memento.memento() use Memento.watch() or Memento()
12
+ * Some code cleanup
4
13
 
5
14
  ### 0.3.7 / 2012-08-13
6
15
 
data/README.rdoc CHANGED
@@ -26,9 +26,9 @@ memento needs two tables in your database, one to store "sessions" (sets of stat
26
26
 
27
27
  script/generate memento_migration
28
28
  rake db:migrate
29
-
29
+
30
30
  memento assumes you have a user-model. Every session is owned by a user.
31
-
31
+
32
32
  == Configure your models
33
33
 
34
34
  Then you have to tell every model you want to undo actions on that it should be watched by memento:
@@ -42,13 +42,13 @@ This will tell memento to create snapshots of the model when an new instance is
42
42
  If you want memento to only take snapshots on specific actions:
43
43
 
44
44
  memento_changes :update, :destroy
45
-
45
+
46
46
  This will take a snapshot only when an instance is updated or destroyed.
47
47
 
48
48
  By default memento will ignore changes to the :updated_at and :created_at attributes. You can add further attributes to ignore with the :ignore option:
49
49
 
50
50
  memento_changes :ignore => [:calculated_birthday, :friends_count]
51
-
51
+
52
52
  This will ignore changes on the calculated_birthday and the firends_count-attributes. When memento saves a whole instance of your model before it is destroyed, those attributes will not be stored for later recovery. Only ignore attributes you can re-calculate from other data!
53
53
 
54
54
  == Action!
@@ -57,22 +57,22 @@ When you perform any of the configured actions on your model in isolation in you
57
57
 
58
58
  Person.create!(:name => "Blah")
59
59
  Memento::Session.count # => 0
60
-
60
+
61
61
  You have to wrap every action block you want memento to track in your controller with the memento-method:
62
62
 
63
63
  memento do
64
64
  Person.create!(:name => "Blah")
65
65
  end
66
66
  Memento::Session.count # => 1
67
-
68
- This assumes there is an method called "current_user" in your controllers. It will set the HTTP-Header 'X-Memento-Session-Id' on your response.
67
+
68
+ This assumes there is an method called "current_user" in your controllers. It will also set the HTTP-Header 'X-Memento-Session-Id' on your response.
69
69
 
70
70
  If you want memento to watch changes outside of your controllers (for example inside the console) you can use:
71
71
 
72
- Memento.instance.memento(user) do
72
+ Memento(user) do
73
73
  Person.create!(:name => "Blah")
74
74
  end
75
-
75
+
76
76
  Where the variable user is assumed to hold an instance of User.
77
77
 
78
78
  == Undo!
@@ -1,11 +1,11 @@
1
1
  class MementoMigration < ActiveRecord::Migration
2
-
2
+
3
3
  def self.up
4
4
  create_table :memento_sessions do |t|
5
5
  t.references :user
6
6
  t.timestamps
7
7
  end
8
-
8
+
9
9
  create_table :memento_states do |t|
10
10
  t.string :action_type
11
11
  t.binary :record_data, :limit => 16777215
@@ -14,10 +14,10 @@ class MementoMigration < ActiveRecord::Migration
14
14
  t.timestamps
15
15
  end
16
16
  end
17
-
17
+
18
18
  def self.down
19
19
  drop_table :memento_states
20
20
  drop_table :memento_sessions
21
21
  end
22
-
22
+
23
23
  end
data/lib/memento.rb CHANGED
@@ -1,63 +1,81 @@
1
1
  require 'active_record'
2
2
 
3
- class Memento
4
- include Singleton
5
-
3
+ module Memento
4
+
6
5
  class ErrorOnRewind < StandardError;end
7
-
8
- def memento(user)
9
- start(user)
10
- yield
11
- !@session.states.count.zero? && @session rescue false
12
- ensure
13
- stop
14
- end
15
-
16
- def start(user_or_id)
17
- user = user_or_id.is_a?(User) ? user_or_id : User.find_by_id(user_or_id)
18
- @session = user ? Memento::Session.new(:user => user) : nil
19
- end
20
-
21
- def stop
22
- @session.destroy if @session && @session.states.count.zero?
23
- @session = nil
24
- end
25
-
26
- def add_state(action_type, record)
27
- return unless save_session
28
- @session.add_state(action_type, record)
29
- end
30
-
31
- def active?
32
- !!(defined?(@session) && @session) && !ignore?
33
- end
34
-
35
- def ignore
36
- @ignore = true
37
- yield
38
- ensure
39
- @ignore = false
40
- end
41
-
42
- def self.serializer=(serializer)
43
- @serializer = serializer
44
- end
45
-
46
- def self.serializer
47
- @serializer ||= YAML
48
- end
49
-
50
- private
51
-
52
- def ignore?
53
- defined?(@ignore) && @ignore
54
- end
55
-
56
- def save_session
57
- active? && (!@session.changed? || @session.save)
6
+
7
+ class << self
8
+
9
+ # For backwards compatibility (was a Singleton)
10
+ def instance
11
+ self
12
+ end
13
+
14
+ def watch(user_or_id)
15
+ start(user_or_id)
16
+ yield
17
+ session && !session.new_record? && session.states.any? ? session : false
18
+ ensure
19
+ stop
20
+ end
21
+
22
+ def start(user_or_id)
23
+ user = user_or_id.is_a?(User) ? user_or_id : User.find_by_id(user_or_id)
24
+ self.session = user ? Memento::Session.new(:user => user) : nil
25
+ end
26
+
27
+ def stop
28
+ session.destroy if session && session.states.count.zero?
29
+ self.session = nil
30
+ end
31
+
32
+ def add_state(action_type, record)
33
+ return unless save_session
34
+ session.add_state(action_type, record)
35
+ end
36
+
37
+ def active?
38
+ !!session && !ignore?
39
+ end
40
+
41
+ def ignore
42
+ Thread.current[:memento_ignore] = true
43
+ yield
44
+ ensure
45
+ Thread.current[:memento_ignore] = false
46
+ end
47
+
48
+ def serializer=(serializer)
49
+ @serializer = serializer
50
+ end
51
+
52
+ def serializer
53
+ @serializer ||= YAML
54
+ end
55
+
56
+ private
57
+
58
+ def session
59
+ Thread.current[:memento_session]
60
+ end
61
+
62
+ def session=(session)
63
+ Thread.current[:memento_session] = session
64
+ end
65
+
66
+ def ignore?
67
+ !!Thread.current[:memento_ignore]
68
+ end
69
+
70
+ def save_session
71
+ active? && (!session.changed? || session.save)
72
+ end
58
73
  end
59
74
  end
60
75
 
76
+ def Memento(user_or_id, &block)
77
+ Memento.watch(user_or_id, &block)
78
+ end
61
79
  require 'memento/result'
62
80
  require 'memento/action'
63
81
  require 'memento/active_record_methods'
@@ -5,29 +5,29 @@ module Memento::Action
5
5
  def initialize(state)
6
6
  @state = state
7
7
  end
8
-
8
+
9
9
  attr_reader :state
10
10
  class_attribute :action_types, :instance_reader => false, :instance_writer => false
11
11
  self.action_types = []
12
-
12
+
13
13
  def record
14
14
  @state.record
15
15
  end
16
-
16
+
17
17
  def record_data
18
18
  @state.record_data
19
19
  end
20
-
20
+
21
21
  def fetch?
22
22
  true
23
23
  end
24
-
24
+
25
25
  def self.inherited(child)
26
26
  self.action_types << child.name.demodulize.underscore
27
27
  end
28
-
28
+
29
29
  private
30
-
30
+
31
31
  def new_object
32
32
  object = @state.record_type.constantize.new
33
33
  yield(object) if block_given?
@@ -1,37 +1,39 @@
1
- class Memento::Action::Create < Memento::Action::Base
2
-
3
- def fetch;end
4
-
5
- def undo
6
- if record.nil?
7
- build_fake_object
8
- elsif record_was_changed?
9
- was_changed
10
- else
11
- destroy_record
1
+ module Memento
2
+ class Action::Create < Memento::Action::Base
3
+
4
+ def fetch;end
5
+
6
+ def undo
7
+ if record.nil?
8
+ build_fake_object
9
+ elsif record_was_changed?
10
+ was_changed
11
+ else
12
+ destroy_record
13
+ end
12
14
  end
13
- end
14
-
15
- private
16
-
17
- def record_was_changed?
18
- record.updated_at > record.created_at rescue false
19
- end
20
-
21
- def build_fake_object
22
- new_object do |object|
23
- object.id = state.record_id
15
+
16
+ private
17
+
18
+ def record_was_changed?
19
+ record.updated_at > record.created_at rescue false
24
20
  end
21
+
22
+ def build_fake_object
23
+ new_object do |object|
24
+ object.id = state.record_id
25
+ end
26
+ end
27
+
28
+ def was_changed
29
+ record.errors.add(:memento_undo, ActiveSupport::StringInquirer.new("was_changed"))
30
+ record
31
+ end
32
+
33
+ def destroy_record
34
+ record.destroy
35
+ record
36
+ end
37
+
25
38
  end
26
-
27
- def was_changed
28
- record.errors.add(:memento_undo, ActiveSupport::StringInquirer.new("was_changed"))
29
- record
30
- end
31
-
32
- def destroy_record
33
- record.destroy
34
- record
35
- end
36
-
37
39
  end
@@ -1,31 +1,32 @@
1
- class Memento::Action::Destroy < Memento::Action::Base
2
-
3
- def fetch
4
- record.attributes_for_memento
5
- end
6
-
7
- def undo
8
- rebuild_object do |object|
9
- begin
10
- object.save!
11
- rescue
12
- object.id = nil
13
- object.save!
1
+ module Memento
2
+ class Action::Destroy < Memento::Action::Base
3
+
4
+ def fetch
5
+ record.attributes_for_memento
6
+ end
7
+
8
+ def undo
9
+ rebuild_object do |object|
10
+ begin
11
+ object.save!
12
+ rescue
13
+ object.id = nil
14
+ object.save!
15
+ end
16
+ state.record = object
17
+ state.save
14
18
  end
15
- state.record = object
16
- state.save
17
19
  end
18
- end
19
-
20
- private
21
-
22
- def rebuild_object
23
- new_object do |object|
24
- state.record_data.each do |attribute, value|
25
- object.send(:"#{attribute}=", value)
20
+
21
+ private
22
+
23
+ def rebuild_object
24
+ new_object do |object|
25
+ state.record_data.each do |attribute, value|
26
+ object.send(:"#{attribute}=", value)
27
+ end
28
+ yield(object) if block_given?
26
29
  end
27
- yield(object) if block_given?
28
30
  end
29
31
  end
30
-
31
32
  end
@@ -1,54 +1,55 @@
1
- class Memento::Action::Update < Memento::Action::Base
2
-
3
- def fetch
4
- record.changes_for_memento
5
- end
6
-
7
- def fetch?
8
- record.changes_for_memento.any?
9
- end
10
-
11
- def undo
12
- if !record
13
- was_destroyed
14
- elsif mergable?
15
- update_record
16
- else
17
- was_changed
1
+ module Memento
2
+ class Action::Update < Memento::Action::Base
3
+
4
+ def fetch
5
+ record.changes_for_memento
18
6
  end
19
- end
20
-
21
- private
22
-
23
- def update_record
24
- record.tap do |object|
25
- record_data.each do |attribute, values|
26
- object.send(:"#{attribute}=", values.first)
7
+
8
+ def fetch?
9
+ record.changes_for_memento.any?
10
+ end
11
+
12
+ def undo
13
+ if !record
14
+ was_destroyed
15
+ elsif mergable?
16
+ update_record
17
+ else
18
+ was_changed
27
19
  end
28
- object.save!
29
20
  end
30
- end
31
-
32
- def mergable?
33
- record_data.all? do |attribute, values|
34
- # ugly fix to compare times
35
- values = values.map{|v| v.is_a?(Time) ? v.to_s(:db) : v }
36
- current_value = record.send(:"#{attribute}")
37
- current_value = current_value.utc.to_s(:db) if current_value.is_a?(Time)
38
- values.include?(current_value) || (current_value.is_a?(String) && values.include?(current_value.gsub(/\r\n/, "\n")))
39
- end || record_data.size.zero?
40
- end
41
-
42
- def was_destroyed
43
- new_object do |object|
44
- object.errors[:memento_undo] << ActiveSupport::StringInquirer.new("was_destroyed")
45
- object.id = state.record_id
21
+
22
+ private
23
+
24
+ def update_record
25
+ record.tap do |object|
26
+ record_data.each do |attribute, values|
27
+ object.send(:"#{attribute}=", values.first)
28
+ end
29
+ object.save!
30
+ end
31
+ end
32
+
33
+ def mergable?
34
+ record_data.all? do |attribute, values|
35
+ # ugly fix to compare times
36
+ values = values.map{|v| v.is_a?(Time) ? v.to_s(:db) : v }
37
+ current_value = record.send(:"#{attribute}")
38
+ current_value = current_value.utc.to_s(:db) if current_value.is_a?(Time)
39
+ values.include?(current_value) || (current_value.is_a?(String) && values.include?(current_value.gsub(/\r\n/, "\n")))
40
+ end || record_data.size.zero?
41
+ end
42
+
43
+ def was_destroyed
44
+ new_object do |object|
45
+ object.errors[:memento_undo] << ActiveSupport::StringInquirer.new("was_destroyed")
46
+ object.id = state.record_id
47
+ end
48
+ end
49
+
50
+ def was_changed
51
+ record.errors[:memento_undo] << ActiveSupport::StringInquirer.new("was_changed")
52
+ record
46
53
  end
47
54
  end
48
-
49
- def was_changed
50
- record.errors[:memento_undo] << ActiveSupport::StringInquirer.new("was_changed")
51
- record
52
- end
53
-
54
55
  end