-
-
Notifications
You must be signed in to change notification settings - Fork 372
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
Reject sequence definitions for Active Record primary keys #419
Conversation
b2911ab
to
b1a9f88
Compare
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.
Nice, into it.
The next steps, as I understand it:
|
The test suite passes locally, but fails in CI. I'm investigating. |
I assume this is because factory_bot isn't released yet. |
is this fixing issues with upcoming Rails 7.1 ? |
More details in thoughtbot/factory_bot#1586 (comment). The short version is: no, it's not fixing anything. It is instead making sure people don't use sequences for something that is generated by the DB, because that will break tests, and because we could not think of a reason to do so. Nothing is merged yet, so if you can think of a usecase then we'd like to know soon. |
factory_bot 6.3.0 is out with support for the |
0586014
to
236a179
Compare
@mike-burns thanks for pushing that through! CI seems to be content with newer versions of Ruby, but failing on 2.7 and below. I'm not sure why. |
08e6b62
to
ac31bf6
Compare
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.
Whew, looks great. Thanks for your patience while we figured out the right solution.
@@ -0,0 +1,67 @@ | |||
require "active_record" | |||
|
|||
module DefineConstantMacros |
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.
Thanks for adding these.
ac31bf6
to
341bf88
Compare
Alternative to [thoughtbot/factory_bot#1586][] Depends on [thoughtbot/factory_bot#1587][] First, introduce the `FactoryBotRails::FactoryValidator` class to serve as a generic `factory_bot.compile_factory` event observer. Throughout the lifecycle of the `FactoryBotRails::Railtie`, afford various initializers with opportunities to add purpose-built validators to the instance's internal set. When `factory_bot.compile_factory` events are published, iterate through the list of validators and forward along the value of the [event.payload][] to the validator's `#validate!` method. Next, introduce the Active Record-specific `FactoryBotRails::FactoryValidator::ActiveRecordValidator` class. Only require the module whenever [Active Record's engine is loaded][on_load]. The `ActiveRecordValidator#validate!` method rejects attributes that define primary key generation logic for `ActiveRecord::Base` descendants. In order to test this behavior, add a development dependency on `sqlite3` and `activerecord`, along with some model and database table generating helper methods. [thoughtbot/factory_bot#1586]: thoughtbot/factory_bot#1586 [thoughtbot/factory_bot#1587]: thoughtbot/factory_bot#1587 [event.payload]: module-ActiveSupport::Notifications-label-Subscribers [on_load]: https://guides.rubyonrails.org/engines.html#avoid-loading-rails-frameworks
341bf88
to
b965bb8
Compare
@seanpdoyle This causes problems for us, as we have a table with a primary key that is not generated by the database (those two are not 100% the same thing). Would it be possible to check the schema instead of the ModelKlass.column_for_attribute(primary_key_name).serial? This seems to work for me - not sure if this is Postgres-specific. |
Ah I see, we should check for |
I agree, this conditional should account for whether or not the database is generating the value itself. Unfortunately, I can't find any documentation for Similarly, there is a Column#auto_populated? predicate defined as public interface that Postgres implements in terms of Is there another way to determine whether or not a column is managed by the database that works with older versions? |
I ran into the same issue, with a model that has a primary key not generated by the database. |
We have at least model (Language) which does not use datbase generated primary keys so we need to be able to set them. thoughtbot/factory_bot_rails#419
@mike-burns, I'm way late to the party, but I just wanted to chime in. We have a usecase where we use FB to generate example JSON payloads (using For example, we have a factory defined like this: FactoryBot.define do
factory :team do
sequence(:name) { |n| "Generic Team #{n}" }
sequence(:slug) { |n| "team_#{n}" }
time_zone { nil }
locale { nil }
factory :team_example do
id { 42 }
sequence(:name) { |n| "EXAMPLE Generic Team #{n}" }
sequence(:slug) { |n| "EXAMPLE team_#{n}" }
time_zone { ActiveSupport::TimeZone.all.first.name }
locale { "en" }
created_at { DateTime.new(2023, 1, 1) }
updated_at { DateTime.new(2023, 1, 2) }
end
end
end And then to demonstrate what a team = FactoryBot.build(:team_example)
payload = team.as_json Ideally we'd like for the (BTW, I'm aware that this is a weird way to use FactoryBot that it's at best an unintended use, and may be drifting into "flagrant misuse" territory.) |
This one bit me too... just trying to figure out why this was released in a minor version when it's a breaking change for people who use sequences that also happen to be primary keys. In my particular use case, we use varchar primary keys that are imported from another system, but when running specs, we just coerce a serial (integer) to a string with an arbitrary prefix. For now, I've just used |
I haven't forgotten about this. My plan is to revert the hard rejection and add it as a configurable lint. I'd also like it to only happen for serial/autoincrementing columns. |
@mike-burns not sure how others feel about this, but I’m okay with the rejection as long as it doesn’t happen for primary keys that aren’t actually serial. As long as that’s reliable, I think the error is sensible enough. |
Just to mention again: some attributes are database-generated but aren't serial/autoincrementing (but rather go through a default function). It seems like it would be great to keep things consistent using something like (A per-factory opt-out mechanism could also be good!) |
We should switch to using
As a second step, sequence(:id, intentionally_override_auto_populated_database_column: true) { |n| "#{n}#{n}" } |
For now I've removed it: #451. |
Alternative to thoughtbot/factory_bot#1586
Depends on thoughtbot/factory_bot#1587
First, introduce the
FactoryBotRails::FactoryValidator
class to serve as a genericfactory_bot.compile_factory
event observer.Throughout the lifecycle of the
FactoryBotRails::Railtie
, afford various initializers with opportunities to add purpose-built validators to the instance's internal set.When
factory_bot.compile_factory
events are published, iterate through the list of validators and forward along the value of the event.payload to the validator's#validate!
method.Next, introduce the Active Record-specific
FactoryBotRails::FactoryValidator::ActiveRecordValidator
class. Only require the module whenever Active Record's engine is loaded.The
ActiveRecordValidator#validate!
method rejects attributes that define primary key generation logic forActiveRecord::Base
descendants.In order to test this behavior, add a development dependency on
sqlite3
andactiverecord
, along with some model and database table generating helper methods.