Order board lists by list_type, and position

parent 35420cec
class Board < ActiveRecord::Base class Board < ActiveRecord::Base
belongs_to :project belongs_to :project
has_many :lists, dependent: :destroy has_many :lists, -> { order(:list_type, :position) }, dependent: :destroy
validates :project, presence: true validates :project, presence: true
end end
...@@ -2,9 +2,9 @@ class List < ActiveRecord::Base ...@@ -2,9 +2,9 @@ class List < ActiveRecord::Base
belongs_to :board belongs_to :board
belongs_to :label belongs_to :label
enum list_type: { label: 0, backlog: 1, done: 2 } enum list_type: { backlog: 0, label: 1, done: 2 }
validates :board, :list_type, :position, presence: true validates :board, :list_type, presence: true
validates :label, presence: true, if: :label? validates :label, :position, presence: true, if: :label?
validates :position, numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :position, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :label?
end end
...@@ -15,8 +15,8 @@ def execute ...@@ -15,8 +15,8 @@ def execute
def create_board! def create_board!
project.create_board project.create_board
project.board.lists.create(list_type: :backlog, position: 0) project.board.lists.create(list_type: :backlog)
project.board.lists.create(list_type: :done, position: 1) project.board.lists.create(list_type: :done)
end end
end end
end end
...@@ -19,10 +19,7 @@ def execute ...@@ -19,10 +19,7 @@ def execute
attr_reader :board, :params attr_reader :board, :params
def find_next_position def find_next_position
return 0 unless board.lists.any? board.lists.label.maximum(:position).to_i + 1
records = board.lists.where.not(list_type: List.list_types[:done])
records.maximum(:position).to_i + 1
end end
def create_list_at(position) def create_list_at(position)
...@@ -30,8 +27,8 @@ def create_list_at(position) ...@@ -30,8 +27,8 @@ def create_list_at(position)
end end
def increment_higher_lists(position) def increment_higher_lists(position)
board.lists.where('position >= ?', position) board.lists.label.where('position >= ?', position)
.update_all('position = position + 1') .update_all('position = position + 1')
end end
end end
end end
......
...@@ -10,7 +10,7 @@ def execute ...@@ -10,7 +10,7 @@ def execute
return false unless list.label? return false unless list.label?
list.with_lock do list.with_lock do
reorder_higher_lists decrement_higher_lists
remove_list remove_list
end end
end end
...@@ -23,9 +23,9 @@ def list ...@@ -23,9 +23,9 @@ def list
@list ||= board.lists.find(params[:list_id]) @list ||= board.lists.find(params[:list_id])
end end
def reorder_higher_lists def decrement_higher_lists
board.lists.where('position > ?', list.position) board.lists.label.where('position > ?', list.position)
.update_all('position = position - 1') .update_all('position = position - 1')
end end
def remove_list def remove_list
......
...@@ -8,7 +8,7 @@ def initialize(project, params = {}) ...@@ -8,7 +8,7 @@ def initialize(project, params = {})
def execute def execute
return false unless list.label? return false unless list.label?
return false if invalid_position? return false unless valid_move?
list.with_lock do list.with_lock do
reorder_intermediate_lists reorder_intermediate_lists
...@@ -24,18 +24,9 @@ def list ...@@ -24,18 +24,9 @@ def list
@list ||= board.lists.find(params[:list_id]) @list ||= board.lists.find(params[:list_id])
end end
def invalid_position? def valid_move?
return true if new_position.blank? new_position.present? && new_position != old_position &&
new_position >= 0 && new_position <= board.lists.label.size
[old_position, first_position, last_position].include?(new_position)
end
def first_position
board.lists.first.try(:position)
end
def last_position
board.lists.last.try(:position)
end end
def old_position def old_position
...@@ -55,15 +46,15 @@ def reorder_intermediate_lists ...@@ -55,15 +46,15 @@ def reorder_intermediate_lists
end end
def decrement_intermediate_lists def decrement_intermediate_lists
board.lists.where('position > ?', old_position) board.lists.label.where('position > ?', old_position)
.where('position <= ?', new_position) .where('position <= ?', new_position)
.update_all('position = position - 1') .update_all('position = position - 1')
end end
def increment_intermediate_lists def increment_intermediate_lists
board.lists.where('position >= ?', new_position) board.lists.label.where('position >= ?', new_position)
.where('position < ?', old_position) .where('position < ?', old_position)
.update_all('position = position + 1') .update_all('position = position + 1')
end end
def update_list_position def update_list_position
......
...@@ -7,8 +7,8 @@ def change ...@@ -7,8 +7,8 @@ def change
create_table :lists do |t| create_table :lists do |t|
t.references :board, index: true, foreign_key: true, null: false t.references :board, index: true, foreign_key: true, null: false
t.references :label, index: true, foreign_key: true t.references :label, index: true, foreign_key: true
t.integer :list_type, null: false, default: 0 t.integer :list_type, null: false, default: 1
t.integer :position, null: false t.integer :position
t.timestamps null: false t.timestamps null: false
end end
......
...@@ -544,8 +544,8 @@ ...@@ -544,8 +544,8 @@
create_table "lists", force: :cascade do |t| create_table "lists", force: :cascade do |t|
t.integer "board_id", null: false t.integer "board_id", null: false
t.integer "label_id" t.integer "label_id"
t.integer "list_type", default: 0, null: false t.integer "list_type", default: 1, null: false
t.integer "position", null: false t.integer "position"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
end end
......
...@@ -2,18 +2,22 @@ ...@@ -2,18 +2,22 @@
factory :list do factory :list do
board board
label label
sequence(:position)
end end
factory :backlog_list, parent: :list do factory :backlog_list, parent: :list do
list_type :backlog list_type :backlog
label nil
position nil
end end
factory :label_list, parent: :list do factory :label_list, parent: :list do
list_type :label list_type :label
sequence(:position)
end end
factory :done_list, parent: :list do factory :done_list, parent: :list do
list_type :done list_type :done
label nil
position nil
end end
end end
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
describe Board do describe Board do
describe 'relationships' do describe 'relationships' do
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:lists).dependent(:destroy) } it { is_expected.to have_many(:lists).order(list_type: :asc, position: :asc).dependent(:destroy) }
end end
describe 'validations' do describe 'validations' do
......
...@@ -13,16 +13,18 @@ ...@@ -13,16 +13,18 @@
it { is_expected.to validate_presence_of(:position) } it { is_expected.to validate_presence_of(:position) }
it { is_expected.to validate_numericality_of(:position).only_integer.is_greater_than_or_equal_to(0) } it { is_expected.to validate_numericality_of(:position).only_integer.is_greater_than_or_equal_to(0) }
it 'does not require label to be set when list_type is set to backlog' do context 'when list_type is set to backlog' do
subject.list_type = :backlog subject { described_class.new(list_type: :backlog) }
expect(subject).not_to validate_presence_of(:label) it { is_expected.not_to validate_presence_of(:label) }
it { is_expected.not_to validate_presence_of(:position) }
end end
it 'does not require label to be set when list_type is set to done' do context 'when list_type is set to done' do
subject.list_type = :done subject { described_class.new(list_type: :done) }
expect(subject).not_to validate_presence_of(:label) it { is_expected.not_to validate_presence_of(:label) }
it { is_expected.not_to validate_presence_of(:position) }
end end
end end
end end
...@@ -9,16 +9,16 @@ ...@@ -9,16 +9,16 @@
subject(:service) { described_class.new(project, label_id: label.id) } subject(:service) { described_class.new(project, label_id: label.id) }
context 'when board lists is empty' do context 'when board lists is empty' do
it 'creates a new list at begginning of the list' do it 'creates a new list at beginning of the list' do
list = service.execute list = service.execute
expect(list.position).to eq 0 expect(list.position).to eq 1
end end
end end
context 'when board lists has a backlog list' do context 'when board lists has only a backlog list' do
it 'creates a new list at end of the list' do it 'creates a new list at beginning of the list' do
create(:backlog_list, board: board, position: 0) create(:backlog_list, board: board)
list = service.execute list = service.execute
...@@ -26,9 +26,10 @@ ...@@ -26,9 +26,10 @@
end end
end end
context 'when board lists are only labels lists' do context 'when board lists has only labels lists' do
it 'creates a new list at end of the list' do it 'creates a new list at end of the lists' do
create_list(:label_list, 2, board: board) create(:label_list, board: board, position: 1)
create(:label_list, board: board, position: 2)
list = described_class.new(project, label_id: label.id).execute list = described_class.new(project, label_id: label.id).execute
...@@ -36,18 +37,16 @@ ...@@ -36,18 +37,16 @@
end end
end end
context 'when board lists has a done list' do context 'when board lists has backlog, label and done lists' do
it 'creates a new list before' do it 'creates a new list at end of the label lists' do
list1 = create(:backlog_list, board: board, position: 1) create(:backlog_list, board: board)
list2 = create(:label_list, board: board, position: 2) create(:done_list, board: board)
list3 = create(:done_list, board: board, position: 3) list1 = create(:label_list, board: board, position: 1)
list = described_class.new(project, label_id: label.id).execute list2 = described_class.new(project, label_id: label.id).execute
expect(list.position).to eq 3
expect(list1.reload.position).to eq 1 expect(list1.reload.position).to eq 1
expect(list2.reload.position).to eq 2 expect(list2.reload.position).to eq 2
expect(list3.reload.position).to eq 4
end end
end end
end end
......
...@@ -14,18 +14,18 @@ ...@@ -14,18 +14,18 @@
end end
it 'decrements position of higher lists' do it 'decrements position of higher lists' do
list1 = create(:backlog_list, board: board, position: 1) backlog = create(:backlog_list, board: board)
list2 = create(:label_list, board: board, position: 2) development = create(:label_list, board: board, position: 1)
list3 = create(:label_list, board: board, position: 3) review = create(:label_list, board: board, position: 2)
list4 = create(:label_list, board: board, position: 4) staging = create(:label_list, board: board, position: 3)
list5 = create(:done_list, board: board, position: 5) done = create(:done_list, board: board)
described_class.new(project, list_id: list2.id).execute described_class.new(project, list_id: development.id).execute
expect(list1.reload.position).to eq 1 expect(backlog.reload.position).to be_nil
expect(list3.reload.position).to eq 2 expect(review.reload.position).to eq 1
expect(list4.reload.position).to eq 3 expect(staging.reload.position).to eq 2
expect(list5.reload.position).to eq 4 expect(done.reload.position).to be_nil
end end
end end
......
...@@ -4,81 +4,98 @@ ...@@ -4,81 +4,98 @@
describe '#execute' do describe '#execute' do
let(:project) { create(:project_with_board) } let(:project) { create(:project_with_board) }
let(:board) { project.board } let(:board) { project.board }
let!(:list1) { create(:backlog_list, board: board, position: 1) }
let!(:list2) { create(:label_list, board: board, position: 2) } let!(:backlog) { create(:backlog_list, board: board) }
let!(:list3) { create(:label_list, board: board, position: 3) } let!(:planning) { create(:label_list, board: board, position: 1) }
let!(:list4) { create(:label_list, board: board, position: 4) } let!(:development) { create(:label_list, board: board, position: 2) }
let!(:list5) { create(:label_list, board: board, position: 5) } let!(:review) { create(:label_list, board: board, position: 3) }
let!(:list6) { create(:done_list, board: board, position: 6) } let!(:staging) { create(:label_list, board: board, position: 4) }
let!(:done) { create(:done_list, board: board) }
context 'when list type is set to label' do context 'when list type is set to label' do
it 'keeps position of lists when new position is nil' do it 'keeps position of lists when new position is nil' do
service = described_class.new(project, { list_id: list2.id, position: nil }) service = described_class.new(project, { list_id: planning.id, position: nil })
service.execute service.execute
expect(positions_of_lists).to eq [1, 2, 3, 4, 5, 6] expect(current_list_positions).to eq [1, 2, 3, 4]
end end
it 'keeps position of lists when new positon is equal to old position' do it 'keeps position of lists when new positon is equal to old position' do
service = described_class.new(project, { list_id: list2.id, position: 2 }) service = described_class.new(project, { list_id: planning.id, position: 1 })
service.execute
expect(current_list_positions).to eq [1, 2, 3, 4]
end
it 'keeps position of lists when new positon is negative' do
service = described_class.new(project, { list_id: planning.id, position: -1 })
service.execute
expect(current_list_positions).to eq [1, 2, 3, 4]
end
it 'keeps position of lists when new positon is greater than number of labels lists' do
service = described_class.new(project, { list_id: planning.id, position: 6 })
service.execute service.execute
expect(positions_of_lists).to eq [1, 2, 3, 4, 5, 6] expect(current_list_positions).to eq [1, 2, 3, 4]
end end
it 'keeps position of lists when new positon is equal to first position' do it 'increments position of intermediate lists when new positon is equal to first position' do
service = described_class.new(project, { list_id: list3.id, position: 1 }) service = described_class.new(project, { list_id: staging.id, position: 1 })
service.execute service.execute
expect(positions_of_lists).to eq [1, 2, 3, 4, 5, 6] expect(current_list_positions).to eq [2, 3, 4, 1]
end end
it 'keeps position of lists when new positon is equal to last position' do it 'decrements position of intermediate lists when new positon is equal to last position' do
service = described_class.new(project, { list_id: list3.id, position: 6 }) service = described_class.new(project, { list_id: planning.id, position: 4 })
service.execute service.execute
expect(positions_of_lists).to eq [1, 2, 3, 4, 5, 6] expect(current_list_positions).to eq [4, 1, 2, 3]
end end
it 'decrements position of intermediate lists when new position is greater than old position' do it 'decrements position of intermediate lists when new position is greater than old position' do
service = described_class.new(project, { list_id: list2.id, position: 5 }) service = described_class.new(project, { list_id: planning.id, position: 3 })
service.execute service.execute
expect(positions_of_lists).to eq [1, 5, 2, 3, 4, 6] expect(current_list_positions).to eq [3, 1, 2, 4]
end end
it 'increments position of intermediate lists when when new position is lower than old position' do it 'increments position of intermediate lists when new position is lower than old position' do
service = described_class.new(project, { list_id: list5.id, position: 2 }) service = described_class.new(project, { list_id: staging.id, position: 2 })
service.execute service.execute
expect(positions_of_lists).to eq [1, 3, 4, 5, 2, 6] expect(current_list_positions).to eq [1, 3, 4, 2]
end end
end end
it 'keeps position of lists when list type is backlog' do it 'keeps position of lists when list type is backlog' do
service = described_class.new(project, { list_id: list1.id, position: 2 }) service = described_class.new(project, { list_id: backlog.id, position: 2 })
service.execute service.execute
expect(positions_of_lists).to eq [1, 2, 3, 4, 5, 6] expect(current_list_positions).to eq [1, 2, 3, 4]
end end
it 'keeps position of lists when list type is done' do it 'keeps position of lists when list type is done' do
service = described_class.new(project, { list_id: list6.id, position: 2 }) service = described_class.new(project, { list_id: done.id, position: 2 })
service.execute service.execute
expect(positions_of_lists).to eq [1, 2, 3, 4, 5, 6] expect(current_list_positions).to eq [1, 2, 3, 4]
end end
end end
def positions_of_lists def current_list_positions
(1..6).map { |index| send("list#{index}").reload.position } [planning, development, review, staging].map { |list| list.reload.position }
end end
end end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment