save_queue 0.3.0 → 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.
@@ -14,21 +14,22 @@ module SaveQueue
14
14
  define_hook :before_add
15
15
  define_hook :after_add
16
16
 
17
+ before_add :check_requirements
18
+
17
19
  # @return [Hash] save
18
- # @option save [Array<Object>] :processed
19
20
  # @option save [Array<Object>] :saved
20
21
  # @option save [Object] :failed
21
22
  # @option save [Array<Object>] :pending
22
23
  attr_reader :errors
24
+
23
25
  def initialize(*args)
24
26
  super
25
27
  @errors = {}
26
28
  end
27
29
 
28
30
  def add object
29
- run_hook :before_add
31
+ run_hook :before_add, object
30
32
 
31
- check_requirements_for object
32
33
  result = super object
33
34
 
34
35
  run_hook :after_add, result, object
@@ -36,13 +37,6 @@ module SaveQueue
36
37
  result
37
38
  end
38
39
 
39
- def << object
40
- add object
41
- self
42
- end
43
-
44
- alias_method :push, :add
45
-
46
40
  def save
47
41
  save!
48
42
  true
@@ -52,34 +46,28 @@ module SaveQueue
52
46
 
53
47
  def save!
54
48
  run_hook :before_save
49
+
55
50
  @errors = {}
56
51
  saved = []
57
- processed = []
58
52
 
59
53
  @queue.each do |object|
60
- if object.has_unsaved_changes?
61
-
62
- result = object.save
63
- if false == result
64
- @errors[:save] = {:processed => processed, :saved => saved, :failed => object, :pending => @queue - (saved + [object])}
65
- raise FailedSaveError, errors[:save]
66
- end
67
-
68
- saved << object
54
+ if false == object.save
55
+ @errors[:save] = {:saved => saved, :failed => object, :pending => @queue - (saved + [object])}
56
+ raise FailedSaveError, errors[:save]
69
57
  end
70
- processed << object
71
- end
72
58
 
73
- @queue.clear
59
+ saved << object
60
+ end
61
+ clear
74
62
 
75
63
  run_hook :after_save
64
+
76
65
  true
77
66
  end
78
67
 
79
-
80
68
  private
81
- def check_requirements_for object
82
- [:save, :has_unsaved_changes?].each do |method|
69
+ def check_requirements(object)
70
+ [:save].each do |method|
83
71
  raise ArgumentError, "#{object.inspect} does not respond to ##{method}" unless object.respond_to? method
84
72
  end
85
73
  end
@@ -0,0 +1,31 @@
1
+ module SaveQueue
2
+ module Plugins
3
+ module Dirty
4
+ module Object
5
+ def mark_as_changed
6
+ @_changed_mark = true
7
+ end
8
+
9
+ def mark_as_saved
10
+ @_changed_mark = false
11
+ end
12
+
13
+ # @returns [Boolean] true if object has been modified
14
+ def has_unsaved_changes?
15
+ @_changed_mark ||= false
16
+ end
17
+
18
+ private
19
+ def _sq_around_original_save
20
+ result = nil
21
+ if has_unsaved_changes?
22
+ result = yield
23
+ mark_as_saved
24
+ end
25
+
26
+ result
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ require "save_queue/plugins/dirty/object"
2
+
3
+ module SaveQueue
4
+ module Plugins
5
+ module Dirty
6
+ def self.included base
7
+ base.send :include, Dirty::Object unless base.include? Dirty::Object
8
+ end
9
+ end
10
+ end
11
+ end
@@ -2,32 +2,14 @@ module SaveQueue
2
2
  module Plugins
3
3
  module Notification
4
4
  module Object
5
- module AddObserverToQueue
6
- def create_queue
7
- super
8
- queue = instance_variable_get("@_save_queue")
9
- raise "save queue should respond to add_observer in order to work correctly" unless queue.respond_to? :add_observer
10
- queue.add_observer(self, :queue_changed_event)
11
- end
12
- end
13
-
14
- def self.included base
15
-
16
- #queue_creator = Module.new do
17
- # def create_queue
18
- # super
19
- # queue = instance_variable_get("@_save_queue")
20
- # raise "save queue should respond to add_observer in order to work correctly" unless queue.respond_to? :add_observer
21
- # queue.add_observer(self, :queue_changed_event)
22
- # end
23
- #end
5
+ def queue_changed_event(result, object, *args); end
24
6
 
