diff --git a/app/models/project_folder.rb b/app/models/project_folder.rb index 3aeacaedc..6cacc54ea 100644 --- a/app/models/project_folder.rb +++ b/app/models/project_folder.rb @@ -8,10 +8,11 @@ class ProjectFolder < ApplicationRecord validates :name, length: { minimum: Constants::NAME_MIN_LENGTH, maximum: Constants::NAME_MAX_LENGTH }, - uniqueness: { scope: :team_id, case_sensitive: false } + uniqueness: { scope: %i(team_id parent_folder_id), case_sensitive: false } validate :parent_folder_team, if: -> { parent_folder.present? } before_validation :inherit_team_from_parent_folder, on: :create, if: -> { parent_folder.present? } + before_validation :ensure_uniqueness_name_on_moving, on: :update, if: -> { parent_folder_id_changed? } belongs_to :team, inverse_of: :project_folders, touch: true belongs_to :parent_folder, class_name: 'ProjectFolder', optional: true @@ -101,4 +102,20 @@ class ProjectFolder < ApplicationRecord errors.add(:parent_folder, I18n.t('activerecord.errors.models.project_folder.attributes.parent_folder')) end + + def ensure_uniqueness_name_on_moving + return unless self.class.where(parent_folder: parent_folder).where('name ILIKE ?', name).any? + + regex = /\((\d+)\)/ + max_number = self.class + .where(parent_folder: parent_folder) + .where('name ILIKE ?', "#{name} (%)") + .order(name: :desc) + .pluck(:name) + .select { |s| s.match(regex) } + .map { |s| s.match(regex)[1].to_i } + .max || 0 + + self.name = name + " (#{max_number + 1})" + end end diff --git a/spec/models/project_folder_spec.rb b/spec/models/project_folder_spec.rb index 26f6f818a..81e733fa2 100644 --- a/spec/models/project_folder_spec.rb +++ b/spec/models/project_folder_spec.rb @@ -37,7 +37,7 @@ describe ProjectFolder, type: :model do .is_at_most(Constants::NAME_MAX_LENGTH)) end it do - expect(project_folder).to validate_uniqueness_of(:name).scoped_to(:team_id).case_insensitive + expect(project_folder).to validate_uniqueness_of(:name).scoped_to(%i(team_id parent_folder_id)).case_insensitive end end @@ -62,5 +62,71 @@ describe ProjectFolder, type: :model do expect { project_folder.save }.to(change { project_folder.team }) end end + + describe 'ensure_uniqueness_name_on_moving' do + let(:team) { create :team } + let(:parent_folder) { create :project_folder, team: team } + + context 'when folder with same name already exists' do + before do + create :project_folder, name: 'FolderOne (some)', parent_folder: parent_folder, team: parent_folder.team + create :project_folder, name: 'FolderOne (test)', parent_folder: parent_folder, team: parent_folder.team + create :project_folder, name: 'FolderOne (111)', parent_folder: parent_folder, team: parent_folder.team + create :project_folder, name: 'FolderOne (41)', parent_folder: parent_folder, team: parent_folder.team + create :project_folder, name: 'FolderOne (1)', parent_folder: parent_folder, team: parent_folder.team + create :project_folder, name: 'FolderOne (0)', parent_folder: parent_folder, team: parent_folder.team + create :project_folder, name: 'FolderOne', parent_folder: parent_folder, team: parent_folder.team + end + + it 'appends number to name' do + folder = create :project_folder, team: team, name: 'FolderOne' + folder.update!(parent_folder: parent_folder) + + expect(folder.name).to be_eql('FolderOne (112)') + end + + it 'keeps number if number is between already existing numbers, but is available' do + folder = create :project_folder, team: team, name: 'FolderOne (40)' + folder.update!(parent_folder: parent_folder) + + expect(folder.name).to be_eql('FolderOne (40)') + end + end + + context 'when folder with same name does not exsits yet' do + it do + folder = create :project_folder, team: team, name: 'FolderOne' + folder.update!(parent_folder: parent_folder) + + expect(folder.name).to be_eql('FolderOne') + end + end + + context 'when new name parenthesises with text' do + before do + create :project_folder, name: 'FolderOne (some)', parent_folder: parent_folder, team: parent_folder.team + end + + it do + folder = create :project_folder, team: team, name: 'FolderOne (some)' + folder.update!(parent_folder: parent_folder) + + expect(folder.name).to be_eql('FolderOne (some) (1)') + end + end + + context 'when new name parenthesises with numbers' do + before do + create :project_folder, name: 'FolderOne (1)', parent_folder: parent_folder, team: parent_folder.team + end + + it do + folder = create :project_folder, team: team, name: 'FolderOne (1)' + folder.update!(parent_folder: parent_folder) + + expect(folder.name).to be_eql('FolderOne (1) (1)') + end + end + end end end