-
Notifications
You must be signed in to change notification settings - Fork 125
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
Migrate existing dialog field association data to use new relationship #80
Migrate existing dialog field association data to use new relationship #80
Conversation
9a6e96c
to
fb00efd
Compare
fb00efd
to
a3040d0
Compare
@@ -0,0 +1,31 @@ | |||
class MigrateDialogFieldAssociationsToUseNewRelationship < ActiveRecord::Migration[5.0] | |||
Rails.application.eager_load! |
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.
nope!
Rails.application.eager_load! | ||
|
||
def up | ||
Dialog.all.each do |dialog| |
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.
Create a stub model, as is done in other data migrations, for Dialog and DialogFieldAssociation
def up | ||
Dialog.all.each do |dialog| | ||
fields = dialog.dialog_fields | ||
dialog_fields_with_associations = fields.select { |df| !df.auto_refresh.nil? || !df.trigger_auto_refresh.nil? }.sort { |n| n.position * -1 } |
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.
!df.auto_refresh.nil? || !df.trigger_auto_refresh.nil?
simplifies to
df.auto_refresh || df.trigger_auto_refresh
... Also, should those be auto_refresh?
and trigger_auto_refresh?
(with trailing ?
)
.sort { |n| n.position * -1 }
simplifies to
.sort { |n| -n.position }
|
||
def up | ||
Dialog.all.each do |dialog| | ||
fields = dialog.dialog_fields |
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.
you might want an includes
in the original query or this is going to be a nasty N+1
Dialog.all.each do |dialog| | ||
fields = dialog.dialog_fields | ||
dialog_fields_with_associations = fields.select { |df| !df.auto_refresh.nil? || !df.trigger_auto_refresh.nil? }.sort { |n| n.position * -1 } | ||
triggers = dialog_fields_with_associations.select { |df| df.trigger_auto_refresh == 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.
if trigger_auto_refresh is a boolean column, this simplifies to
.select(&:trigger_auto_refresh)
(same with the next line)
triggers = dialog_fields_with_associations.select { |df| df.trigger_auto_refresh == true } | ||
responders = dialog_fields_with_associations.select { |df| df.auto_refresh == true } | ||
triggers.each_with_index do |t, index| | ||
responder_fields_for_t = if !triggers[index + 1].nil? |
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.
Prefer coding in the positive as opposed to ! nil?
because double-negative are hard for people to reason about. So,
responder_fields_for_t =
if triggers[index + 1]
responders.select { |r| r.position
....
Don't forget the specs 😄 All data migrations require specs. |
108a22a
to
ca220ba
Compare
@eclarizio just working on the testing for this one now |
@miq-bot assign @eclarizio |
ca220ba
to
33f510e
Compare
a244cf4
to
406045f
Compare
before(:each) do | ||
dialog_group_stub.create!(:id => 2, :dialog_tab_id => 8, :position => 7) | ||
dialog_tab_stub.create!(:id => 8, :dialog_id => 10, :position => 4) | ||
dialog_stub.create!(:id => 10) |
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.
Do not use manual ids because it bypasses proper region testing.
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.
Instead create the record, let ActiveRecord create a proper id, then use that id after the fact to update the associations.
let(:dialog_field_association_stub) { migration_stub(:DialogFieldAssociation) } | ||
|
||
migration_context :up do | ||
before(:each) do |
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.
:each is redundant, so you can remove it.
index = field_position + dialog_group_position * 100 + dialog_tab_position * 10_000 | ||
positions << {:id => f.id, :position => index} | ||
end | ||
positions |
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.
prefer .collect over the
x = []
foo.each { x << y }
x
pattern.
x = foo.collect { y }
field_position = f.position | ||
dialog_group_position = DialogGroup.find(f.dialog_group_id).position | ||
dialog_tab_position = DialogTab.find(DialogGroup.find(f.dialog_group_id).dialog_tab_id).position | ||
index = field_position + dialog_group_position * 100 + dialog_tab_position * 10_000 |
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.
Why these numbers? Are they just arbitrarily for segmenting? I only ask because I remember a customer with over 100 fields on a form, and so this could cause issues. Even so, since you are updating the position, I still think it's weird to segment and then leave it that way...I would expect something to post-process the "weird" numbers, and put them into "normal" numbers by sorting and mapping the indexes or something.
|
||
def responder_range(trigger_min, trigger_max) | ||
min = trigger_min[:position] + 1 | ||
max = trigger_max.present? ? trigger_max[:position] - 1 : 10_000_000 |
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.
Also curious about this 10 million number.
end | ||
|
||
def down | ||
DialogFieldAssociation.delete_all |
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.
Will the previous migration just drop the table? If so, then there's no need for this at all. (You could just put a comment stating that fact)
end | ||
|
||
def dialog_fields_with_associations_by_dialog(dialog_fields_with_associations) | ||
dialog_fields_with_associations.group_by { |df| Dialog.find(DialogTab.find(DialogGroup.find(DialogField.find(df.id).dialog_group_id).dialog_tab_id).dialog_id) } |
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 looks like an N+1 nightmare. There are much better approaches for querying all the data, then mixing and matching (we do something very similar with the RelationshipMixin for child objects. I can sit with you and show you.
dialog_fields.each do |f| | ||
field_position = f.position | ||
dialog_group_position = DialogGroup.find(f.dialog_group_id).position | ||
dialog_tab_position = DialogTab.find(DialogGroup.find(f.dialog_group_id).dialog_tab_id).position |
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.
Also an N+1 issue (on both of these lines)
78e5e20
to
3687054
Compare
@gmcculloug can you take a quick looksee at this, 'specially the specs, again please? |
.where(:auto_refresh => true) | ||
.or(DialogField.where(:trigger_auto_refresh => true)) | ||
.includes(:dialog_group => {:dialog_tab => :dialog}) | ||
.group_by { |f| f.dialog_group.dialog_tab.dialog } |
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.
@Fryguy had the suggest to protect against this relationship chain hitting a nil
here and I would agree. If we use try
and allow the fields to get grouped together until nil
we can throw those fields away before processing.
cbe0bd9
to
73f524a
Compare
|
||
migration_context :down do | ||
it "should delete dialog field associations" do | ||
migrate |
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 should create a dialog_field_association record before the migrate, otherwise you are testing nothing.
minor problem with the down migration spec but otherwise looks good to me 👍 |
e41ebc4
to
035c632
Compare
035c632
to
a860e35
Compare
Checked commit d-m-u@a860e35 with ruby 2.3.3, rubocop 0.47.1, and haml-lint 0.20.0 |
The planned targeted auto-refresh enhancement to dialog fields requires the dialog fields to be able to be associated to one another. This migration takes our old system of relating dialog fields by order-dependent auto_refresh and trigger_auto_refresh fields and updates them to use the recently added DialogFieldAssociation table. It's worth noting that this may not upgrade all existing dialog associations correctly and that it may be necessary to manually check and possibly fix some of the associations not caught by this migration.