25
- #base.send :include, queue_creator
26
- base.send :include, AddObserverToQueue unless base.include?(AddObserverToQueue)
27
- end
7
+ def create_queue
8
+ super
28
9
 
29
- def queue_changed_event(result, object)
30
- mark_as_changed if result
10
+ queue = instance_variable_get("@_save_queue")
11
+ raise "save queue should respond to add_observer in order to work correctly" unless queue.respond_to? :add_observer
12
+ queue.add_observer(self, :queue_changed_event)
31
13
  end
32
14
  end
33
15
  end
@@ -9,8 +9,8 @@ module SaveQueue
9
9
  module Notification
10
10
  module Queue
11
11
  def self.included base
12
- base.send :include, Observable unless base.include? Observable
13
- base.after_add :change_and_notify # if base.respond_to? :after_add
12
+ base.send :include, Observable
13
+ base.after_add :change_and_notify
14
14
  end
15
15
 
16
16
  private
@@ -18,7 +18,6 @@ module SaveQueue
18
18
  changed
19
19
  notify_observers(*args)
20
20
  end
21
-
22
21
  end
23
22
  end
24
23
  end
@@ -6,8 +6,8 @@ module SaveQueue
6
6
  module Notification
7
7
  def self.included base
8
8
  klass = Class.new(base.queue_class)
9
- klass.send :include, Notification::Queue unless klass.include? Notification::Queue
10
- base.send :include, Notification::Object unless base.include? Notification::Object
9
+ klass.send :include, Notification::Queue unless klass.include? Notification::Queue
10
+ base.send :include, Notification::Object unless base.include? Notification::Object
11
11
  base.queue_class = klass
12
12
  end
13
13
  end
@@ -1,12 +1,20 @@
1
1
  module SaveQueue
2
2
  class FailedValidationError < Error
3
3
  attr_reader :failed_objects
4
+
4
5
  def initialize(failed_objects)
5
6
  @failed_objects = Array(failed_objects)
6
7
  end
7
8
 
8
- def to_s # Some default way to display errors
9
- "#{super}: " + @failed_objects.map{|object| "\"#{object.to_s}\": " + object.errors.full_messages.join(', ')}.join("\n")
9
+
10
+ # Overwrite to your needs
11
+ def to_s
12
+ "#{super}: " + @failed_objects.join("\n")
10
13
  end
14
+
15
+ # danger if object not respond to errors or full_messages
16
+ #def to_s
17
+ # "#{super}: " + @failed_objects.map{|object| "\"#{object.to_s}\": " + object.errors.full_messages.join(', ')}.join("\n")
18
+ #end
11
19
  end
12
20
  end
@@ -5,7 +5,7 @@ module SaveQueue
5
5
  module Validation
6
6
  module Queue
7
7
  def self.included base
8
- base.before_save :validate! if base.respond_to? :before_save
8
+ base.before_save :validate!
9
9
  end
10
10
 
11
11
  def valid?
@@ -7,7 +7,7 @@ module SaveQueue
7
7
  module Validation
8
8
  def self.included base
9
9
  klass = Class.new(base.queue_class)
10
- klass.send :include, Validation::Queue
10
+ klass.send :include, Validation::Queue unless klass.include? Validation::Queue
11
11
  base.queue_class = klass
12
12
  end
13
13
  end
@@ -32,7 +32,15 @@ module SaveQueue
32
32
 
33
33
  true
34
34
  end
35
- alias_method :push, :add
36
- alias_method :<<, :add
35
+
36
+ #alias_method :push, :add # Not working as expected: does not save inheritance
37
+ def push(*args)
38
+ add *args
39
+ end
40
+
41
+ def << *args
42
+ add *args
43
+ self
44
+ end
37
45
  end
38
46
  end
