-
-
Notifications
You must be signed in to change notification settings - Fork 357
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
insert_at respects unique not null check (>= 0) db constraints #246
Changes from 16 commits
6d7ddae
567f867
8cd67ca
17d5ed2
9aeaa2d
dac0b19
dbac159
a89b21f
fa4c678
1f5aedc
51bd155
f529ad0
0e2ed61
49f5df6
dbf1807
83cdf79
674aa88
3392191
58e81c2
0dc8572
516053b
8036261
2ddb04c
c7d238b
1093911
e4e0802
49c24b5
56fd879
88d9af1
5eb3e6e
ec8f582
27e688a
1d458aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -317,32 +317,53 @@ def increment_positions_on_all_items | |
# Reorders intermediate items to support moving an item from old_position to new_position. | ||
def shuffle_positions_on_intermediate_items(old_position, new_position, avoid_id = nil) | ||
return if old_position == new_position | ||
scope = acts_as_list_list | ||
scope = acts_as_list_list.select(:id) | ||
|
||
if avoid_id | ||
scope = scope.where("#{quoted_table_name}.#{self.class.primary_key} != ?", self.class.connection.quote(avoid_id)) | ||
end | ||
|
||
# unique constraint prevents regular increment_all and forces to do increments one by one | ||
# http://stackoverflow.com/questions/7703196/sqlite-increment-unique-integer-field | ||
# both SQLite and PostgreSQL (and most probably MySQL too) has same issue | ||
update_one_by_one = acts_as_list_list.connection.index_exists?(acts_as_list_list.table_name, position_column, unique: true) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm thinking this should be defined more globally (even though it's only used here) as it feels more like a configuration option. What do you think? You could set it at initialisation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At first I thought that if you have unique index this is not an option, really, but a requirement. Still someone may want to disable this for performance reasons iif DB has deferred unique check on this table, and this will work too. Preparing update to make :sequential_updates option. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, this option could be used in other methods in future, definitely the way to go, thanks for the feedback. |
||
|
||
if old_position < new_position | ||
# Decrement position of intermediate items | ||
# | ||
# e.g., if moving an item from 2 to 5, | ||
# move [3, 4, 5] to [2, 3, 4] | ||
scope.where( | ||
items = scope.where( | ||
"#{quoted_position_column_with_table_name} > ?", old_position | ||
).where( | ||
"#{quoted_position_column_with_table_name} <= ?", new_position | ||
).decrement_all | ||
) | ||
|
||
if update_one_by_one | ||
items.order("#{quoted_position_column_with_table_name} ASC").pluck(:id).each do |id| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are you plucking the id's only to re-find the records in order to decrement etc...? You could call |
||
acts_as_list_list.find(id).decrement!(position_column) | ||
end | ||
else | ||
items.decrement_all | ||
end | ||
else | ||
# Increment position of intermediate items | ||
# | ||
# e.g., if moving an item from 5 to 2, | ||
# move [2, 3, 4] to [3, 4, 5] | ||
scope.where( | ||
items = scope.where( | ||
"#{quoted_position_column_with_table_name} >= ?", new_position | ||
).where( | ||
"#{quoted_position_column_with_table_name} < ?", old_position | ||
).increment_all | ||
) | ||
|
||
if update_one_by_one | ||
items.order("#{quoted_position_column_with_table_name} DESC").pluck(:id).each do |id| | ||
acts_as_list_list.find(id).increment!(position_column) | ||
end | ||
else | ||
items.increment_all | ||
end | ||
end | ||
end | ||
|
||
|
@@ -352,23 +373,19 @@ def insert_at_position(position) | |
if in_list? | ||
old_position = send(position_column).to_i | ||
return if position == old_position | ||
shuffle_positions_on_intermediate_items(old_position, position) | ||
# temporary move after bottom with gap, avoiding duplicate values | ||
# gap is required to leave room for position increments | ||
# positive number will be valid with unique not null check (>= 0) db constraint | ||
tmp_position_after_everything = acts_as_list_class.unscoped.maximum(position_column).to_i + 2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this unscoping remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I want exactly this to bypass unique index on table column constraint. Still, you're right that it's not needed. There could be no such situation when different scopes have same positions in one table with unique index. |
||
set_list_position(tmp_position_after_everything) | ||
shuffle_positions_on_intermediate_items(old_position, position, id) | ||
else | ||
increment_positions_on_lower_items(position) | ||
end | ||
set_list_position(position) | ||
end | ||
end | ||
|
||
# used by insert_at_position instead of remove_from_list, as postgresql raises error if position_column has non-null constraint | ||
def store_at_0 | ||
if in_list? | ||
old_position = send(position_column).to_i | ||
set_list_position(0) | ||
decrement_positions_on_lower_items(old_position) | ||
end | ||
end | ||
|
||
def update_positions | ||
old_position = send("#{position_column}_was") || bottom_position_in_list + 1 | ||
new_position = send(position_column).to_i | ||
|
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 you can remove the
pluck
then you can remove this too I think.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.
Yes, pluck will be removed, it's redundant. Still, wouldnt it be performance-wise to only select ids for repositioning? Otherwise DB response will be a little bit larger and slower.