memento 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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