Skip to content

Commit e1613a0

Browse files
authored
Merge pull request #4485 from dks17/add_shift
fix before_remove and after_remove relation callbacks and add shift r…
2 parents d30a144 + 0bac8de commit e1613a0

File tree

2 files changed

+270
-17
lines changed
  • lib/mongoid/association/embedded/embeds_many
  • spec/mongoid/association/embedded/embeds_many

2 files changed

+270
-17
lines changed

lib/mongoid/association/embedded/embeds_many/proxy.rb

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,29 @@ def pop(count = nil)
271271
end
272272
end
273273

274+
# Shift documents off the relation. This can be a single document or
275+
# multiples, and will automatically persist the changes.
276+
#
277+
# @example Shift a single document.
278+
# relation.shift
279+
#
280+
# @example Shift multiple documents.
281+
# relation.shift(3)
282+
#
283+
# @param [ Integer ] count The number of documents to shift, or 1 if not
284+
# provided.
285+
#
286+
# @return [ Document, Array<Document> ] The shifted document(s).
287+
def shift(count = nil)
288+
if count
289+
if _target.size > 0 && docs = _target[0, count]
290+
docs.each { |doc| delete(doc) }
291+
end
292+
else
293+
delete(_target[0])
294+
end
295+
end
296+
274297
# Substitutes the supplied target documents for the existing documents
275298
# in the relation.
276299
#
@@ -526,4 +549,4 @@ def foreign_key_suffix
526549
end
527550
end
528551
end
529-
end
552+
end

spec/mongoid/association/embedded/embeds_many/proxy_spec.rb

Lines changed: 246 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2544,6 +2544,25 @@ class TrackingIdValidationHistory
25442544
person.addresses.create(street: "hermannstr")
25452545
end
25462546

2547+
context "when the number is zero" do
2548+
2549+
let!(:popped) do
2550+
person.addresses.pop(0)
2551+
end
2552+
2553+
it "returns an empty array" do
2554+
expect(popped).to eq([])
2555+
end
2556+
2557+
it "does not remove the document from the relation" do
2558+
expect(person.addresses).to eq([ address_one, address_two ])
2559+
end
2560+
2561+
it "does not persist the pop" do
2562+
expect(person.reload.addresses).to eq([ address_one, address_two ])
2563+
end
2564+
end
2565+
25472566
context "when the number is not larger than the relation" do
25482567

25492568
let!(:popped) do
@@ -2601,6 +2620,125 @@ class TrackingIdValidationHistory
26012620
end
26022621
end
26032622

2623+
describe "#shift" do
2624+
2625+
let(:person) do
2626+
Person.create
2627+
end
2628+
2629+
context "when no argument is provided" do
2630+
2631+
let!(:address_one) do
2632+
person.addresses.create(street: "sonnenallee")
2633+
end
2634+
2635+
let!(:address_two) do
2636+
person.addresses.create(street: "hermannstr")
2637+
end
2638+
2639+
let!(:shifted) do
2640+
person.addresses.shift
2641+
end
2642+
2643+
it "returns the shifted document" do
2644+
expect(shifted).to eq(address_one)
2645+
end
2646+
2647+
it "removes the document from the relation" do
2648+
expect(person.addresses).to eq([ address_two ])
2649+
end
2650+
2651+
it "persists the shift" do
2652+
expect(person.reload.addresses).to eq([ address_two ])
2653+
end
2654+
end
2655+
2656+
context "when an integer is provided" do
2657+
2658+
let!(:address_one) do
2659+
person.addresses.create(street: "sonnenallee")
2660+
end
2661+
2662+
let!(:address_two) do
2663+
person.addresses.create(street: "hermannstr")
2664+
end
2665+
2666+
context "when the number is zero" do
2667+
2668+
let!(:shifted) do
2669+
person.addresses.shift(0)
2670+
end
2671+
2672+
it "returns an empty array" do
2673+
expect(shifted).to eq([])
2674+
end
2675+
2676+
it "does not remove the document from the relation" do
2677+
expect(person.addresses).to eq([ address_one, address_two ])
2678+
end
2679+
2680+
it "does not persist the shift" do
2681+
expect(person.reload.addresses).to eq([ address_one, address_two ])
2682+
end
2683+
end
2684+
2685+
context "when the number is not larger than the relation" do
2686+
2687+
let!(:shifted) do
2688+
person.addresses.shift(2)
2689+
end
2690+
2691+
it "returns the shifted documents" do
2692+
expect(shifted).to eq([ address_one, address_two ])
2693+
end
2694+
2695+
it "removes the document from the relation" do
2696+
expect(person.addresses).to be_empty
2697+
end
2698+
2699+
it "persists the shift" do
2700+
expect(person.reload.addresses).to be_empty
2701+
end
2702+
end
2703+
2704+
context "when the number is larger than the relation" do
2705+
2706+
let!(:shifted) do
2707+
person.addresses.shift(4)
2708+
end
2709+
2710+
it "returns the shifted documents" do
2711+
expect(shifted).to eq([ address_one, address_two ])
2712+
end
2713+
2714+
it "removes the document from the relation" do
2715+
expect(person.addresses).to be_empty
2716+
end
2717+
2718+
it "persists the shift" do
2719+
expect(person.reload.addresses).to be_empty
2720+
end
2721+
end
2722+
end
2723+
2724+
context "when the relation is empty" do
2725+
2726+
context "when providing no number" do
2727+
2728+
it "returns nil" do
2729+
expect(person.addresses.shift).to be_nil
2730+
end
2731+
end
2732+
2733+
context "when providing a number" do
2734+
2735+
it "returns nil" do
2736+
expect(person.addresses.shift(2)).to be_nil
2737+
end
2738+
end
2739+
end
2740+
end
2741+
26042742
describe "#scoped" do
26052743

