about summary refs log tree commit diff
path: root/app/lib/command_tag
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-07-27 15:29:34 -0500
committerFire Demon <firedemon@creature.cafe>2020-08-30 05:45:16 -0500
commit89b5668d48229c18f89a7e34b814ea2cb0231422 (patch)
tree0b4476d7fd46633fc70a271dee13947e4f346bab /app/lib/command_tag
parent1bd2aa1fd6a16c2067255b936da071d451aee884 (diff)
[Command Tags] Simplify tag processing and add support for return value templates
Diffstat (limited to 'app/lib/command_tag')
-rw-r--r--app/lib/command_tag/commands/hello_world.rb4
-rw-r--r--app/lib/command_tag/commands/variables.rb2
-rw-r--r--app/lib/command_tag/processor.rb71
3 files changed, 47 insertions, 30 deletions
diff --git a/app/lib/command_tag/commands/hello_world.rb b/app/lib/command_tag/commands/hello_world.rb
index 2341d22e2..d67f65333 100644
--- a/app/lib/command_tag/commands/hello_world.rb
+++ b/app/lib/command_tag/commands/hello_world.rb
@@ -5,7 +5,7 @@ module CommandTag::Commands::HelloWorld
     @vars['hello_world'] = ['Hello, world!']
   end
 
-  def handle_hello_world_at_start(_)
-    @text << "\nHello, world!"
+  def handle_hello_world_with_return(_)
+    'Hello, world!'
   end
 end
diff --git a/app/lib/command_tag/commands/variables.rb b/app/lib/command_tag/commands/variables.rb
index ab90ca13b..997131cd9 100644
--- a/app/lib/command_tag/commands/variables.rb
+++ b/app/lib/command_tag/commands/variables.rb
@@ -9,8 +9,6 @@ module CommandTag::Commands::Variables
     case args.count
     when 1
       @vars.delete(args[0])
-    when 2
-      @vars[args[0]] = [args.last]
     else
       @vars[args[0]] = args[1..-1]
     end
diff --git a/app/lib/command_tag/processor.rb b/app/lib/command_tag/processor.rb
index 6ca355687..74fe30509 100644
--- a/app/lib/command_tag/processor.rb
+++ b/app/lib/command_tag/processor.rb
@@ -15,9 +15,10 @@ class CommandTag::Processor
   include ImgProxyHelper
   include CommandTag::Commands
 
