Skip to content
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

Open
wants to merge 12 commits into
base: sti-join-bug
Choose a base branch
from

Conversation

lorint
Copy link

@lorint lorint commented Aug 17, 2018

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!)

@lorint lorint changed the base branch from master to sti-join-bug August 17, 2018 23:08
@lorint
Copy link
Author

lorint commented Aug 18, 2018

This last commit provides a slightly cleaner approach to allow the correct item_type to persist during create.

# 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?
Copy link
Owner

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?

Copy link
Owner

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.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

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.

Copy link
Owner

@seanlinsley seanlinsley Aug 21, 2018

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?

Copy link
Author

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.

@lorint
Copy link
Author

lorint commented Aug 19, 2018

(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.

Copy link

@jaredbeck jaredbeck left a 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.

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)

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.

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

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.

super
end
end
})

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.

Copy link
Author

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.


# Add a STI-ish class on-the-fly
class Management < Customer
end

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

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.

Copy link
Author

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.

@jaredbeck
Copy link

Every year that goes by, I dislike STI more and more.

@jaredbeck
Copy link

Maybe we should switch gears and go with adding an item_reification_type to our versions table. Then we wouldn't have to jump through any of these hoops, right?

jaredbeck added a commit to paper-trail-gem/paper_trail that referenced this pull request Aug 22, 2018
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
@jaredbeck
Copy link

Maybe we should switch gears and go with adding an item_reification_type to our versions table. Then we wouldn't have to jump through any of these hoops, right?

I have implemented this in paper-trail-gem#1143. Please let me know what you think there.

@lorint
Copy link
Author

lorint commented Aug 22, 2018

Every year that goes by, I dislike STI more and more.

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!

aried3r pushed a commit to aried3r/paper_trail that referenced this pull request Dec 14, 2020
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants