]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/core/utils/extends.rb
extends: Array#shuffle! and shuffle methods
[user/henk/code/ruby/rbot.git] / lib / rbot / core / utils / extends.rb
index e587f5641901a96de59a82677a0401608327f892..19157009a06b9f9d264f1c08584844421155fc1d 100644 (file)
@@ -38,6 +38,50 @@ class ::Module
 end
 
 
+# DottedIndex mixin: extend a Hash or Array class with this module
+# to achieve [] and []= methods that automatically split indices
+# at dots (indices are automatically converted to symbols, too)
+#
+# You have to define the single_retrieve(_key_) and
+# single_assign(_key_,_value_) methods (usually aliased at the
+# original :[] and :[]= methods)
+#
+module ::DottedIndex
+  def rbot_index_split(*ar)
+    keys = ([] << ar).flatten
+    keys.map! { |k|
+      k.to_s.split('.').map { |kk| kk.to_sym rescue nil }.compact
+    }.flatten
+  end
+
+  def [](*ar)
+    keys = self.rbot_index_split(ar)
+    return self.single_retrieve(keys.first) if keys.length == 1
+    h = self
+    while keys.length > 1
+      k = keys.shift
+      h[k] ||= self.class.new
+      h = h[k]
+    end
+    h[keys.last]
+  end
+
+  def []=(*arr)
+    val = arr.last
+    ar = arr[0..-2]
+    keys = self.rbot_index_split(ar)
+    return self.single_assign(keys.first, val) if keys.length == 1
+    h = self
+    while keys.length > 1
+      k = keys.shift
+      h[k] ||= self.class.new
+      h = h[k]
+    end
+    h[keys.last] = val
+  end
+end
+
+
 # Extensions to the Array class
 #
 class ::Array
@@ -50,13 +94,39 @@ class ::Array
     self[rand(self.length)]
   end
 
-  # This method returns a random element from the array, deleting it from the
-  # array itself. The method returns nil if the array is empty
+  # This method returns a given element from the array, deleting it from the
+  # array itself. The method returns nil if the element couldn't be found.
+  #
+  # If nil is specified, a random element is returned and deleted.
   #
-  def delete_one
+  def delete_one(val=nil)
     return nil if self.empty?
-    self.delete_at(rand(self.length))
+    if val.nil?
+      index = rand(self.length)
+    else
+      index = self.index(val)
+      return nil unless index
+    end
+    self.delete_at(index)
   end
+
+  # This method shuffles the items in the array
+  def shuffle!
+    base = self.dup
+    self.clear
+    while item = base.delete_one
+      self << item
+    end
+    self
+  end
+
+  # This method returns a new array with the same items as
+  # the receiver, but shuffled
+  def shuffle
+    ret = self.dup
+    ret.shuffle!
+  end
+
 end
 
 # Extensions to the Range class
@@ -295,5 +365,67 @@ module ::Irc
         end
       }.uniq
     end
+
+    # The recurse depth of a message, for fake messages. 0 means an original
+    # message
+    def recurse_depth
+      unless defined? @recurse_depth
+        @recurse_depth = 0
+      end
+      @recurse_depth
+    end
+
+    # Set the recurse depth of a message, for fake messages. 0 should only
+    # be used by original messages
+    def recurse_depth=(val)
+      @recurse_depth = val
+    end
+  end
+
+  class Bot
+    module Plugins
+
+      # Maximum fake message recursion
+      MAX_RECURSE_DEPTH = 10
+
+      class RecurseTooDeep < RuntimeError
+      end
+
+      class BotModule
+        # Sometimes plugins need to create a new fake message based on an existing
+        # message: for example, this is done by alias, linkbot, reaction and remotectl.
+        #
+        # This method simplifies the message creation, including a recursion depth
+        # check.
+        #
+        # In the options you can specify the :bot, the :server, the :source,
+        # the :target, the message :class and whether or not to :delegate. To
+        # initialize these entries from an existing message, you can use :from
+        #
+        # If you don't specify a :from you should specify a :source.
+        #
+        def fake_message(string, opts={})
+          if from = opts[:from]
+            o = {
+              :bot => from.bot, :server => from.server, :source => from.source,
+              :target => from.target, :class => from.class, :delegate => true,
+              :depth => from.recurse_depth + 1
+            }.merge(opts)
+          else
+            o = {
+              :bot => @bot, :server => @bot.server, :target => @bot.myself,
+              :class => PrivMessage, :delegate => true, :depth => 1
+            }.merge(opts)
+          end
+          raise RecurseTooDeep if o[:depth] > MAX_RECURSE_DEPTH
+          new_m = o[:class].new(o[:bot], o[:server], o[:source], o[:target], string)
+          new_m.recurse_depth = o[:depth]
+          return new_m unless o[:delegate]
+          method = o[:class].to_s.gsub(/^Irc::|Message$/,'').downcase
+          method = 'privmsg' if method == 'priv'
+          o[:bot].plugins.irc_delegate(method, new_m)
+        end
+      end
+    end
   end
 end