diff --git a/app/models/command/filter_by_tag.rb b/app/models/command/filter_by_tag.rb index 8c6a49e74..f62aca01a 100644 --- a/app/models/command/filter_by_tag.rb +++ b/app/models/command/filter_by_tag.rb @@ -1,18 +1,13 @@ class Command::FilterByTag < Command - store_accessor :data, :tag_id, :params + include Command::Tags - validates_presence_of :tag_id + store_accessor :data, :params def title - "Filter by tag ##{tag&.title || tag_id} " + "Filter by tag ##{tag_title}" end def execute - redirect_to cards_path(**params.merge(tag_ids: [ tag_id ])) + redirect_to cards_path(**params.merge(tag_ids: [ tag.id ])) end - - private - def tag - Tag.find_by_id(tag_id) - end end diff --git a/app/models/command/parser.rb b/app/models/command/parser.rb index 8fbd5ccc3..ea330420a 100644 --- a/app/models/command/parser.rb +++ b/app/models/command/parser.rb @@ -19,14 +19,16 @@ class Command::Parser command_name, *command_arguments = string.strip.split(" ") case command_name + when /^#/ + Command::FilterByTag.new(tag_title: tag_title_from(string), params: filter.as_params) + when /^@/ + Command::GoToUser.new(user_id: assignee_from(command_name)&.id) when "/assign", "/assignto" Command::Assign.new(assignee_ids: assignees_from(command_arguments).collect(&:id), card_ids: cards.ids) when "/close" Command::Close.new(card_ids: cards.ids, reason: command_arguments.join(" ")) - when /^#/ - Command::FilterByTag.new(tag_id: tag_from(string).id, params: filter.as_params) - when /^@/ - Command::GoToUser.new(user_id: assignee_from(command_name)&.id) + when "/tag" + Command::Tag.new(tag_title: tag_title_from(command_arguments.join(" ")), card_ids: cards.ids) else parse_free_string(string) end @@ -46,9 +48,8 @@ class Command::Parser User.all.find { |user| user.mentionable_handles.include?(string_without_at) } end - def tag_from(string) - title = string.gsub(/^#/, "") - Tag.find_or_create_by!(title: title) + def tag_title_from(string) + string.gsub(/^#/, "") end def parse_free_string(string) diff --git a/app/models/command/tag.rb b/app/models/command/tag.rb new file mode 100644 index 000000000..c6596de56 --- /dev/null +++ b/app/models/command/tag.rb @@ -0,0 +1,37 @@ +class Command::Tag < Command + include Command::Cards, Command::Tags + + store_accessor :data, :tagged_card_ids + + def title + "Tag #{cards_description} with ##{tag_title}" + end + + def execute + tagged_card_ids = [] + + transaction do + cards.find_each do |card| + unless card.tagged_with?(tag) + tagged_card_ids << card.id + card.toggle_tag_with(tag_title) + end + end + + update! tagged_card_ids: tagged_card_ids + end + end + + def undo + transaction do + tagged_cards.find_each do |card| + card.toggle_tag_with(tag_title) if card.tagged_with?(tag) + end + end + end + + private + def tagged_cards + user.accessible_cards.where(id: tagged_card_ids) + end +end diff --git a/app/models/command/tags.rb b/app/models/command/tags.rb new file mode 100644 index 000000000..448c7375c --- /dev/null +++ b/app/models/command/tags.rb @@ -0,0 +1,14 @@ +module Command::Tags + extend ActiveSupport::Concern + + included do + store_accessor :data, :tag_title + + validates_presence_of :tag_title + end + + private + def tag + Tag.find_or_create_by!(title: tag_title) + end +end diff --git a/test/models/command/filter_by_tag_test.rb b/test/models/command/filter_by_tag_test.rb index ef335a1d3..53be19ca9 100644 --- a/test/models/command/filter_by_tag_test.rb +++ b/test/models/command/filter_by_tag_test.rb @@ -7,7 +7,7 @@ class Command::FilterByCardTest < ActionDispatch::IntegrationTest @tag = tags(:web) end - test "redirects to the cards index filtering by cards" do + test "redirect to the cards index filtering by cards" do result = execute_command "##{@tag.title}" assert_equal cards_path(indexed_by: "newest", tag_ids: [ @tag.id ]), result.url diff --git a/test/models/command/tag_test.rb b/test/models/command/tag_test.rb new file mode 100644 index 000000000..7c3486b34 --- /dev/null +++ b/test/models/command/tag_test.rb @@ -0,0 +1,47 @@ +require "test_helper" + +class Command::TagTest < ActionDispatch::IntegrationTest + include CommandTestHelper + + setup do + Current.session = sessions(:david) + @card = cards(:text) + @tag = tags(:web) + end + + test "tag card on perma with existing tag" do + assert_changes -> { @card.tagged_with?(@tag) }, from: false, to: true do + execute_command "/tag #{@tag.title}", context_url: collection_card_url(@card.collection, @card) + end + end + + test "tag card on perma with new card" do + assert_difference -> { @card.tags.count }, +1 do + execute_command "/tag some-new-tag", context_url: collection_card_url(@card.collection, @card) + end + + assert_equal "some-new-tag", @card.tags.last.title + end + + test "tag several cards on cards' index page" do + cards = cards(:logo, :text, :layout) + cards.each { it.taggings.destroy_all } + + execute_command "/tag #{@tag.title}", context_url: collection_cards_url(@card.collection) + + cards.each { assert it.reload.tagged_with?(@tag) } + end + + test "undo tagged cards" do + cards = cards(:logo, :text, :layout) + cards.each { it.taggings.destroy_all } + + command = parse_command "/tag #{@tag.title}", context_url: collection_cards_url(@card.collection) + command.execute + + cards.each { assert it.reload.tagged_with?(@tag) } + + command.undo + cards.each { assert_not it.reload.tagged_with?(@tag) } + end +end