From b3b87b63f6a6f9cf84af220ed29c8eb85fa34c19 Mon Sep 17 00:00:00 2001 From: Fire Demon Date: Fri, 14 Aug 2020 01:18:17 -0500 Subject: [Command Tags] Add support for interpreting arbitary blocks of command statement --- app/lib/command_tag/processor.rb | 50 +++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/app/lib/command_tag/processor.rb b/app/lib/command_tag/processor.rb index 0c589364d..7532d6d85 100644 --- a/app/lib/command_tag/processor.rb +++ b/app/lib/command_tag/processor.rb @@ -34,9 +34,9 @@ class CommandTag::Processor @status = status @parent = status.thread @conversation = status.conversation + @text = status.text @run_once = Set[] @vars = { 'statement_uuid' => [nil] } - @text = prepare_input(status.text) @statements = {} return unless @account.present? && @account.local? && @status.present? @@ -53,11 +53,11 @@ class CommandTag::Processor all_handlers!(:startup) - parse_statements + @text = parse_statements_from!(@text, @statements) execute_statements(:at_start) execute_statements(:with_return, true) - @text = parse_templates(@text).rstrip + @text = replace_templates(@text) execute_statements(:before_save) if status_text_blank? @@ -94,11 +94,13 @@ class CommandTag::Processor end end + # Moves command tags placed after hashtags and mentions to their own line. def prepare_input(text) text.gsub(/\r\n|\n\r|\r/, "\n").gsub(/^\s*(#{MENTIONS_OR_HASHTAGS_RE})#!/, "\\1\n#!") end - def parse_templates(text) + # Translates %%...%% templates. + def replace_templates(text) text.gsub(TEMPLATE_RE) do template = unescape_literals(Regexp.last_match(1)) next if template.blank? @@ -133,11 +135,16 @@ class CommandTag::Processor var(name)[index_start..index_end].presence || '' end end - end + end.rstrip end - def parse_statements - @text.gsub!(STATEMENT_RE) do + # Parses statements from text and merges them into statement queues. + # Mutates statement queues hash! + def parse_statements_from!(text, statement_queues) + @run_once.clear + + text = prepare_input(text) + text.gsub!(STATEMENT_RE) do statement = unescape_literals(Regexp.last_match(0).strip[2..-1]) next if statement.blank? @@ -146,10 +153,14 @@ class CommandTag::Processor next unless statement_array[0].match?(/\A[\w_]+\z/) statement_array[-1].rstrip! if statement_array.count > 1 - add_statement_handlers_for(statement_array) + add_statement_handlers_for!(statement_array, statement_queues) end + + @run_once.clear + text end + # Yields all possible handler names for a command. def potential_handlers_for(name) ['_once', ''].each_with_index do |count_affix, index| %w(at_start with_return when_blank at_end).each do |when_affix| @@ -164,13 +175,15 @@ class CommandTag::Processor end end - def add_statement_handlers_for(statement_array) + # Expands a statement to a handler method call, arguments, and template UUID for each handler affix. + # Mutates statement queues hash! + def add_statement_handlers_for!(statement_array, statement_queues = {}) statement_uuid = SecureRandom.uuid potential_handlers_for(statement_array[0]) do |when_affix, handler, once| if !(once && @run_once.include?(handler)) && respond_to?(handler) - @statements[when_affix] ||= [] - @statements[when_affix] << [handler, statement_array[1..-1], statement_uuid] + statement_queues[when_affix] ||= [] + statement_queues[when_affix] << [handler, statement_array[1..-1], statement_uuid] @run_once << handler if once end end @@ -179,11 +192,14 @@ class CommandTag::Processor "%% statement:#{statement_uuid} all %%" end - def execute_statements(event, with_return = false) + # Calls all handlers for a queue of statements in order. + def execute_statements(event, with_return = false, statements: nil) + statements = @statements if statements.blank? + ["_#{event}", "_once_#{event}"].each do |when_affix| - next if @statements[when_affix].blank? + next if statements[when_affix].blank? - @statements[when_affix].each do |handler, arguments, uuid| + statements[when_affix].each do |handler, arguments, uuid| @vars['statement_uuid'][0] = uuid if with_return @vars["statement:#{uuid}"] = [public_send(handler, arguments)] @@ -194,8 +210,10 @@ class CommandTag::Processor end end - def reset_status_caches - [@status, @parent].each do |status| + # Expire cached statuses after potentially updating them. + def reset_status_caches(statuses = nil) + statuses = [@status, @parent] if statuses.blank? + statuses.each do |status| next unless @account.id == status&.account_id Rails.cache.delete_matched("statuses/#{status.id}-*") -- cgit