-  STATEMENT_RE = /^\s*#!\s*([^\n]+ (?:start|begin|do)$.*?)\n\s*#!\s*(?:end|stop|done)\s*$|^\s*#!\s*(.*?)\s*$/im.freeze
-  TEMPLATE_RE = /%%\s*(.*?)\s*%%/.freeze
   MENTIONS_OR_HASHTAGS_RE = /(?:(?:#{Account::MENTION_RE}|#{Tag::HASHTAG_RE})\s*)+/.freeze
+  PARSEABLE_RE = /^\s*(?:#{MENTIONS_OR_HASHTAGS_RE})?#!|%%.+?%%/.freeze
+  STATEMENT_RE = /^\s*#!\s*[^\n]+ (?:start|begin|do)$.*?\n\s*#!\s*(?:end|stop|done)\s*$|^\s*#!\s*.*?\s*$/im.freeze
+  TEMPLATE_RE = /%%\s*(.*?)\s*%%/.freeze
   ESCAPE_MAP = {
     '\n' => "\n",
     '\r' => "\r",
@@ -31,15 +32,15 @@ class CommandTag::Processor
     @parent       = status.thread
     @conversation = status.conversation
     @run_once     = Set[]
-    @vars         = {}
+    @vars         = { 'statement_uuid' => [nil] }
     @text         = prepare_input(status.text)
-    @statements   = []
+    @statements   = {}
 
     return unless @account.present? && @account.local? && @status.present?
   end
 
   def process!
-    unless @text.include?('#!') && @text.match?(/^\s*(?:#{MENTIONS_OR_HASHTAGS_RE})?#!/)
+    unless @text.match?(PARSEABLE_RE)
       process_inline_images!
       @status.save!
       return
@@ -51,31 +52,32 @@ class CommandTag::Processor
 
     parse_statements
 
-    %w(at_start once_at_start).each { |suffix| execute_statements(suffix) }
+    execute_statements(:at_start)
+    execute_statements(:with_return, true)
     @text = parse_templates(@text).rstrip
-    %w(before_save once_before_save).each { |suffix| execute_statements(suffix) }
+    execute_statements(:before_save)
 
     if @text.blank? || @text.gsub(MENTIONS_OR_HASHTAGS_RE, '').strip.blank?
-      %w(when_blank once_when_blank).each { |suffix| execute_statements(suffix) }
+      execute_statements(:when_blank)
 
       unless (@status.published? && !@status.edited.zero?) || @text.present?
-        %w(before_destroy once_before_destroy).each { |suffix| execute_statements(suffix) }
+        execute_statements(:before_destroy)
         @status.destroy
-        %w(after_destroy once_after_destroy).each { |suffix| execute_statements(suffix) }
+        execute_statements(:after_destroy)
       end
     elsif @status.destroyed?
-      %w(after_destroy once_after_destroy).each { |suffix| execute_statements(suffix) }
+      execute_statements(:after_destroy)
     else
       @status.text = @text
       process_inline_images!
       if @status.save
-        %w(after_save once_after_save).each { |suffix| execute_statements(suffix) }
+        execute_statements(:after_save)
       else
-        %w(after_save_fail once_after_save_fail).each { |suffix| execute_statements(suffix) }
+        execute_statements(:after_save_fail)
       end
     end
 
-    %w(at_end once_at_end).each { |suffix| execute_statements(suffix) }
+    execute_statements(:at_end)
     reset_status_caches
   end
 
@@ -126,12 +128,12 @@ class CommandTag::Processor
 
   def parse_statements
     @text.gsub!(STATEMENT_RE) do
-      statement = Regexp.last_match[1..-1].flatten.compact
+      statement = Regexp.last_match(0).strip[2..-1]
       next if statement.blank?
 
       statement_array = statement.scan(/'([^']*)'|"([^"]*)"|(\S+)|\s+(?:start|begin|do)\s*$\n+(.*)/im).flatten.compact
       statement_array[0] = statement_array[0].strip.tr(':.\- ', '_').gsub(/__+/, '_').downcase
-      next unless statement_array[0].match?(/\A\w+\z/)
+      next unless statement_array[0].match?(/\A[\w_]+\z/)
 
       statement_array[-1].rstrip! if statement_array.count > 1
       add_statement_handlers_for(statement_array)
@@ -140,28 +142,45 @@ class CommandTag::Processor
 
   def potential_handlers_for(name)
     ['_once', ''].each_with_index do |count_affix, index|
-      %w(at_start when_blank at_end).each do |when_affix|
-        yield ["handle_#{name}#{count_affix}_#{when_affix}", index.zero?]
+      %w(at_start with_return when_blank at_end).each do |when_affix|
+        yield ["#{count_affix}_#{when_affix}", "handle_#{name}#{count_affix}_#{when_affix}", index.zero?]
       end
 
       %w(destroy save postprocess save_fail).each do |event_affix|
         %w(before after).each do |when_affix|
-          yield ["handle_#{name}#{count_affix}_#{when_affix}_#{event_affix}", index.zero?]
+          yield ["#{count_affix}_#{when_affix}_#{event_affix}", "handle_#{name}#{count_affix}_#{when_affix}_#{event_affix}", index.zero?]
         end
       end
     end
   end
 
-  def add_statement_handlers_for(statement_array, _index)
-    potential_handlers_for(statement_array[0]) do |handler, once|
-      (@statements << [handler, statement_array[1..-1]]) if respond_to?(handler) && !(once && @run_once.include?(handler))
-      @run_once << handler if once
+  def add_statement_handlers_for(statement_array)
+    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]
+        @run_once << handler if once
+      end
     end
+
+    # Template for statement return value.
+    "%% statement:#{statement_uuid} %%"
   end
 
-  def execute_statements(event)
-    @statements.each do |handler, arguments|
-      public_send(handler, arguments) if handler.end_with?("_#{event}")
+  def execute_statements(event, with_return = false)
+    ["_#{event}", "_once_#{event}"].each do |when_affix|
+      next if @statements[when_affix].blank?
+
+      @statements[when_affix].each do |handler, arguments, uuid|
+        @vars['statement_uuid'][0] = uuid
+        if with_return
+          @vars["statement:#{uuid}"] = [public_send(handler, arguments)]
+        else
+          public_send(handler, arguments)
+        end
+      end
     end
   end