@@ -1,3 +1,3 @@
1
1
  module SaveQueue
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,74 @@
1
+ require "spec_helper"
2
+ require "save_queue/plugins/dirty"
3
+
4
+ describe SaveQueue::Plugins::Dirty do
5
+ describe "#integration" do
6
+ it "should mix Object to object class" do
7
+ klass = new_class
8
+ klass.send :include, SaveQueue::Plugins::Dirty
9
+
10
+ klass.should include SaveQueue::Plugins::Dirty::Object
11
+ end
12
+ describe "functional" do
13
+ it "README example should work" do
14
+ article = Article.new
15
+
16
+ tag_objects = []
17
+ 3.times do
18
+ tag = Tag.new
19
+ tag.change_attribute :title, "new tag"
20
+ tag.should_receive(:save).once
21
+ tag_objects << tag
22
+ end
23
+ article.tags = tag_objects
24
+
25
+ tag = Tag.new
26
+ tag.change_attribute :title, "single tag"
27
+ tag.should_receive(:save).once
28
+ article.add_tag tag
29
+
30
+ # that will save article and all tags in this article if article
31
+ # and tags are valid, and if article.save and all tag.save returns true
32
+ # You may also use #save! method, that will trigger save_queue.save! and
33
+ # raise SaveQueue::FailedSaveError on fail
34
+ article.save.should be_true
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ require 'save_queue'
41
+ class Article
42
+ include SaveQueue
43
+ include SaveQueue::Plugins::Dirty
44
+
45
+ def change_attribute attr, value
46
+ @attributes ||= {}
47
+ @attributes[attr] = value
48
+ mark_as_changed # call this and object will be marked for save
49
+ end
50
+
51
+ def tags= tag_objects
52
+ @tags = tag_objects
53
+ mark_as_changed
54
+ save_queue.add_all tag_objects
55
+ end
56
+
57
+ def add_tag tag
58
+ @tags ||= []
59
+ @tags << tag
60
+ save_queue.add tag # or use <<, push methods
61
+ end
62
+ end
63
+
64
+
65
+ class Tag
66
+ include SaveQueue
67
+ include SaveQueue::Plugins::Dirty
68
+
69
+ def change_attribute attr, value
70
+ @attributes ||= {}
71
+ @attributes[attr] = value
72
+ mark_as_changed # call this and object will be marked for save
73
+ end
74
+ end
@@ -0,0 +1,200 @@
1
+ require "spec_helper"
2
+ require "save_queue/plugins/dirty/object"
3
+
4
+ class DirtyObject
5
+ include SaveQueue
6
+ include SaveQueue::Plugins::Dirty::Object
7
+ end
8
+
9
+ describe SaveQueue::Plugins::Dirty::Object do
10
+ let(:object) { DirtyObject.new }
11
+
12
+ describe "#has_unsaved_changes?" do
13
+ it "should return true for changed object" do
14
+ object.mark_as_changed
15
+ object.has_unsaved_changes?.should eq true
16
+ end
17
+
18
+ it "should return false for unchanged object" do
19
+ object.mark_as_saved
20
+ object.has_unsaved_changes?.should eq false
21
+ end
22
+
23
+ it "should return false for new object" do
24
+ object.has_unsaved_changes?.should eq false
25
+ end
26
+ end
27
+
28
+ describe "marks" do
29
+ it "should change state of an object" do
30
+ object.mark_as_saved
31
+ object.should_not have_unsaved_changes
32
+ object.mark_as_changed
33
+ object.should have_unsaved_changes
34
+ object.mark_as_saved
35
+ object.should_not have_unsaved_changes
36
+ end
37
+ end
38
+
39
+ describe "#save!" do
40
+ context "changed object" do
41
+ let(:changed_object) {object.mark_as_changed; object}
42
+
43
+ it "should call #save! on queue" do
44
+ changed_object.save_queue.should_receive(:save!).once
45
+ changed_object.save!
46
+ end
47
+
48
+ it "should mark object as saved" do
49
+ changed_object.should_receive(:mark_as_saved).once
50
+ changed_object.save!
51
+ end
52
+
53
+ context "original class has #save! method defined" do
54
+ it "should call that method" do
55
+ changed_object =
56
+ Class.new(DirtyObject) do
57
+ attr_reader :save_called
58
+ def save!
59
+ @save_called = true
60
+ end
61
+ end.new
62
+
63
+ changed_object.mark_as_changed
64
+ expect{ changed_object.save! }.to change { changed_object.save_called }.from(nil).to(true)
65
+ end
66
+ end
67
+ end
68
+
69
+ context "not changed object" do
70
+ let(:not_changed_object) {object.mark_as_saved; object}
71
+
72
+ it "should not mark object as saved" do
73
+ not_changed_object.should_not_receive(:mark_as_saved)
74
+ not_changed_object.save!
75
+ end
76
+
77
+ it "should call #save! on queue" do
78
+ not_changed_object.save_queue.should_receive(:save!).once
79
+ not_changed_object.save!
80
+ end
81
+
82
+ context "original class has #save! method defined" do
83
+ it "should not call that method" do
84
+ not_changed_object =
85
+ Class.new(DirtyObject) do
86
+ attr_reader :save_called
87
+ def save!
88
+ @save_called = true
89
+ end
90
+ end.new
91
+
92
+ not_changed_object.mark_as_saved
93
+ expect{ not_changed_object.save! }.to_not change { not_changed_object.save_called }
94
+ end
95
+
96
+ context "multiple save with changing state" do
97
+ it "should call that method if state was changed after 1st save" do
98
+ not_changed_object =
99
+ Class.new(DirtyObject) do
100
+ attr_reader :save_called
101
+ def save!
102
+ @save_called = true
103
+ end
104
+ end.new
105
+ not_changed_object.save!
106
+ not_changed_object.mark_as_changed
107
+ changed_object = not_changed_object
108
+
109
+ expect{ changed_object.save! }.to change { changed_object.save_called }.from(nil).to(true)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "#save" do
117
+ context "changed object" do
118
+ let(:changed_object) {object.mark_as_changed; object}
119
+
120
+ it "should save queue" do
121
+ changed_object.save_queue.should_receive(:save).once
122
+ changed_object.save
123
+ end
124
+
125
+ context "original class has #save method defined" do
126
+ it "should call that method (save object)" do
127
+ changed_object =
128
+ Class.new(DirtyObject) do
129
+ attr_reader :save_called
130
+ def save
131
+ @save_called = true
132
+ end
133
+ end.new
134
+
135
+ changed_object.mark_as_changed
136
+ expect{ changed_object.save }.to change { changed_object.save_called }.from(nil).to(true)
137
+ end
138
+
139
+ #it "should save object only once in multiple queues" do
140
+ # other_object = new_object
141
+ # target = new_count_object
142
+ #
143
+ # object .save_queue.add target
144
+ # other_object.save_queue.add target
145
+ #
146
+ # other_object.save.should be_true
147
+ # object.save.should be_true
148
+ #
149
+ # target.save_call_count.should == 1
150
+ #end
151
+ end
152
+ end
153
+
154
+ context "not changed object" do
155
+ let(:not_changed_object) {object.mark_as_saved; object}
156
+
157
+ it "should not mark object as saved" do
158
+ not_changed_object.should_not_receive(:mark_as_saved)
159
+ not_changed_object.save
160
+ end
161
+
162
+ it "should save queue" do
163
+ not_changed_object.save_queue.should_receive(:save).once
164
+ not_changed_object.save
165
+ end
166
+
167
+ context "original class has #save method defined" do
168
+ it "should not call that method (dont save an object)" do
169
+ not_changed_object =
170
+ Class.new(DirtyObject) do
171
+ attr_reader :save_called
172
+ def save
173
+ @save_called = true
174
+ end
175
+ end.new
176
+
177
+ not_changed_object.mark_as_saved
178
+ expect{ not_changed_object.save }.to_not change { not_changed_object.save_called }
179
+ end
180
+
181
+ context "multiple save with changing state" do
182
+ it "should call that method if state was changed after 1st save" do
183
+ not_changed_object =
184
+ Class.new(DirtyObject) do
185
+ attr_reader :save_called
186
+ def save
187
+ @save_called = true
188
+ end
189
+ end.new
190
+ not_changed_object.save
191
+ not_changed_object.mark_as_changed
192
+ changed_object = not_changed_object
193
+
194
+ expect{ changed_object.save }.to change { changed_object.save_called }.from(nil).to(true)
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -34,11 +34,10 @@ describe SaveQueue::Plugins::Notification do
34
34
  klass.new
