Skip to content

Commit 04ec9d1

Browse files
committed
Add specs for Queue. Split it to UniqQueue and ObjectQueue
1 parent ea64032 commit 04ec9d1

File tree

8 files changed

+295
-63
lines changed

8 files changed

+295
-63
lines changed

lib/save_queue.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
require "save_queue/version"
2-
require "save_queue/object"
2+
#require "save_queue/object"
33

44
module SaveQueue
55
def self.included base

lib/save_queue/object_queue.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
require 'forwardable'
2+
require 'save_queue/uniq_queue'
3+
4+
module SaveQueue
5+
class ObjectQueue < UniqQueue
6+
def add object
7+
check_requirements_for object
8+
super object
9+
end
10+
11+
def save
12+
saved = []
13+
processed = []
14+
15+
@queue.each do |object|
16+
if object.has_unsaved_changes?
17+
18+
result = object.save
19+
raise FailedSaveError, {:processed => processed, :saved => saved, :failed => object, :pending => @queue - (saved + [object])} if false == result
20+
21+
saved << object
22+
end
23+
processed << object
24+
end
25+
26+
@queue.clear
27+
28+
true
29+
end
30+
31+
32+
private
33+
def check_requirements_for object
34+
[:save, :has_unsaved_changes?].each do |method|
35+
raise ArgumentError, "#{object.inspect} does not respond to ##{method}" unless object.respond_to? method
36+
end
37+
end
38+
end
39+
40+
class FailedSaveError < RuntimeError
41+
attr_reader :context
42+
def initialize(context_hash)
43+
@context = context_hash
44+
end
45+
46+
def to_s # Some default way to display errors
47+
"#{super}: " + @context.to_s
48+
end
49+
end
50+
end

lib/save_queue/queue.rb

Lines changed: 0 additions & 45 deletions
This file was deleted.

lib/save_queue/uniq_queue.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
require 'forwardable'
2+
module SaveQueue
3+
class UniqQueue
4+
extend ::Forwardable
5+
DELEGATED_METHODS = [:empty?,
6+
:size,
7+
:count,
8+
:clear,
9+
:inspect,
10+
:to_s,
11+
:last,
12+
:pop,
13+
:shift]
14+
15+
def_delegators :@queue, *DELEGATED_METHODS
16+
17+
def initialize
18+
@queue = []
19+
end
20+
21+
def add_all objects
22+
Array(objects).each do |object|
23+
add object
24+
end
25+
end
26+
27+
def add object
28+
@queue << object unless @queue.include? object
29+
end
30+
alias_method :push, :add
31+
alias_method :<<, :add
32+
end
33+
end

spec/objects_queue_spec.rb

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
require "spec_helper"
2+
require "save_queue/objects_queue"
3+
4+
describe SaveQueue::ObjectQueue do
5+
let(:queue) { SaveQueue::ObjectQueue.new }
6+
let(:element) { new_element(:element) }
7+
8+
[:add, :<<, :push].each do |method|
9+
describe "##{method}" do
10+
#it "should add only objects that implement SaveQueue::Object interface" do
11+
it "should not accept objects that does not respond to #save" do
12+
element.unstub(:save)
13+
expect{ queue.add element }.to raise_error ArgumentError, "#{element.inspect} does not respond to #save"
14+
end
15+
16+
it "should not accept objects that does not respond to #has_unsaved_changes?" do
17+
element.unstub(:has_unsaved_changes?)
18+
expect{ queue.add element }.to raise_error ArgumentError, "#{element.inspect} does not respond to #has_unsaved_changes?"
19+
end
20+
end
21+
end
22+
23+
describe "#save" do
24+
it "should save all object in queue" do
25+
5.times do
26+
element = stub(:element)
27+
element.stub(:has_unsaved_changes?).and_return(true)
28+
element.should_receive(:save).once
29+
queue << element
30+
end
31+
32+
queue.save
33+
end
34+
35+
it "should save an object if it has unsaved changes" do
36+
element = stub(:element)
37+
element.stub(:has_unsaved_changes?).and_return(true)
38+
element.should_receive(:save).once
39+
40+
queue << element
41+
queue.save
42+
end
43+
44+
it "should not save an object if it has not been changed" do
45+
element = stub(:element)
46+
element.stub(:has_unsaved_changes?).and_return(false)
47+
element.should_not_receive(:save)
48+
49+
queue << element
50+
queue.save
51+
end
52+
53+
it "should raise SaveQueue::FailedSaveError if at least one object in queue was not saved" do
54+
objects ={}
55+
objects[:valid1] = new_element(:valid1)
56+
objects[:valid2] = new_element(:valid2)
57+
objects[:not_changed] = new_element(:not_changed, :changed => false, :saved => true)
58+
objects[:unsaved_but_changed] = new_element(:unsaved_but_changed, :changed => true, :saved => false)
59+
objects[:saved] = new_element(:saved, :changed => true, :saved => true)
60+
objects[:valid3] = new_element(:valid3)
61+
62+
objects.each_value do |object|
63+
queue << object
64+
end
65+
66+
67+
expect{ queue.save }.to raise_error(SaveQueue::FailedSaveError) {|error| \
68+
error.context.should == { :processed => objects.values_at(:valid1, :valid2, :not_changed),
69+
:saved => objects.values_at(:valid1, :valid2),
70+
:failed => objects[:unsaved_but_changed],
71+
:pending => objects.values_at(:not_changed, :saved, :valid3) }
72+
}
73+
end
74+
end
75+
end

