Michael Schuerig
2005-Sep-26 06:15 UTC
[Rails-core] When eager becomes lazy: nil-valued has_one and belongs_to
When finding with included associations the intention is to pay a lesser
price up front for work that has to be done anyway. With has_one and
belongs_to associations where there is no target this doesn''t work. The
finding code only creates an AssociationProxy when a target exists.
Later, when the respective association is accessed and there is no
AssociationProxy, this results in another futile access to the
database.
def find_with_associations(options = {})
...
when :has_one, :belongs_to
next unless
row[primary_key_table[reflection.table_name]]
record.send(
"set_#{reflection.name}_target",
reflection.klass.send(:instantiate,
extract_record(schema_abbreviations, reflection.table_name, row))
)
end
end
end
This behavior is even asserted by a test
def test_loading_with_no_associations
assert_nil Post.find(posts(:authorless).id, :include
=> :author).author
end
So, obviously someone did this deliberately. I just don''t understand
the
reason and think behavior should be different, i.e. an AssociationProxy
should be created for non-existent targets.
Michael
--
Michael Schuerig All good people read good books
mailto:michael@schuerig.de Now your conscience is clear
http://www.schuerig.de/michael/ --Tanita Tikaram, Twist In My Sobriety
Jamis Buck
2005-Sep-26 09:29 UTC
[Rails-core] When eager becomes lazy: nil-valued has_one and belongs_to
One difference, I think, between the collection associations and the has_one/belongs_to is that the collection associations are never nil-- and never would have been nil--even if they don''t contain anything, so you would never want to check for nilness anyway. It is perfectly reasonable to assume that the target itself won''t be loaded until later, because you''re not going to be comparing on the target itself, you''ll be comparing based on methods of the target (i.e., empty?, size, etc.). With has_one and belongs_to, I most commonly check for nilness simply by comparing the association (e.g. #author) to nil. If the ability to make that comparison were taken away, it would break a lot of _my_ code, to be sure, and I suspect others as well. To be honest, I''ve pretty much stopped using the model.target.create approach to building new association members anyway, because it causes the target to be loaded and if you''re dealing with a collection of several hundred or thousand elements, that''s not really acceptible. - Jamis On Sep 26, 2005, at 3:50 AM, Michael Schuerig wrote:> > When finding with included associations the intention is to pay a > lesser > price up front for work that has to be done anyway. With has_one and > belongs_to associations where there is no target this doesn''t work. > The > finding code only creates an AssociationProxy when a target exists. > Later, when the respective association is accessed and there is no > AssociationProxy, this results in another futile access to the > database. > > def find_with_associations(options = {}) > ... > when :has_one, :belongs_to > next unless > row[primary_key_table[reflection.table_name]] > > record.send( > "set_#{reflection.name}_target", > reflection.klass.send(:instantiate, > extract_record(schema_abbreviations, reflection.table_name, row)) > ) > end > end > end > > This behavior is even asserted by a test > > def test_loading_with_no_associations > assert_nil Post.find(posts(:authorless).id, :include > => :author).author > end > > So, obviously someone did this deliberately. I just don''t > understand the > reason and think behavior should be different, i.e. an > AssociationProxy > should be created for non-existent targets. > > Michael > > -- > Michael Schuerig All good people read good books > mailto:michael@schuerig.de Now your conscience is clear > http://www.schuerig.de/michael/ --Tanita Tikaram, Twist In My Sobriety > _______________________________________________ > Rails-core mailing list > Rails-core@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails-core >
Michael Schuerig
2005-Sep-26 10:01 UTC
[Rails-core] Re: When eager becomes lazy: nil-valued has_one and belongs_to
On Monday 26 September 2005 15:05, Jamis Buck wrote:> With has_one and belongs_to, I most commonly check for nilness simply > by comparing the association (e.g. #author) to nil. If the ability to > make that comparison were taken away, it would break a lot of _my_ > code, to be sure, and I suspect others as well.What I''d like to point out is that each of these checks results in a database access. That''s what I want to get rid of. Would it be possible without major breakage to override AssociationProxy#nil? to return true when there is no target object? Michael -- Michael Schuerig Nothing is as brilliantly adaptive mailto:michael@schuerig.de as selective stupidity. http://www.schuerig.de/michael/ --A.O. Rorty, The Deceptive Self
Michael Koziarski
2005-Sep-26 15:47 UTC
[Rails-core] Re: When eager becomes lazy: nil-valued has_one and belongs_to
On 9/27/05, Michael Schuerig <michael@schuerig.de> wrote:> On Monday 26 September 2005 15:05, Jamis Buck wrote: > > > With has_one and belongs_to, I most commonly check for nilness simply > > by comparing the association (e.g. #author) to nil. If the ability to > > make that comparison were taken away, it would break a lot of _my_ > > code, to be sure, and I suspect others as well. > > What I''d like to point out is that each of these checks results in a > database access. That''s what I want to get rid of. > > Would it be possible without major breakage to override > AssociationProxy#nil? to return true when there is no target object?Unfortunately, that would break this kind of code: if object.association # do something end As far as I recall, you can''t cause your own classes to be treated as ''false''. Only NilClass and FalseClass get treated that way.> Michael > > -- > Michael Schuerig Nothing is as brilliantly adaptive > mailto:michael@schuerig.de as selective stupidity. > http://www.schuerig.de/michael/ --A.O. Rorty, The Deceptive Self > _______________________________________________ > Rails-core mailing list > Rails-core@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails-core >-- Cheers Koz
Caio Chassot
2005-Sep-26 16:46 UTC
[Rails-core] Re: When eager becomes lazy: nil-valued has_one and belongs_to
>> What I''d like to point out is that each of these checks results in a >> database access. That''s what I want to get rid of. >>I think this can be done while still returning nil. The issue is that right now we store the return value of association_proxy.reload, and we always reload if the association value is nil. So the solution is rather simple. We need to internally keep a reference to the actual association proxy, rather than to the association target, even when the target is nil, and let the association accessor method return the target of this same proxy, always (unless force_reload). Completely untested, but maybe this will do: (@ associations.rb ~l 605) define_method(association_name) do |*params| force_reload = params.first unless params.empty? association_proxy = instance_variable_get("@#{association_name}") if association_proxy.nil? or force_reload association_proxy = association_proxy_class.new(self, association_name, association_class_name, association_class_primary_key_name, options) association_proxy.reload instance_variable_set("@#{association_name}", association_proxy) end association_proxy.target end Probably some tweaking on the eager loader and how it sets the association instance variable would be necessary too.