From 876154f938ecce23e7e7e2d4d485a28648b6a656 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Fri, 5 Sep 2008 14:57:46 +0200 Subject: [PATCH] IRC refactoring: casemaps --- lib/irc.rb | 257 +------------------------------------------ lib/irc/casemap.rb | 268 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+), 256 deletions(-) create mode 100644 lib/irc/casemap.rb diff --git a/lib/irc.rb b/lib/irc.rb index 19ea3370..e923d72b 100644 --- a/lib/irc.rb +++ b/lib/irc.rb @@ -19,8 +19,6 @@ # # Author:: Giuseppe Bilotta (giuseppe.bilotta@gmail.com) -require 'singleton' - class Object # We extend the Object class with a method that @@ -40,263 +38,11 @@ end # in the same namespace # module Irc - - - # Due to its Scandinavian origins, IRC has strange case mappings, which - # consider the characters {}|^ as the uppercase - # equivalents of # []\~. - # - # This is however not the same on all IRC servers: some use standard ASCII - # casemapping, other do not consider ^ as the uppercase of - # ~ - # - class Casemap - @@casemaps = {} - - # Create a new casemap with name _name_, uppercase characters _upper_ and - # lowercase characters _lower_ - # - def initialize(name, upper, lower) - @key = name.to_sym - raise "Casemap #{name.inspect} already exists!" if @@casemaps.has_key?(@key) - @@casemaps[@key] = { - :upper => upper, - :lower => lower, - :casemap => self - } - end - - # Returns the Casemap with the given name - # - def Casemap.get(name) - @@casemaps[name.to_sym][:casemap] - end - - # Retrieve the 'uppercase characters' of this Casemap - # - def upper - @@casemaps[@key][:upper] - end - - # Retrieve the 'lowercase characters' of this Casemap - # - def lower - @@casemaps[@key][:lower] - end - - # Return a Casemap based on the receiver - # - def to_irc_casemap - self - end - - # A Casemap is represented by its lower/upper mappings - # - def inspect - self.__to_s__[0..-2] + " #{upper.inspect} ~(#{self})~ #{lower.inspect}>" - end - - # As a String we return our name - # - def to_s - @key.to_s - end - - # Two Casemaps are equal if they have the same upper and lower ranges - # - def ==(arg) - other = arg.to_irc_casemap - return self.upper == other.upper && self.lower == other.lower - end - - # Give a warning if _arg_ and self are not the same Casemap - # - def must_be(arg) - other = arg.to_irc_casemap - if self == other - return true - else - warn "Casemap mismatch (#{self.inspect} != #{other.inspect})" - return false - end - end - - end - - # The rfc1459 casemap - # - class RfcCasemap < Casemap - include Singleton - - def initialize - super('rfc1459', "\x41-\x5e", "\x61-\x7e") - end - - end - RfcCasemap.instance - - # The strict-rfc1459 Casemap - # - class StrictRfcCasemap < Casemap - include Singleton - - def initialize - super('strict-rfc1459', "\x41-\x5d", "\x61-\x7d") - end - - end - StrictRfcCasemap.instance - - # The ascii Casemap - # - class AsciiCasemap < Casemap - include Singleton - - def initialize - super('ascii', "\x41-\x5a", "\x61-\x7a") - end - - end - AsciiCasemap.instance - - - # This module is included by all classes that are either bound to a server - # or should have a casemap. - # - module ServerOrCasemap - - attr_reader :server - - # This method initializes the instance variables @server and @casemap - # according to the values of the hash keys :server and :casemap in _opts_ - # - def init_server_or_casemap(opts={}) - @server = opts.fetch(:server, nil) - raise TypeError, "#{@server} is not a valid Irc::Server" if @server and not @server.kind_of?(Server) - - @casemap = opts.fetch(:casemap, nil) - if @server - if @casemap - @server.casemap.must_be(@casemap) - @casemap = nil - end - else - @casemap = (@casemap || 'rfc1459').to_irc_casemap - end - end - - # This is an auxiliary method: it returns true if the receiver fits the - # server and casemap specified in _opts_, false otherwise. - # - def fits_with_server_and_casemap?(opts={}) - srv = opts.fetch(:server, nil) - cmap = opts.fetch(:casemap, nil) - cmap = cmap.to_irc_casemap unless cmap.nil? - - if srv.nil? - return true if cmap.nil? or cmap == casemap - else - return true if srv == @server and (cmap.nil? or cmap == casemap) - end - return false - end - - # Returns the casemap of the receiver, by looking at the bound - # @server (if possible) or at the @casemap otherwise - # - def casemap - return @server.casemap if defined?(@server) and @server - return @casemap - end - - # Returns a hash with the current @server and @casemap as values of - # :server and :casemap - # - def server_and_casemap - h = {} - h[:server] = @server if defined?(@server) and @server - h[:casemap] = @casemap if defined?(@casemap) and @casemap - return h - end - - # We allow up/downcasing with a different casemap - # - def irc_downcase(cmap=casemap) - self.to_s.irc_downcase(cmap) - end - - # Up/downcasing something that includes this module returns its - # Up/downcased to_s form - # - def downcase - self.irc_downcase - end - - # We allow up/downcasing with a different casemap - # - def irc_upcase(cmap=casemap) - self.to_s.irc_upcase(cmap) - end - - # Up/downcasing something that includes this module returns its - # Up/downcased to_s form - # - def upcase - self.irc_upcase - end - - end - end +require 'irc/casemap' -# We start by extending the String class -# with some IRC-specific methods -# class String - - # This method returns the Irc::Casemap whose name is the receiver - # - def to_irc_casemap - Irc::Casemap.get(self) rescue raise TypeError, "Unkown Irc::Casemap #{self.inspect}" - end - - # This method returns a string which is the downcased version of the - # receiver, according to the given _casemap_ - # - # - def irc_downcase(casemap='rfc1459') - cmap = casemap.to_irc_casemap - self.tr(cmap.upper, cmap.lower) - end - - # This is the same as the above, except that the string is altered in place - # - # See also the discussion about irc_downcase - # - def irc_downcase!(casemap='rfc1459') - cmap = casemap.to_irc_casemap - self.tr!(cmap.upper, cmap.lower) - end - - # Upcasing functions are provided too - # - # See also the discussion about irc_downcase - # - def irc_upcase(casemap='rfc1459') - cmap = casemap.to_irc_casemap - self.tr(cmap.lower, cmap.upper) - end - - # In-place upcasing - # - # See also the discussion about irc_downcase - # - def irc_upcase!(casemap='rfc1459') - cmap = casemap.to_irc_casemap - self.tr!(cmap.lower, cmap.upper) - end - # This method checks if the receiver contains IRC glob characters # # IRC has a very primitive concept of globs: a * stands for "any @@ -334,7 +80,6 @@ class String end - # ArrayOf is a subclass of Array whose elements are supposed to be all # of the same class. This is not intended to be used directly, but rather # to be subclassed as needed (see for example Irc::UserList and Irc::NetmaskList) diff --git a/lib/irc/casemap.rb b/lib/irc/casemap.rb new file mode 100644 index 00000000..aaa5fadb --- /dev/null +++ b/lib/irc/casemap.rb @@ -0,0 +1,268 @@ +#-- vim:sw=2:et +#++ +# :title: IRC casemaps +# +# This module defines the IRC casemap class and the most common casemaps +# +# Author:: Giuseppe Bilotta (giuseppe.bilotta@gmail.com) + +require 'singleton' + +module Irc + + # Due to its Scandinavian origins, IRC has strange case mappings, which + # consider the characters {}|^ as the uppercase + # equivalents of # []\~. + # + # This is however not the same on all IRC servers: some use standard ASCII + # casemapping, other do not consider ^ as the uppercase of + # ~ + # + class Casemap + @@casemaps = {} + + # Create a new casemap with name _name_, uppercase characters _upper_ and + # lowercase characters _lower_ + # + def initialize(name, upper, lower) + @key = name.to_sym + raise "Casemap #{name.inspect} already exists!" if @@casemaps.has_key?(@key) + @@casemaps[@key] = { + :upper => upper, + :lower => lower, + :casemap => self + } + end + + # Returns the Casemap with the given name + # + def Casemap.get(name) + @@casemaps[name.to_sym][:casemap] + end + + # Retrieve the 'uppercase characters' of this Casemap + # + def upper + @@casemaps[@key][:upper] + end + + # Retrieve the 'lowercase characters' of this Casemap + # + def lower + @@casemaps[@key][:lower] + end + + # Return a Casemap based on the receiver + # + def to_irc_casemap + self + end + + # A Casemap is represented by its lower/upper mappings + # + def inspect + self.__to_s__[0..-2] + " #{upper.inspect} ~(#{self})~ #{lower.inspect}>" + end + + # As a String we return our name + # + def to_s + @key.to_s + end + + # Two Casemaps are equal if they have the same upper and lower ranges + # + def ==(arg) + other = arg.to_irc_casemap + return self.upper == other.upper && self.lower == other.lower + end + + # Give a warning if _arg_ and self are not the same Casemap + # + def must_be(arg) + other = arg.to_irc_casemap + if self == other + return true + else + warn "Casemap mismatch (#{self.inspect} != #{other.inspect})" + return false + end + end + + end + + # The rfc1459 casemap + # + class RfcCasemap < Casemap + include Singleton + + def initialize + super('rfc1459', "\x41-\x5e", "\x61-\x7e") + end + + end + RfcCasemap.instance + + # The strict-rfc1459 Casemap + # + class StrictRfcCasemap < Casemap + include Singleton + + def initialize + super('strict-rfc1459', "\x41-\x5d", "\x61-\x7d") + end + + end + StrictRfcCasemap.instance + + # The ascii Casemap + # + class AsciiCasemap < Casemap + include Singleton + + def initialize + super('ascii', "\x41-\x5a", "\x61-\x7a") + end + + end + AsciiCasemap.instance + + + # This module is included by all classes that are either bound to a server + # or should have a casemap. + # + module ServerOrCasemap + + attr_reader :server + + # This method initializes the instance variables @server and @casemap + # according to the values of the hash keys :server and :casemap in _opts_ + # + def init_server_or_casemap(opts={}) + @server = opts.fetch(:server, nil) + raise TypeError, "#{@server} is not a valid Irc::Server" if @server and not @server.kind_of?(Server) + + @casemap = opts.fetch(:casemap, nil) + if @server + if @casemap + @server.casemap.must_be(@casemap) + @casemap = nil + end + else + @casemap = (@casemap || 'rfc1459').to_irc_casemap + end + end + + # This is an auxiliary method: it returns true if the receiver fits the + # server and casemap specified in _opts_, false otherwise. + # + def fits_with_server_and_casemap?(opts={}) + srv = opts.fetch(:server, nil) + cmap = opts.fetch(:casemap, nil) + cmap = cmap.to_irc_casemap unless cmap.nil? + + if srv.nil? + return true if cmap.nil? or cmap == casemap + else + return true if srv == @server and (cmap.nil? or cmap == casemap) + end + return false + end + + # Returns the casemap of the receiver, by looking at the bound + # @server (if possible) or at the @casemap otherwise + # + def casemap + return @server.casemap if defined?(@server) and @server + return @casemap + end + + # Returns a hash with the current @server and @casemap as values of + # :server and :casemap + # + def server_and_casemap + h = {} + h[:server] = @server if defined?(@server) and @server + h[:casemap] = @casemap if defined?(@casemap) and @casemap + return h + end + + # We allow up/downcasing with a different casemap + # + def irc_downcase(cmap=casemap) + self.to_s.irc_downcase(cmap) + end + + # Up/downcasing something that includes this module returns its + # Up/downcased to_s form + # + def downcase + self.irc_downcase + end + + # We allow up/downcasing with a different casemap + # + def irc_upcase(cmap=casemap) + self.to_s.irc_upcase(cmap) + end + + # Up/downcasing something that includes this module returns its + # Up/downcased to_s form + # + def upcase + self.irc_upcase + end + + end + +end + + +# We extend the String class with some Irc::Casemap methods +# +class String + + # This method returns the Irc::Casemap whose name is the receiver + # + def to_irc_casemap + Irc::Casemap.get(self) rescue raise TypeError, "Unkown Irc::Casemap #{self.inspect}" + end + + # This method returns a string which is the downcased version of the + # receiver, according to the given _casemap_ + # + # + def irc_downcase(casemap='rfc1459') + cmap = casemap.to_irc_casemap + self.tr(cmap.upper, cmap.lower) + end + + # This is the same as the above, except that the string is altered in place + # + # See also the discussion about irc_downcase + # + def irc_downcase!(casemap='rfc1459') + cmap = casemap.to_irc_casemap + self.tr!(cmap.upper, cmap.lower) + end + + # Upcasing functions are provided too + # + # See also the discussion about irc_downcase + # + def irc_upcase(casemap='rfc1459') + cmap = casemap.to_irc_casemap + self.tr(cmap.lower, cmap.upper) + end + + # In-place upcasing + # + # See also the discussion about irc_downcase + # + def irc_upcase!(casemap='rfc1459') + cmap = casemap.to_irc_casemap + self.tr!(cmap.lower, cmap.upper) + end +end + + -- 2.32.0.93.g670b81a890