spec/support/mock_helpers.rb

Lines changed: 0 additions & 17 deletions
This file was deleted.

spec/support/queue_helpers.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
def fill_queue
2+
5.times do
3+
queue << new_element
4+
end
5+
end
6+
7+
def new_element(name = :element, options = {})
8+
element = stub(name)
9+
element.stub(:save).and_return(options.has_key?(:saved) ? options[:saved] : true)
10+
element.stub(:has_unsaved_changes?).and_return(options.has_key?(:changed) ? options[:changed] : true)
11+
12+
element
13+
end

spec/uniq_queue_spec.rb

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
require "spec_helper"
2+
require "save_queue/uniq_queue"
3+
4+
describe SaveQueue::UniqQueue do
5+
let(:queue) { SaveQueue::UniqQueue.new }
6+
#let(:element) { new_element(:element) }
7+
8+
[:size, :count].each do |method|
9+
describe "##{method}" do
10+
it "should return 0 for empty queue" do
11+
queue.send(method).should be_zero
12+
end
13+
14+
it "should count elements in queue" do
15+
3.times do
16+
queue << new_element
17+
end
18+
19+
queue.send(method).should == 3
20+
end
21+
end
22+
end
23+
24+
describe "#new" do
25+
it "should be empty queue" do
26+
queue.should have(0).elements
27+
queue.should be_empty
28+
end
29+
end
30+
31+
[:add, :<<, :push].each do |method|
32+
describe "##{method}" do
33+
it "should add object to a queue" do
34+
queue.should be_empty
35+
36+
queue.send(method, new_element)
37+
queue.should_not be_empty
38+
queue.should have(1).elements
39+
40+
queue.send(method, new_element)
41+
queue.should_not be_empty
42+
queue.should have(2).elements
43+
end
44+
45+
it "should add object to a queue once" do
46+
element = new_element
47+
queue.should be_empty
48+
49+
queue.send(method, element)
50+
queue.should have(1).elements
51+
52+
queue.send(method, element)
53+
queue.should have(1).elements
54+
end
55+
end
56+
end
57+
58+
describe "#add_all" do
59+
it "should delegate to #add" do
60+
queue.should_receive(:add).exactly(3).times
61+
queue.add_all [1,2,3]
62+
end
63+
64+
it "should act as #add if single argument passed" do
65+
queue.should_receive(:add).once
66+
queue.add_all 1
67+
end
68+
end
69+
70+
71+
describe "#clear" do
72+
it "should clear the queue" do
73+
fill_queue
74+
#expect{ queue.clear }.to change{ queue.count }.from(5).to(0)
75+
queue.should_not be_empty
76+
queue.clear
77+
queue.should be_empty
78+
end
79+
end
80+
81+
describe "#pop" do
82+
it "should remove last element from queue and return it back" do
83+
queue << new_element(:first)
84+
queue << new_element
85+
queue << new_element
86+
last = new_element(:last)
87+
queue << last
88+
89+
result = nil
90+
expect{ result = queue.pop }.to change{ queue.count }.by(-1)
91+
result.should_not be_nil
92+
result.should be last
93+
end
94+
end
95+
96+
describe "#shift" do
97+
it "should remove first element from queue and return it back" do
98+
first = new_element(:first)
99+
queue << first
100+
queue << new_element
101+
queue << new_element
102+
queue << new_element(:last)
103+
104+
result = nil
105+
expect{ result = queue.shift }.to change{ queue.count }.by(-1)
106+
result.should_not be_nil
107+
result.should be first
108+
end
109+
end
110+
111+
describe "delegates" do
112+
let(:queue_var) { queue.instance_variable_get("@queue") }
113+
114+
SaveQueue::UniqQueue::DELEGATED_METHODS.each do |method|
115+
describe "##{method}" do
116+
it "should delegate to @queue##{method}" do
117+
queue_var.should_receive(method).once
118+
queue.send method
119+
end
120+
end
121+
end
122+
end
123+
end

0 commit comments

Comments
 (0)