26062744
let(:person) do
@@ -3788,7 +3926,7 @@ class TrackingIdValidationHistory
37883926
end
37893927
end
37903928

3791-
context "#delete or #clear with before_remove callback" do
3929+
context "#delete, or #clear, or #pop, or #shift with before_remove callback" do
37923930

37933931
let(:artist) do
37943932
Artist.new
@@ -3834,35 +3972,81 @@ class TrackingIdValidationHistory
38343972
end
38353973
end
38363974

3837-
context "when errors are raised" do
3975+
describe "#pop" do
38383976

38393977
before do
3840-
expect(artist).to receive(:before_remove_song).and_raise
3978+
artist.songs.pop
38413979
end
38423980

3843-
describe "#delete" do
3981+
it "executes the callback" do
3982+
artist.songs.pop
3983+
expect(artist.before_remove_embedded_called).to be true
3984+
end
3985+
end
38443986

3845-
it "does not remove the document from the relation" do
3846-
begin; artist.songs.delete(song); rescue; end
3847-
expect(artist.songs).to eq([ song ])
3848-
end
3987+
describe "#shift" do
3988+
3989+
before do
3990+
artist.songs.shift
38493991
end
38503992

3851-
describe "#clear" do
3993+
it "executes the callback" do
3994+
artist.songs.shift
3995+
expect(artist.before_remove_embedded_called).to be true
3996+
end
3997+
end
3998+
end
38523999

3853-
before do
3854-
begin; artist.songs.clear; rescue; end
3855-
end
4000+
context "when errors are raised" do
38564001

3857-
it "removes the documents from the relation" do
3858-
expect(artist.songs).to eq([ song ])
3859-
end
4002+
before do
4003+
expect(artist).to receive(:before_remove_song).and_raise
4004+
end
4005+
4006+
describe "#delete" do
4007+
4008+
it "does not remove the document from the relation" do
4009+
begin; artist.songs.delete(song); rescue; end
4010+
expect(artist.songs).to eq([ song ])
4011+
end
4012+
end
4013+
4014+
describe "#clear" do
4015+
4016+
before do
4017+
begin; artist.songs.clear; rescue; end
4018+
end
4019+
4020+
it "removes the documents from the relation" do
4021+
expect(artist.songs).to eq([ song ])
4022+
end
4023+
end
4024+
4025+
describe "#pop" do
4026+
4027+
before do
4028+
begin; artist.songs.pop; rescue; end
4029+
end
4030+
4031+
it "should remove from collection" do
4032+
expect(artist.songs).to eq([ song ])
4033+
end
4034+
end
4035+
4036+
describe "#shift" do
4037+
4038+
before do
4039+
begin; artist.songs.shift; rescue; end
4040+
end
4041+
4042+
it "should remove from collection" do
4043+
expect(artist.songs).to eq([ song ])
38604044
end
38614045
end
38624046
end
38634047
end
38644048

3865-
context "#delete or #clear with after_remove callback" do
4049+
context "#delete, or #clear, or #pop, or #shift with after_remove callback" do
38664050

38674051
let(:artist) do
38684052
Artist.new
@@ -3899,6 +4083,30 @@ class TrackingIdValidationHistory
38994083
expect(artist.after_remove_embedded_called).to be true
39004084
end
39014085
end
4086+
4087+
describe "#pop" do
4088+
4089+
before do
4090+
artist.labels.pop
4091+
end
4092+
4093+
it "executes the callback" do
4094+
artist.labels.pop
4095+
expect(artist.after_remove_embedded_called).to be true
4096+
end
4097+
end
4098+
4099+
describe "#shift" do
4100+
4101+
before do
4102+
artist.labels.shift
4103+
end
4104+
4105+
it "executes the callback" do
4106+
artist.labels.shift
4107+
expect(artist.after_remove_embedded_called).to be true
4108+
end
4109+
end
39024110
end
39034111

39044112
context "when errors are raised" do
@@ -3928,6 +4136,28 @@ class TrackingIdValidationHistory
39284136
expect(artist.labels).to be_empty
39294137
end
39304138
end
4139+
4140+
describe "#pop" do
4141+
4142+
before do
4143+
begin; artist.labels.pop; rescue; end
4144+
end
4145+
4146+
it "should remove from collection" do
4147+
expect(artist.labels).to be_empty
4148+
end
4149+
end
4150+
4151+
describe "#shift" do
4152+
4153+
before do
4154+
begin; artist.labels.shift; rescue; end
4155+
end
4156+
4157+
it "should remove from collection" do
4158+
expect(artist.labels).to be_empty
4159+
end
4160+
end
39314161
end
39324162
end
39334163

0 commit comments

Comments
 (0)