From 2e58475400bc159eb5fdc0a1679dd0f0d23cc284 Mon Sep 17 00:00:00 2001 From: Emmanuel Gomez Date: Tue, 9 Aug 2011 14:39:17 -0700 Subject: [PATCH 1/4] Prevent DirtyMinder from attempting to wrap nil values. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specifically, this happens when a resource is lazy-loading a property that includes DirtyMinder: Resource#eager_load iterates over the set of properties to lazy-load and calls Property#set with nil for each before calling Collection#lazy_load. The Property#set call blows up when given nil because DirtyMinder tries to add instance variables to nil—@resource & @property in DirtyMinder#wrap_value—which is a frozen object and thus blows up. Do the research if you don't believe me. Seriously, please do; I'd love to be proven wrong about this. Anyways, nil can't be mutated, thus I don't think anyone cares about tracking nil, so this fix/work-around should not impact functionality at all. --- lib/dm-types/support/dirty_minder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dm-types/support/dirty_minder.rb b/lib/dm-types/support/dirty_minder.rb index 14ed939..c2737ea 100644 --- a/lib/dm-types/support/dirty_minder.rb +++ b/lib/dm-types/support/dirty_minder.rb @@ -137,13 +137,13 @@ def track(resource, property) # This catches any direct assignment, allowing us to hook the Hash or Array. def set(resource, value) - hook_value(resource, value) unless value.kind_of? Hooker + wrap_value(resource, value) unless value.frozen? || value.kind_of?(Wrapper) super end # This gets called when Resource#reload is called (instead of #set). def set!(resource, value) - hook_value(resource, value) unless value.kind_of? Hooker + wrap_value(resource, value) unless value.frozen? || value.kind_of?(Wrapper) super end From 9e38ca7d36d6b1dfc3a70078a80cb5719ff2d0f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gomez Date: Tue, 9 Aug 2011 14:54:34 -0700 Subject: [PATCH 2/4] Oops, I didn't look very closely. My changes were based on a ref that had a Wrapper module, now it's called Hooker. --- lib/dm-types/support/dirty_minder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dm-types/support/dirty_minder.rb b/lib/dm-types/support/dirty_minder.rb index c2737ea..25d3528 100644 --- a/lib/dm-types/support/dirty_minder.rb +++ b/lib/dm-types/support/dirty_minder.rb @@ -137,13 +137,13 @@ def track(resource, property) # This catches any direct assignment, allowing us to hook the Hash or Array. def set(resource, value) - wrap_value(resource, value) unless value.frozen? || value.kind_of?(Wrapper) + wrap_value(resource, value) unless value.frozen? || value.kind_of?(Hooker) super end # This gets called when Resource#reload is called (instead of #set). def set!(resource, value) - wrap_value(resource, value) unless value.frozen? || value.kind_of?(Wrapper) + wrap_value(resource, value) unless value.frozen? || value.kind_of?(Hooker) super end From c08d2225e9108aebbec20ecbe3e6128a8f82e491 Mon Sep 17 00:00:00 2001 From: Emmanuel Gomez Date: Tue, 9 Aug 2011 15:11:56 -0700 Subject: [PATCH 3/4] Jeez, I should test really this locally before pushing. But I won't. --- lib/dm-types/support/dirty_minder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dm-types/support/dirty_minder.rb b/lib/dm-types/support/dirty_minder.rb index 25d3528..ae9e611 100644 --- a/lib/dm-types/support/dirty_minder.rb +++ b/lib/dm-types/support/dirty_minder.rb @@ -137,13 +137,13 @@ def track(resource, property) # This catches any direct assignment, allowing us to hook the Hash or Array. def set(resource, value) - wrap_value(resource, value) unless value.frozen? || value.kind_of?(Hooker) + hook_value(resource, value) unless value.frozen? || value.kind_of?(Hooker) super end # This gets called when Resource#reload is called (instead of #set). def set!(resource, value) - wrap_value(resource, value) unless value.frozen? || value.kind_of?(Hooker) + hook_value(resource, value) unless value.frozen? || value.kind_of?(Hooker) super end From d87497939a40c63404ea0569420399578987ae02 Mon Sep 17 00:00:00 2001 From: Emmanuel Gomez Date: Tue, 16 Aug 2011 13:05:59 -0700 Subject: [PATCH 4/4] In DirtyMinder, test #nil? instead of #frozen?. Also outdent the private keyword, because outdented method visibility declarations are the bees knees. --- lib/dm-types/support/dirty_minder.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/dm-types/support/dirty_minder.rb b/lib/dm-types/support/dirty_minder.rb index ae9e611..2ceb6fe 100644 --- a/lib/dm-types/support/dirty_minder.rb +++ b/lib/dm-types/support/dirty_minder.rb @@ -137,17 +137,17 @@ def track(resource, property) # This catches any direct assignment, allowing us to hook the Hash or Array. def set(resource, value) - hook_value(resource, value) unless value.frozen? || value.kind_of?(Hooker) + hook_value(resource, value) unless value.nil? || value.kind_of?(Hooker) super end # This gets called when Resource#reload is called (instead of #set). def set!(resource, value) - hook_value(resource, value) unless value.frozen? || value.kind_of?(Hooker) + hook_value(resource, value) unless value.nil? || value.kind_of?(Hooker) super end - private + private def hook_value(resource, value) return if value.kind_of? Hooker