Evil cache bug with composed_of #2084

Closed
bradphelan opened this Issue Jul 15, 2011 · 5 comments

Comments

Projects
None yet
4 participants
@bradphelan

The method

----------------------
activerecord-3.1.0.rc4/lib/active_record/aggregations.rb
----------------------
235         def writer_method(name, class_name, mapping, allow_nil, converter)
236           define_method("#{name}=") do |part|
237             if part.nil? && allow_nil
238               mapping.each { |pair| self[pair.first] = nil }
239               @aggregation_cache[name] = nil
240             else
241               unless part.is_a?(class_name.constantize) || converter.nil?
242                 part = converter.respond_to?(:call) ?
243                   converter.call(part) :
244                   class_name.constantize.send(converter, part)
245               end
246  
247               mapping.each { |pair| self[pair.first] = part.send(pair.last) }
248               @aggregation_cache[name] = part.freeze
249             end
250           end
251         end

is broken at line 247 due to caching. The problem is that rails does type conversions on activerecord fields. So for example if I have an AR class Foo with a field bar declared as an integer in the db then

foo = new Foo
foo.bar = "10.0"
puts foo.bar.class

=> Integer

However composed_of assumes that the there is no conversion during setting the object. Thus the aggregate objects will be returned with a string field rather than an integer field because the converted object was not read back and put in the cache.
The solution is to add an extra line in the above method that reads back the attribute after writing and caches the converted value and not the original value.

Something like


        def writer_method(name, class_name, mapping, allow_nil, converter)
          define_method("#{name}=") do |part|
            if part.nil? && allow_nil
              mapping.each { |pair| self[pair.first] = nil }
              @aggregation_cache[name] = nil
            else
              unless part.is_a?(class_name.constantize) || converter.nil?
                part = converter.respond_to?(:call) ?
                  converter.call(part) :
                  class_name.constantize.send(converter, part)
              end

              mapping.each { |pair| 
                  self[pair.first] = part.send(pair.last)
                  # Cache the converted attribute in the aggregate part
                  part.send("#{pair.last}=", self[pair.first]
               }
              @aggregation_cache[name] = part.freeze
            end
          end
        end
@jonkessler

This comment has been minimized.

Show comment
Hide comment
@jonkessler

jonkessler Apr 25, 2012

Contributor

@bradphelan Is this still an issue, or can it be closed?

Contributor

jonkessler commented Apr 25, 2012

@bradphelan Is this still an issue, or can it be closed?

@bradphelan

This comment has been minimized.

Show comment
Hide comment
@bradphelan

bradphelan Apr 25, 2012

Has the suggested fix been made in the code. I don't use composed of anymore. I doubt it has been fixed by some other vector if the change I suggested has not been made.

Has the suggested fix been made in the code. I don't use composed of anymore. I doubt it has been fixed by some other vector if the change I suggested has not been made.

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Apr 28, 2012

Member

Since @bradphelan isn't sure if this is still a problem, I'm closing. If anyone wants to produce a test case and/or a patch, I will gladly re-open.

Member

steveklabnik commented Apr 28, 2012

Since @bradphelan isn't sure if this is still a problem, I'm closing. If anyone wants to produce a test case and/or a patch, I will gladly re-open.

@rafaelfranca

This comment has been minimized.

Show comment
Hide comment
@rafaelfranca

rafaelfranca Apr 29, 2012

Member

Reopening since we got a patch. So it is still an issue

Member

rafaelfranca commented Apr 29, 2012

Reopening since we got a patch. So it is still an issue

@steveklabnik

This comment has been minimized.

Show comment
Hide comment
@steveklabnik

steveklabnik Jun 16, 2012

Member

If #6743 gets merged, this bug will go away.

Member

steveklabnik commented Jun 16, 2012

If #6743 gets merged, this bug will go away.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment