about summary refs log tree commit diff
path: root/app/lib
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-08-14 01:18:17 -0500
committerFire Demon <firedemon@creature.cafe>2020-08-30 05:45:18 -0500
commitb3b87b63f6a6f9cf84af220ed29c8eb85fa34c19 (patch)
tree1a6ea4ee335e011fca7f00a8b9792adf3b21b688 /app/lib
parente21c2211c75693d03496f2002fd59b0f9a725727 (diff)
[Command Tags] Add support for interpreting arbitary blocks of command statement
Diffstat (limited to 'app/lib')
-rw-r--r--app/lib/command_tag/processor.rb50
1 files 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}-*")