-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make STI .joins() fix more consistent with existing tests #1
base: sti-join-bug
Are you sure you want to change the base?
Conversation
This last commit provides a slightly cleaner approach to allow the correct |
lib/paper_trail/model_config.rb
Outdated
# back to base_class at the final stages when Association#creation_attributes | ||
# gets called. | ||
has_manys[klass.versions_association_name.to_s].define_singleton_method(:collection?) do | ||
active_record.descends_from_active_record? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How confident can we be that collection?
will continue to be used by Rails in this way? Why does Rails have this method in the first place, just to return true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How often does collection?
end up getting called? I wonder if it's worth the effort to memoize the result of descends_from_active_record?
so it's not calculated all the time.
BTW, when monkeypatching code I like to add a link to the version-specific lines of code like this: https://github.com/rails/rails/blob/v5.2.1/activerecord/lib/active_record/associations/association.rb#L198-L210
Since creation_attributes
only sets the foreign key & type attributes, wouldn't it be more expressive to override it directly?
has_manys[klass.versions_association_name.to_s].singleton_class.prepend(Module.new {
def creation_attributes
if active_record.descends_from_active_record?
super # sets `item_type` and `item_id`
else
{} # we set these elsewhere. Rails would incorrectly set them to the STI base class.
end
end
})
That raises the question: why don't we always pass in the correct item_type
via this monkeypatch? If we're going to patch Rails, it might as well be consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CC @jaredbeck
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How confident can we be that
collection?
will continue to be used by Rails in this way?
It's been used in this way since Rails v4.0, and before that a very similar test was used, albeit one that wouldn't work out with our override:
if reflection.macro.in?([:has_one, :has_many]) && !options[:through]
Since
creation_attributes
only sets the foreign key & type attributes, wouldn't it be more expressive to override it directly?
At first I had hoped to have a solution that utilised this approach ... but it requires a little more code. The only way I see it working is as a class method added to the HasManyAssociation class, and inside a test to see if we're dealing with a PaperTrail::Version. For fun I've put that together in the latest commit. 10 lines of stuff as opposed to this little 4-liner.
I'm certainly OK with having this either way -- both approaches establish the same net result.
why don't we always pass in the correct item_type via this monkeypatch?
It's very specific just to PaperTrail::Version objects, and only has effect for anything subclassed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re: paper-trail-gem#1137 (comment)
Are we okay with globally patching creation_attributes
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good points on creation_attributes
-- have gone back to directly patching collection?
on the specific :verisons HasManyReflection
objects that are actually subclassed. Much less intrusive.
(Your last review above became outdated, and if you expand it then you'll find three answers to the various points you've brought up.) I'm certainly OK with having the fix for .create() events reverting back to base_class done either with an override to .collection?() or to .creation_attributes() -- both of these establish the same net result. In this latest commit I've done it the .creation_attributes() way. Hmmm, that reminds me that I haven't put in a comment with a link to the original Rails code! Will do that now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just an initial review, and I'm jumping into this in the middle of the discussion, so please keep that in mind when reading my comments. Maybe I'm missing something. That said, I'm concerned by the direction this is going, and I haven't even reviewed Sean's main PR yet.
I'd like to support Sean's use-case, but not if it dramatically increases the complexity of PT.
lib/paper_trail/model_config.rb
Outdated
item_type = klass.paper_trail.send(:versions_association_item_type) | ||
relation = relation.unscope(where: :item_type).where(item_type: item_type) | ||
relation | ||
order!(model.timestamp_sort_order) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
order!
is tagged :nodoc:
meaning that it is private API. The rails team could remove it at any time without warning. I don't think we should use it.
lib/paper_trail/model_config.rb
Outdated
order!(model.timestamp_sort_order) | ||
unless klass.descendants.any? && klass.columns.exclude?(klass.inheritance_column) | ||
unscope(where: :item_type).where(item_type: klass.name) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't fully understand this unless
condition and I could use an explanation comment, pretty please.
lib/paper_trail/model_config.rb
Outdated
super | ||
end | ||
end | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really uncomfortable with patching rails. PT does not currently patch rails at all and I don't want to start.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completely agree -- I'm just trying to keep everyone happy here. Sean's project uses STI in a way in which he wants changes made to Management
objects to end up with an item_type
of Company
instead, so he suggested a workaround where if there is no defined inheritance_column then it would revert back to pre-1108 behaviour. This patch is only necessary because of this edge case, combined with a hope to have .joins() work on the versions
association.
I would LOVE to have total consistency in how PaperTrail operates, and zero reason to have to patch Rails. Perhaps instead of throwing the baby out with the bathwater in 1143 then we can encourage the use of STI as it was originally conceived -- along with a type
column so that a single object doesn't temporarily masquerade as another.
spec/models/management_spec.rb
Outdated
|
||
# Add a STI-ish class on-the-fly | ||
class Management < Customer | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put models in spec/dummy_app/app/models
@@ -1,6 +1,10 @@ | |||
# frozen_string_literal: true | |||
|
|||
class CallbackModifier < ActiveRecord::Base | |||
# These guys need to look just a _little_ more like a real STI class in order for the tests to | |||
# call modifier.versions(). | |||
attr_accessor :type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems highly unusual. Is this how your app works, Sean? I've never heard of this technique and I'm not surprised that PT was confused by it. My gut reaction is that the amount of complexity I'd be willing to introduce into PT to support this use-case is minimal. I certainly am not interested in patching rails to support this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is in relation to the workaround that triggers pre-1108 behaviour.
Perhaps we can have a flag so people can choose base_class or real class. I'm not totally onboard with us always storing both.
Every year that goes by, I dislike STI more and more. |
Maybe we should switch gears and go with adding an |
This partially reverts commit 58369e1. I have kept the specs, skipped. Per the following, this approach does not seem to be working: - #1135 - #1137 - seanlinsley#1
I have implemented this in paper-trail-gem#1143. Please let me know what you think there. |
Relational data sets never have expressed the concept of inheritance very cleanly. Props to DHH and the crew for giving it a go. Anything that can steer people away from the temptation of architecting a NoSQL thing is beneficial as a project matures. (Have seen so many that have to be rewritten away from Mongo, it's crazy!) I will say that STI is usually a little less obnoxious than gratuitous use of polymorphism! When it's appropriate then it's good. I mean, with PaperTrail's Version it is obviously a big win. But wow, when people try to abstract problem statements with similar-sounding associations via polymorphic inheritance, it can get out of hand pretty quickly! |
This partially reverts commit 58369e1. I have kept the specs, skipped. Per the following, this approach does not seem to be working: - paper-trail-gem#1135 - paper-trail-gem#1137 - seanlinsley#1
What do you think about this approach?
BTW it had worked fine with all the
.count
changes that had been made in the tests, and also works with the original.length
and.size
calls, so I reverted the examples to how they were originally. (Keeping your two.joins()
tests in place of course!)