Files
fizzy/app/models/command/parser.rb
T
Jorge Manrubia bd7b9a442e Merge branch 'main' into fizzy-do-prompts
* main: (57 commits)
  Untenanted access in authenticated controllers should request auth
  dep: bundle update
  dep: update Rails
  dep: bump AR::Tenanted
  Clean up the cable meta tag
  Fix activity feed pagination to fetch on demand
  Create a "published" event if a card is created published
  Scatter Honcho seeds over a 30 day period
  Fix the PWA manifest to use slugged URLs
  Collection publication edits render a turbo frame
  Collection workflow edits render a turbo frame
  Collection entropy config edits render a turbo frame
  Use automatic sizing where supported
  Introduce a separate controller for collection entropy config
  Add `card_id` to index
  Move to scope and inline method
  Prefer modern syntax
  Fix that stage should be visible above card background
  Indicate opens menu
  Hide dialog targets from screen readers when closed
  ...

# Conflicts:
#	db/schema.rb
#	db/schema_cache.yml
2025-07-03 16:11:00 +02:00

117 lines
3.6 KiB
Ruby

class Command::Parser
attr_reader :context
delegate :user, :cards, :filter, :script_name, to: :context
def initialize(context)
@context = context
end
def parse(string)
parse_command(string).tap do |command|
command.user = user
command.line ||= as_plain_text(string)
command.context ||= context
command.default_url_options[:script_name] = script_name
end
end
private
def as_plain_text_with_attachable_references(string)
ActionText::Content.new(string).render_attachments(&:to_gid).fragment.to_plain_text
end
def as_plain_text(string)
ActionText::Content.new(string).to_plain_text
end
def parse_command(string)
rich_text_command = as_plain_text_with_attachable_references(string)
plain_text_command = as_plain_text(string)
parse_plain_text_command(plain_text_command) || parse_rich_text_command(rich_text_command)
end
def parse_plain_text_command(string)
command_name, *_ = 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: context.find_user(command_name)&.id)
end
end
def parse_rich_text_command(string)
command_name, *command_arguments = string.strip.split(" ")
combined_arguments = command_arguments.join(" ")
case command_name
when "/user"
Command::GoToUser.new(user_id: context.find_user(combined_arguments)&.id)
when "/assign", "/assignto"
Command::Assign.new(assignee_ids: assignees_from(command_arguments).collect(&:id), card_ids: cards.ids)
when "/clear"
Command::ClearFilters.new(params: filter.as_params)
when "/close"
Command::Close.new(card_ids: cards.ids, reason: combined_arguments)
when "/reopen"
Command::Reopen.new(card_ids: cards.ids)
when "/consider", "/reconsider"
Command::Consider.new(card_ids: cards.ids)
when "/do"
Command::Do.new(card_ids: cards.ids)
when "/insight"
Command::GetInsight.new(query: combined_arguments, card_ids: cards.ids)
when "/add_card"
Command::AddCard.new(card_title: combined_arguments, collection_id: guess_collection&.id)
when "/search"
Command::Search.new(terms: combined_arguments)
when "/stage"
Command::Stage.new(stage_id: context.find_workflow_stage(combined_arguments)&.id, card_ids: cards.ids)
when "/visit"
Command::VisitUrl.new(url: command_arguments.first)
when "/tag"
Command::Tag.new(tag_title: tag_title_from(combined_arguments), card_ids: cards.ids)
else
parse_free_string(string)
end
end
def assignees_from(strings)
Array(strings).filter_map do |string|
context.find_user(string)
end
end
def guess_collection
cards.first&.collection || Collection.first
end
def tag_title_from(string)
context.find_tag(string)&.title || string.gsub(/^#/, "")
end
def parse_free_string(string)
if cards = multiple_cards_from(string)
Command::FilterCards.new(card_ids: cards.ids, params: filter.as_params)
elsif card = single_card_from(string)
Command::GoToCard.new(card_id: card.id)
else
Command::Ai::Parser.new(context).parse(string)
end
end
def multiple_cards_from(string)
if string.match?(/^[\d\s,]+$/)
tokens = string.split(/[\s,]+/)
user.accessible_cards.where(id: tokens).presence if tokens&.many?
end
end
def single_card_from(string)
user.accessible_cards.find_by_id(string)
end
end