This project is archived and is in readonly mode.
[PATCH] Refactoring JoinDependency and friends
Reported by Jon Leighton | October 6th, 2010 @ 09:34 PM
Bo Jeanes and I are working to finally try and create a solid patch adding nested has many through association support to Active Record. See #1152 for that.
Part of that patch will be to enable stuff like
Author.joins(:similar_authors)
where
:similar_authors
is a nested has many through
association. I started looking into that and realised that I would
need to make some changes to how JoinAssociation works, in order to
allow it to generate an arbitrary number of actual joins.
This lead to a bit of a refactoring effort at the same time, as I did not want it to be a complete kludge that would be even harder to understand. The attached patch is the refactoring - it does nothing related to nested has many through, other than that it will enable the nested has many through stuff to be more easily added in.
I have added 3 tests which cover existing functionality which did not have tests.
All tests pass. If you want you can see our nested has many through branch here: http://github.com/bjeanes/rails, and the refactoring in particular here: http://github.com/bjeanes/rails/commit/f2b41914d6be935182d37e0c0d49....
Comments and changes to this ticket
-
Bodaniel Jeanes October 7th, 2010 @ 11:21 PM
Jon, perhaps prefix the title of this ticket with [PATCH] so they know there's a patch to apply/test instead of a problem to solve.
FWIW, +1 and this change introduces no extra detectable bugs and will go along way with regards to Jon and I finishing integrated nested has_many :through support in #1152
-
Jon Leighton October 7th, 2010 @ 11:22 PM
- Title changed from Refactoring JoinDependency and friends to [PATCH] Refactoring JoinDependency and friends
-
Jon Leighton October 7th, 2010 @ 11:23 PM
I thought that the 'patch' tag was the official way to do it, but if [PATCH] in the title helps then why not :)
-
Ryan Bigg October 8th, 2010 @ 03:01 AM
- Importance changed from to Low
+1 from me too. This patch applies cleanly and the tests run.
-
Ryan Bigg October 8th, 2010 @ 03:04 AM
- Importance changed from Low to Medium
-
Jon Leighton October 8th, 2010 @ 03:02 PM
- Tag changed from activerecord, associations, patch to activerecord, associations, patch, verified
-
Jon Leighton October 8th, 2010 @ 03:05 PM
- Assigned user set to Santiago Pastorino
Assigning to Santiago as that's what it says to do here: https://rails.lighthouseapp.com/projects/8994/ticket-assignment
-
Santiago Pastorino October 8th, 2010 @ 06:29 PM
- Assigned user changed from Santiago Pastorino to Aaron Patterson
-
Aaron Patterson October 11th, 2010 @ 06:27 PM
- State changed from new to needs-more-info
Hi @Jon,
It's very difficult for me to review this patch. Could you break it up in to multiple patches please? It looks like you're renaming variables and changing formatting. It's difficult for me to tell whether you're just renaming and reformatting, or actually changing stuff.
Would you mind breaking this up to one patch that does formatting and renaming changes, and one patch that makes any refactors (like new methods, etc)?
Thanks.
-
Jon Leighton October 12th, 2010 @ 11:05 AM
Hi Aaron,
Thanks for taking time to look at this, and sorry that my patch confused you.
I'm happy to split it up if that helps, but I thought perhaps it might be more useful if I provided a better explanation of what the patch actually changes. (Admittedly I should have done this originally.)
- Currently
JoinAssociation
has the methods#relation
,#association_join
and#join_class
. These are used together inActiveRecord::QueryMethods.build_joins
to build up the joins which are necessary to join an association onto a query. - The current situation is that joining an association will
either result in one or two actual SQL
JOIN
statements being generated (depending on whether the association in question involves a join table or not). The way thatJoinAssociation
deals with this is to have#relation
either output a singleArel::Table
for a single join, or to output an array containing twoArel::Table
s, for two joins. In the latter case,#association_join
also outputs an array which has the different conditions for different joins at different positions in the array. - The work I am doing on having arbitrarily nested associations
(x goes through y which in turn goes through z) means that the
current
JoinAssociation
is inflexible for this, because it is geared towards only having either one or two joins. Additionally the code already uses special cases in different situations to achieve its ends (for example, testing whether the return value ofassociation.relation
is an array or not, inbuild_joins
). - I wanted to refactor how
JoinAssociation
andbuild_joins
worked to make them more generic.
This is the basic rationale. In terms of what I actually did:
- As I said, currently
JoinAssociation
has various methods which output various things.build_joins
uses their output to produce the actual join, e.g.relation.join(some_table).on(*some_conditions)
. - Instead of this, I let
JoinAssociation
have a#join_to(relation)
method, with the idea thatJoinAssociation
would now be responsible for adding as many joins to the relation as are necessary.#join_to
would then return a relation with the joins added. This prevents the situation of having to pass around arrays where the different positions in the arrays take on a special meaning, etc. - As you can see in the patch, this makes the code in
build_joins
a lot nicer - instead of all that stuff with building up theto_join
array, we just iterate each of theJoinAssociation
s and dorelation = association.join_to(relation)
I also made some slightly more cosmetic changes, which seemed to 'make sense' as part of it:
- Currently
JoinAssociation
is a subclass ofJoinBase
. Instead I created an abstractJoinPart
class whichJoinAssociation
andJoinBase
both individually inherit from. This seems cleaner to me, as I don't think aJoinAssociation
is a type ofJoinBase
, though they are related. For exampleJoinBase
has thetable_joins
attribute which means nothing toJoinAssociation
. - Due to the addition of this
JoinPart
superclass, I thought it was more consistent to name the collection inJoinDependency
tojoin_parts
instead of justjoins
- I also renamed
JoinAssociation#join_class
tojoin_type
. This is purely me being opinionated about what it should be called. I'm not attached to that name and it doesn't fundamentally matter either way. - I didn't make any sweeping formatting changes or anything like that.
- I didn't change any of the logic which actually builds the joins and their conditions.
I hope this explanation helps. I'm willing to break up the patch if you'd still like me too, but obviously happy to avoid that work if possible :) Let me know how you think it's best to proceed.
Cheers,
Jon - Currently
-
Jon Leighton October 12th, 2010 @ 11:06 AM
Also - if you'd like to have a chat on Skype or anything like that then let me know.
-
Aaron Patterson October 12th, 2010 @ 06:59 PM
Damn! Thanks for the detailed explanation, I really appreciate it. The problem though, is that the first ~200 lines of the patch are mostly formatting and changing variable names. I think there might be one or two lines out of the first 200 that are substantial.
I'm in favor of fixing formatting and changing variable names, but it doesn't help me understand the core of the patch. I like your description of the changes and I am in favor of adding this functionality, but please split this up.
I would really like one patch that does variable renaming and formatting changes, and one that adds / changes functionality. Not just to help me understand what you're changing, but to help people in the future. If there are bugs in your patch, I don't want people to have to wade through a 40k diff to find them.
-
Jon Leighton October 12th, 2010 @ 07:48 PM
Ok, no worries. I will split it up, hopefully within the next 24 hours.
-
Jon Leighton October 13th, 2010 @ 12:32 PM
Okay, here is the split up patch. There are actually four commits now:
- Add 3 new tests for existing functionality, and fix up other
tests affected by changing the fixtures
- Do the "core changes" talked about here
- Renaming/formating changes
- I took the liberty of deleting two methods from JoinAssociation which are not used anywhere.
I have verified that the entire test suite still succeeds after these changes.
Hope this helps.
Cheers
- Add 3 new tests for existing functionality, and fix up other
tests affected by changing the fixtures
-
Aaron Patterson October 13th, 2010 @ 04:33 PM
- State changed from needs-more-info to committed
@Jon awesome. Much easier to review. I've applied all your patches to master and pushed. Thank you very much!
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>
People watching this ticket
Attachments
Referenced by
- 1152 Support for nested has_many :through associations As part of it I needed to do some refactoring. I've submi...