35
35
  end
36
36
 
37
- [:add, :<<, :push].each do |method|
38
- it "should mark object as changed if save_queue was changed by ##{method}" do
39
- object.mark_as_saved
37
+ ADD_METHODS.each do |method|
38
+ it "should call queue_changed_event if save_queue was changed by ##{method} method" do
39
+ object.should_receive(:queue_changed_event)
40
40
  object.save_queue.send method, new_object
41
- object.should have_unsaved_changes
42
41
  end
43
42
  end
44
43
  end
@@ -34,21 +34,4 @@ describe SaveQueue::Plugins::Notification::Object do
34
34
  NotifyObject.queue_class = queue
35
35
  expect { NotifyObject.new }.to_not raise_error
36
36
  end
37
-
38
- describe "#queue_changed_event" do
39
- let(:object) do
40
- NotifyObject.any_instance.stub(:create_queue)
41
- NotifyObject.new
42
- end
43
-
44
- it "should mark self as changed" do
45
- object.should_receive(:mark_as_changed)
46
- object.send :queue_changed_event, true, stub(:some_object)
47
- end
48
-
49
- it "should not mark self as changed if result of adding element to a queue was false" do
50
- object.should_not_receive(:mark_as_changed)
51
- object.send :queue_changed_event, false, stub(:some_object)
52
- end
53
- end
54
37
  end
