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

issue with primary key other than id #868

Merged
merged 7 commits into from
Sep 28, 2016
Merged

issue with primary key other than id #868

merged 7 commits into from
Sep 28, 2016

Conversation

notEthan
Copy link
Contributor

attempting to reify records with a primary key other than id can run into an error NoMethodError: undefined method 'find_by_id' when the Version#item association does not exist, due to either not being in the default scope, or the record no longer existing.

this fixes use of find_by_id to instead use whatever the primary key is.

I tried to heed rubocop warnings and follow existing testing conventions, but that may need some changes. I had some trouble testing the full matrix, will see what travis does with this PR.

t.text :object
t.datetime :created_at
end
add_index :orange_versions, [:item_type, :item_id]
Copy link
Contributor Author

@notEthan notEthan Sep 27, 2016

Choose a reason for hiding this comment

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

orange is largely modeled after banana, without the Kitchen namespace, and changed to use a string item_id

@notEthan
Copy link
Contributor Author

ugh. I don't know why it is complaining about this

lib/paper_trail/reifier.rb:34:23: C: Space inside { missing.
          find_cond = {klass.primary_key => version.item_id}
                      ^
lib/paper_trail/reifier.rb:34:60: C: Space inside } missing.
          find_cond = {klass.primary_key => version.item_id}
                                                           ^

that is a hash, it should not have spaces

@@ -31,7 +31,8 @@ def reify(version, options)
klass = version_reification_class(version, attrs)
# The `dup` option always returns a new object, otherwise we should
# attempt to look for the item outside of default scope(s).
if options[:dup] || (item_found = klass.unscoped.find_by_id(version.item_id)).nil?
find_cond = { klass.primary_key => version.item_id }
if options[:dup] || (item_found = klass.unscoped.where(find_cond).first).nil?
Copy link
Member

Choose a reason for hiding this comment

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

This part looks good to me.

before_create do
self.uuid ||= SecureRandom.uuid
end
end
Copy link
Member

Choose a reason for hiding this comment

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

Instead of calling this model Orange, what about CustomPrimaryKeyRecord?

We have so many models in our test suite it's getting hard to remember how they differ.

Copy link
Member

@jaredbeck jaredbeck Sep 27, 2016

Choose a reason for hiding this comment

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

Please require "securerandom" at the top of this file.

@@ -0,0 +1,3 @@
class OrangeVersion < PaperTrail::Version
self.table_name = "orange_versions"
end
Copy link
Member

@jaredbeck jaredbeck Sep 27, 2016

Choose a reason for hiding this comment

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

The orange_versions table is necessary because you need a table whose item_id column is a string, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

correct

t.datetime :created_at
end
add_index :orange_versions, [:item_type, :item_id]

Copy link
Member

Choose a reason for hiding this comment

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

When we commit changes to 20110208155312_set_up_test_tables.rb, we also must commit the corresponding changes to test/dummy/db/schema.rb.

@jaredbeck
Copy link
Member

that is a hash, it should not have spaces

This project follows the Ruby Style Guide, see https://github.com/bbatsov/ruby-style-guide#collections

has_paper_trail class_name: "OrangeVersion"
default_scope -> { where(name: "orange") }

self.primary_key = :uuid
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we make this the first declaration within the model?

@notEthan
Copy link
Contributor Author

that is a hash, it should not have spaces

This project follows the Ruby Style Guide, see https://github.com/bbatsov/ruby-style-guide#collections

well, quoting

# good - space after { and before }
{ one: 1, two: 2 }

# good - no space after { and before }
{one: 1, two: 2}

but I have changed it.

@notEthan
Copy link
Contributor Author

I'm not sure how to get the schema.rb updated, I have had trouble getting the tests to run locally in many cases. I think I have addressed the rest of the feedback, can you help me with the schema.rb bit?

@notEthan
Copy link
Contributor Author

hm. item_id as string doesn't seem to work so well with postgres. investigating further ...

@jaredbeck
Copy link
Member

can you help me with the schema.rb bit?

I think I ususally cd into test/dummy and run db:migrate.

@notEthan
Copy link
Contributor Author

okay, so this error:

PG::UndefinedFunction: ERROR:  operator does not exist: character varying = integer
LINE 1: ...RE "custom_primary_key_record_versions"."item_id" = 651 AND ...
                                                             ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
: SELECT  "custom_primary_key_record_versions".* FROM "custom_primary_key_record_versions"  WHERE "custom_primary_key_record_versions"."item_id" = 651 AND "custom_primary_key_record_versions"."item_type" = 'CustomPrimaryKeyRecord' ORDER BY "custom_primary_key_record_versions"."created_at" DESC, "custom_primary_key_record_versions"."id" DESC LIMIT 1

was just an issue with how I was specifying the uuid primary key, in a way that wasn't supported by older activerecord, and uuid was being incorrectly created as an int instead of string.

that does surface an issue that if you have a versions table with a string versions.item_id, it can only be used with string primary key fields in postgres. if you have an application you perhaps started with traditional numeric id fields but are transitioning to a uuid (or any string) primary key, you won't be able to use the same versions table to track both sorts of models.

I don't have a solution for that (perhaps smart type casting in a query somewhere - not sure what capability activerecord even offers in that regard), just noting it as it occurs to me.

@notEthan
Copy link
Contributor Author

seems to be largely passing, I think feedback is addressed. Gem::InstallError: pg requires Ruby version >= 2.0.0. on 1.9.3 appears unrelated to this pull request

@jaredbeck
Copy link
Member

.. Gem::InstallError: pg requires Ruby version >= 2.0.0. on 1.9.3 appears unrelated to this pull request

Correct, and fixed by #870. Please rebase. It would be great if you could get the tests green before we review again. Let us know if you have trouble, thanks.

@notEthan
Copy link
Contributor Author

💥 rebased, green

self.primary_key = :uuid

has_paper_trail class_name: "CustomPrimaryKeyRecordVersion"
default_scope -> { where(name: "custom_primary_key_record") }
Copy link
Member

Choose a reason for hiding this comment

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

What's the purpose of this default_scope?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it is to prevent the record from being returned on version.item. this bug surfaces in the else path of https://github.com/airblade/paper_trail/blob/v5.2.2/lib/paper_trail/reifier.rb#L25-L40

version.item returns nil when the item is destroyed, and when a default scope on the model prevents the association from returning it. I added this default scope to test both of those cases.

Copy link
Member

Choose a reason for hiding this comment

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

ok that makes sense. Please add your explanation as a comment above the default_scope.

Also, please add a line in CHANGELOG.md and I think we'll be done here.

@jaredbeck
Copy link
Member

This all looks good, my only question is what is the purpose of the default_scope in custom_primary_key_record.rb?

@notEthan
Copy link
Contributor Author

added explanatory comment and CHANGELOG entry

@jaredbeck jaredbeck merged commit c900f2d into paper-trail-gem:master Sep 28, 2016
@jaredbeck
Copy link
Member

Merged, thanks!

@notEthan
Copy link
Contributor Author

cool, thanks

batter pushed a commit that referenced this pull request Nov 29, 2016
* support reifying using primary_key field other than 'id'

* custom_primary_key_record - model to use a uuid primary key, with a weird default scope

* test custom_primary_key_record also with destroyed record

* name index on custom_primary_key_record_versions item; default is too long for mysql

* specify custom_pk_record uuid as a string primary key in a way that AR 3 supports - previous way worked with 4 and 5 but not 3

* update schema for custom_primary_key_records

* note fix in changelog
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