Skip to content

Commit 6a1fadd

Browse files
Fixes for embedded touch (#7)
* Fixes for embedded touch * Fix touch loop fix on Mongoid 4 and 5
1 parent 9fca9d8 commit 6a1fadd

File tree

9 files changed

+151
-38
lines changed

9 files changed

+151
-38
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11

2+
#### 0.2.5
3+
4+
* Improvements to `:touch` callback support on `:embedded_in`.
5+
* Backport fix to an infinite loop issue related to `:touch` callbacks from Mongoid 6.
6+
27
#### 0.2.4
38

49
* Support aliases for index options.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ If you would only like some of the patches, please copy and paste the code to yo
3737
| `instrument.rb` | Backport instrumentation change to Moped 1. ||||
3838
| `reorder.rb` | Backport `Criteria#reorder` method from Mongoid 4. ||||
3939
| `only_pluck_localized.rb` | Backport [PR #4299](https://github.com/mongodb/mongoid/pull/4299) from Mongoid 6 which fixes `#only`, `#without`, and `#pluck` with localized fields. || × | × |
40-
| `embedded_touch.rb` | Backport [Issue #3310](https://github.com/mongodb/mongoid/commit/a94c2f43573e58f973913c881ad9d11d62bf857c) from Mongoid 4 to add `:touch` option to `embedded_in`. ||||
40+
| `embedded_touch.rb` (1) | Backport [Issue #3310](https://github.com/mongodb/mongoid/commit/a94c2f43573e58f973913c881ad9d11d62bf857c) from Mongoid 4 to add `:touch` option to `embedded_in`. ||||
41+
| `embedded_touch.rb` (2) | Backport [PR #4392](https://github.com/mongodb/mongoid/pull/4392) from Mongoid 6 to fix an infinite loop issue related to `:touch` callbacks. ||||
4142
| `index_options.rb` | Backport latest index valid index options from Mongoid 6. ||||
4243

4344
### License

lib/patches/embedded_touch.rb

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,96 @@
1-
# Backport Mongoid 4 :touch option for #embedded_in to Mongoid 3.
1+
# Backport support #embedded_in :touch option from Mongoid 4 to Mongoid 3.
2+
# Also support touch callback on update, and fix infinite loop issue.
23

34
if Mongoid::VERSION =~ /\A3\./
45

56
module Mongoid
6-
module Relations
7-
module Embedded
8-
class In < Relations::One
9-
class << self
10-
def valid_options
11-
[ :autobuild, :cyclic, :polymorphic, :touch ]
7+
module Relations
8+
module Embedded
9+
class In < Relations::One
10+
class << self
11+
def valid_options
12+
[ :autobuild, :cyclic, :polymorphic, :touch ]
13+
end
14+
end
15+
end
16+
end
17+
18+
module Macros
19+
module ClassMethods
20+
21+
def embedded_in(name, options = {}, &block)
22+
if ancestors.include?(Mongoid::Versioning)
23+
raise Errors::VersioningNotOnRoot.new(self)
24+
end
25+
meta = characterize(name, Embedded::In, options, &block)
26+
self.embedded = true
27+
relate(name, meta)
28+
builder(name, meta).creator(name, meta)
29+
touchable(meta)
30+
add_counter_cache_callbacks(meta) if meta.counter_cached?
31+
meta
1232
end
1333
end
1434
end
15-
end
1635

17-
module Macros
18-
module ClassMethods
36+
module Touchable
37+
module ClassMethods
1938

20-
def embedded_in(name, options = {}, &block)
21-
if ancestors.include?(Mongoid::Versioning)
22-
raise Errors::VersioningNotOnRoot.new(self)
39+
def touchable(metadata)
40+
if metadata.touchable?
41+
name = metadata.name
42+
method_name = define_relation_touch_method(name)
43+
after_save method_name
44+
after_destroy method_name
45+
after_touch method_name
46+
end
47+
self
2348
end
24-
meta = characterize(name, Embedded::In, options, &block)
25-
self.embedded = true
26-
relate(name, meta)
27-
builder(name, meta).creator(name, meta)
28-
touchable(meta)
29-
add_counter_cache_callbacks(meta) if meta.counter_cached?
30-
meta
3149
end
3250
end
3351
end
52+
53+
module Callbacks
54+
def cascadable_children(kind, children = Set.new)
55+
embedded_relations.each_pair do |name, metadata|
56+
next unless metadata.cascading_callbacks?
57+
without_autobuild do
58+
delayed_pulls = delayed_atomic_pulls[name]
59+
delayed_unsets = delayed_atomic_unsets[name]
60+
children.merge(delayed_pulls) if delayed_pulls
61+
children.merge(delayed_unsets) if delayed_unsets
62+
relation = send(name)
63+
Array.wrap(relation).each do |child|
64+
next if children.include?(child)
65+
children.add(child) if cascadable_child?(kind, child, metadata)
66+
child.send(:cascadable_children, kind, children)
67+
end
68+
end
69+
end
70+
children.to_a
71+
end
72+
73+
def cascadable_child?(kind, child, metadata)
74+
return false if kind == :initialize || kind == :find || kind == :touch
75+
return false if kind == :validate && metadata.validate?
76+
child.callback_executable?(kind) ? child.in_callback_state?(kind) : false
77+
end
78+
end
3479
end
80+
81+
end
82+
83+
if Mongoid::VERSION =~ /\A[45]\./
84+
85+
module Mongoid
86+
87+
module Interceptable
88+
def cascadable_child?(kind, child, metadata)
89+
return false if kind == :initialize || kind == :find || kind == :touch
90+
return false if kind == :validate && metadata.validate?
91+
child.callback_executable?(kind) ? child.in_callback_state?(kind) : false
92+
end
93+
end
3594
end
3695

3796
end

lib/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module MongoidMonkey
2-
VERSION = '0.2.4'
2+
VERSION = '0.2.5'
33
end

spec/app/models/book.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class Book
2+
include Mongoid::Document
3+
include Mongoid::Timestamps
4+
5+
embeds_many :pages, cascade_callbacks: true
6+
end

spec/app/models/edit.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class Edit
2+
include Mongoid::Document
3+
include Mongoid::Timestamps::Updated
4+
embedded_in :wiki_page, touch: true
5+
end

spec/app/models/page.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class Page
2+
include Mongoid::Document
3+
4+
embedded_in :book, touch: true
5+
field :content, :type => String
6+
end

spec/app/models/wiki_page.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class WikiPage
2+
include Mongoid::Document
3+
include Mongoid::Timestamps
4+
5+
field :title, type: String
6+
7+
embeds_many :edits, validate: false
8+
end

spec/unit/embedded_touch_spec.rb

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
11
require "spec_helper"
22

3-
if Mongoid::VERSION =~ /\A3\./
4-
5-
class Edit
6-
include Mongoid::Document
7-
include Mongoid::Timestamps::Updated
8-
embedded_in :wiki_page, touch: true
9-
end
10-
11-
class WikiPage
12-
include Mongoid::Document
13-
include Mongoid::Timestamps
14-
15-
field :title, type: String
16-
17-
embeds_many :edits, validate: false
18-
end
3+
if Mongoid::VERSION =~ /\A[345]\./
194

205
describe Mongoid::Relations::Embedded::In do
216

@@ -48,5 +33,43 @@ class WikiPage
4833
expect(page.updated_at).to be_within(5).of(Time.now)
4934
end
5035
end
36+
37+
context "when the parent of embedded doc has cascade callbacks" do
38+
39+
let!(:book) do
40+
Book.new
41+
end
42+
43+
before do
44+
book.pages.new
45+
book.save
46+
book.unset(:updated_at)
47+
book.pages.first.touch
48+
end
49+
50+
it "touches the parent document" do
51+
expect(book.updated_at).to be_within(5).of(Time.now)
52+
end
53+
end
54+
55+
context "when multiple embedded docs with cascade callbacks" do
56+
57+
let!(:book) do
58+
Book.new
59+
end
60+
61+
before do
62+
2.times { book.pages.new }
63+
book.save
64+
book.unset(:updated_at)
65+
book.pages.first.content = "foo"
66+
book.pages.second.content = "bar"
67+
book.pages.first.touch
68+
end
69+
70+
it "touches the parent document" do
71+
expect(book.updated_at).to be_within(5).of(Time.now)
72+
end
73+
end
5174
end
5275
end

0 commit comments

Comments
 (0)