# * do we want to handle a Channel list for each User telling which\r
# Channels is the User on (of those the client is on too)?\r
# We may want this so that when a User leaves all Channels and he hasn't\r
-# sent us privmsgs, we know remove him from the Server @users list\r
+# sent us privmsgs, we know we can remove him from the Server @users list\r
# * Maybe ChannelList and UserList should be HashesOf instead of ArrayOf?\r
-# See items marked as TODO Ho\r
+# See items marked as TODO Ho.\r
+# The framework to do this is now in place, thanks to the new [] method\r
+# for NetmaskList, which allows retrieval by Netmask or String\r
#++\r
# :title: IRC module\r
#\r
\r
require 'singleton'\r
\r
+class Object\r
+\r
+ # We extend the Object class with a method that\r
+ # checks if the receiver is nil or empty\r
+ def nil_or_empty?\r
+ return true unless self\r
+ return true if self.respond_to? :empty and self.empty?\r
+ return false\r
+ end\r
+end\r
\r
# The Irc module is used to keep all IRC-related classes\r
# in the same namespace\r
}\r
end\r
\r
+ # We introduce the 'downcase' method, which maps downcase() to all the Array\r
+ # elements, properly failing when the elements don't have a downcase method\r
+ #\r
+ def downcase\r
+ self.map { |el| el.downcase }\r
+ end\r
+\r
# Modifying methods which we don't handle yet are made private\r
#\r
private :[]=, :collect!, :map!, :fill, :flatten!\r
if self.class == Netmask\r
return self if fits_with_server_and_casemap?(opts)\r
end\r
- return self.fullform.to_irc_netmask(server_and_casemap.merge(opts))\r
+ return self.downcase.to_irc_netmask(opts)\r
end\r
\r
# Converts the receiver into a User with the given (optional)\r
super(Netmask, ar)\r
end\r
\r
+ # We enhance the [] method by allowing it to pick an element that matches\r
+ # a given Netmask, a String or a Regexp\r
+ # TODO take into consideration the opportunity to use select() instead of\r
+ # find(), and/or a way to let the user choose which one to take (second\r
+ # argument?)\r
+ #\r
+ def [](*args)\r
+ if args.length == 1\r
+ case args[0]\r
+ when Netmask\r
+ self.find { |mask|\r
+ mask.matches?(args[0])\r
+ }\r
+ when String\r
+ self.find { |mask|\r
+ mask.matches?(args[0].to_irc_netmask(:casemap => mask.casemap))\r
+ }\r
+ when Regexp\r
+ self.find { |mask|\r
+ mask.fullform =~ args[0]\r
+ }\r
+ else\r
+ super(*args)\r
+ end\r
+ else\r
+ super(*args)\r
+ end\r
+ end\r
+\r
end\r
\r
end\r
end\r
end\r
\r
+ # Users can be either simply downcased (their nick only)\r
+ # or fully downcased: this will return the fullform downcased\r
+ # according to the given casemap.\r
+ #\r
+ def full_irc_downcase(cmap=casemap)\r
+ self.fullform.irc_downcase(cmap)\r
+ end\r
+\r
+ # full_downcase() will return the fullform downcased according to the\r
+ # User's own casemap\r
+ #\r
+ def full_downcase\r
+ self.full_irc_downcase\r
+ end\r
+\r
# Since to_irc_user runs the same checks on server and channel as\r
# to_irc_netmask, we just try that and return self if it works.\r
#\r
#\r
def to_irc_user(opts={})\r
return self if fits_with_server_and_casemap?(opts)\r
- return self.fullform.to_irc_user(server_and_casemap(opts))\r
+ return self.full_downcase.to_irc_user(opts)\r
end\r
\r
# We can replace everything at once with data from another User\r
\r
\r
# A UserList is an ArrayOf <code>User</code>s\r
+ # We derive it from NetmaskList, which allows us to inherit any special\r
+ # NetmaskList method\r
#\r
- class UserList < ArrayOf\r
+ class UserList < NetmaskList\r
\r
# Create a new UserList, optionally filling it with the elements from\r
# the Array argument fed to it.\r
#\r
def initialize(ar=[])\r
- super(User, ar)\r
+ super(ar)\r
+ @element_class = User\r
+ end\r
+\r
+ # Convenience method: convert the UserList to a list of nicks. The indices\r
+ # are preserved\r
+ #\r
+ def nicks\r
+ self.map { |user| user.nick }\r
end\r
\r
end\r
# Mode on a Channel\r
#\r
class Mode\r
+ attr_reader :channel\r
def initialize(ch)\r
@channel = ch\r
end\r
# Example: b (banlist)\r
#\r
class ModeTypeA < Mode\r
+ attr_reader :list\r
def initialize(ch)\r
super\r
@list = NetmaskList.new\r
@arg = nil\r
end\r
\r
+ def status\r
+ @arg\r
+ end\r
+ alias :value :status\r
+\r
def set(val)\r
@arg = val\r
end\r
# modes of type A\r
#\r
class UserMode < ModeTypeB\r
+ attr_reader :list\r
+ alias :users :list\r
def initialize(ch)\r
super\r
@list = UserList.new\r
class ModeTypeC < Mode\r
def initialize(ch)\r
super\r
- @arg = false\r
+ @arg = nil\r
end\r
\r
def status\r
@arg\r
end\r
+ alias :value :status\r
\r
def set(val)\r
@arg = val\r
end\r
\r
def reset\r
- @arg = false\r
+ @arg = nil\r
end\r
\r
end\r
super(Channel, ar)\r
end\r
\r
+ # Convenience method: convert the ChannelList to a list of channel names.\r
+ # The indices are preserved\r
+ #\r
+ def names\r
+ self.map { |chan| chan.name }\r
+ end\r
+\r
end\r
\r
end\r
# Checks if the receiver already has a channel with the given _name_\r
#\r
def has_channel?(name)\r
+ return false if name.nil_or_empty?\r
channel_names.index(name.irc_downcase(casemap))\r
end\r
alias :has_chan? :has_channel?\r
# Returns the channel with name _name_, if available\r
#\r
def get_channel(name)\r
+ return nil if name.nil_or_empty?\r
idx = has_channel?(name)\r
channels[idx] if idx\r
end\r
# Create a new Channel object bound to the receiver and add it to the\r
# list of <code>Channel</code>s on the receiver, unless the channel was\r
# present already. In this case, the default action is to raise an\r
- # exception, unless _fails_ is set to false\r
+ # exception, unless _fails_ is set to false. An exception can also be\r
+ # raised if _str_ is nil or empty, again only if _fails_ is set to true;\r
+ # otherwise, the method just returns nil\r
#\r
def new_channel(name, topic=nil, users=[], fails=true)\r
+ if name.nil_or_empty?\r
+ raise "Tried to look for empty or nil channel name #{name.inspect}" if fails\r
+ return nil\r
+ end\r
ex = get_chan(name)\r
if ex\r
raise "Channel #{name} already exists on server #{self}" if fails\r
# Checks if the receiver already has a user with the given _nick_\r
#\r
def has_user?(nick)\r
+ return false if nick.nil_or_empty?\r
user_nicks.index(nick.irc_downcase(casemap))\r
end\r
\r
# Create a new User object bound to the receiver and add it to the list\r
# of <code>User</code>s on the receiver, unless the User was present\r
# already. In this case, the default action is to raise an exception,\r
- # unless _fails_ is set to false\r
+ # unless _fails_ is set to false. An exception can also be raised\r
+ # if _str_ is nil or empty, again only if _fails_ is set to true;\r
+ # otherwise, the method just returns nil\r
#\r
def new_user(str, fails=true)\r
+ if str.nil_or_empty?\r
+ raise "Tried to look for empty or nil user name #{str.inspect}" if fails\r
+ return nil\r
+ end\r
tmp = str.to_irc_user(:server => self)\r
old = get_user(tmp.nick)\r
# debug "Tmp: #{tmp.inspect}"\r