@@ -1,14 +1,14 @@
1
1
  require "spec_helper"
2
2
  require "save_queue/plugins/notification/queue"
3
3
 
4
- class NotifyQueue < SaveQueue::ObjectQueue
4
+ class QueueWithNotification < SaveQueue::ObjectQueue
5
5
  include SaveQueue::Plugins::Notification::Queue
6
6
  end
7
7
 
8
8
  describe SaveQueue::Plugins::Notification::Queue do
9
- let(:queue) { NotifyQueue.new }
9
+ let(:queue) { QueueWithNotification.new }
10
10
 
11
- [:add, :<<, :push].each do |method|
11
+ ADD_METHODS.each do |method|
12
12
  describe "##{method}" do
13
13
  let(:element) { new_element }
14
14
 
@@ -18,7 +18,7 @@ describe SaveQueue::Plugins::Notification::Queue do
18
18
  queue.send method, element
19
19
  end
20
20
 
21
- it "should notify observer, provided result of a method call and input object" do
21
+ it "should provide input params and result of ##{method} method call to observers" do
22
22
  queue.should_receive(:changed)
23
23
  queue.should_receive(:notify_observers).with(true, element)
24
24
  queue.send method, element
@@ -0,0 +1,53 @@
1
+ require 'save_queue/object/queue_class_management'
2
+
3
+ describe SaveQueue::Object::QueueClassManagement do
4
+ let(:klass) do
5
+ Class.new do
6
+ extend SaveQueue::Object::QueueClassManagement
7
+ end
8
+ end
9
+ let(:default_queue_class) { SaveQueue::ObjectQueue }
10
+ let(:first_queue_class) { Class.new(default_queue_class) }
11
+ let(:second_queue_class) { Class.new(default_queue_class) }
12
+
13
+ it "should map to SaveQueue::ObjectQueue by default" do
14
+ klass.queue_class.should == default_queue_class
15
+ end
16
+
17
+ describe "changing" do
18
+ it "should be possible" do
19
+ klass.queue_class = second_queue_class
20
+ klass.queue_class.should eq second_queue_class
21
+ end
22
+
23
+ it "should check inclusion of Hooks module" do
24
+ expect{ klass.queue_class = Class.new }.to raise_error(RuntimeError, /Hooks/)
25
+ end
26
+ end
27
+
28
+ it "inclusion of SaveQueue::Object should not override settings" do
29
+ klass.queue_class = second_queue_class
30
+ expect { klass.send :include, SaveQueue::Object }.to_not change { klass.queue_class }
31
+ end
32
+
33
+ describe "inheritance" do
34
+ let(:parent) { klass }
35
+ let(:child) { Class.new(parent) }
36
+ before(:each) do
37
+ parent.queue_class = first_queue_class
38
+ end
39
+
40
+ it "should inherit settings of parent class" do
41
+ child.queue_class.should == first_queue_class
42
+ end
43
+
44
+ it "should not override settings of parent class" do
45
+ expect { child.queue_class = second_queue_class }.to_not change { parent.queue_class }
46
+ end
47
+
48
+ it "inclusion of SaveQueue::Object should not override settings of child class" do
49
+ child.queue_class = second_queue_class
50
+ expect { child.send :include, SaveQueue::Object }.to_not change { child.queue_class }
51
+ end
52
+ end
53
+ end