diff options
| author | Patrick J Cherry <patrick@bytemark.co.uk> | 2011-07-20 16:16:14 +0100 | 
|---|---|---|
| committer | Patrick J Cherry <patrick@bytemark.co.uk> | 2011-07-20 16:16:14 +0100 | 
| commit | 3185e5d746abda1b7f42ecdbd74ec14359fda3bc (patch) | |
| tree | e74ee3a587d62fdf68f79093af2202188aed3bc5 /lib/mauve | |
| parent | 4ffe222986d4e3565a1f189325d77b33f90dfa15 (diff) | |
| parent | d3a3cfef9650b08f62db62bd7e86b673f9d77d0b (diff) | |
merge
Diffstat (limited to 'lib/mauve')
| -rw-r--r-- | lib/mauve/configuration.rb | 56 | ||||
| -rw-r--r-- | lib/mauve/heartbeat.rb | 64 | ||||
| -rw-r--r-- | lib/mauve/http_server.rb | 7 | ||||
| -rw-r--r-- | lib/mauve/mauve_thread.rb | 70 | ||||
| -rw-r--r-- | lib/mauve/notifier.rb | 6 | ||||
| -rw-r--r-- | lib/mauve/notifiers/templates/email.html.erb | 4 | ||||
| -rw-r--r-- | lib/mauve/notifiers/templates/email.txt.erb | 2 | ||||
| -rw-r--r-- | lib/mauve/notifiers/templates/xmpp.html.erb | 21 | ||||
| -rw-r--r-- | lib/mauve/notifiers/templates/xmpp.txt.erb | 8 | ||||
| -rw-r--r-- | lib/mauve/notifiers/xmpp.rb | 75 | ||||
| -rw-r--r-- | lib/mauve/server.rb | 25 | ||||
| -rw-r--r-- | lib/mauve/timer.rb | 15 | ||||
| -rw-r--r-- | lib/mauve/version.rb | 2 | ||||
| -rw-r--r-- | lib/mauve/web_interface.rb | 12 | 
14 files changed, 238 insertions, 129 deletions
| diff --git a/lib/mauve/configuration.rb b/lib/mauve/configuration.rb index 0d3e520..2794c03 100644 --- a/lib/mauve/configuration.rb +++ b/lib/mauve/configuration.rb @@ -7,6 +7,7 @@ require 'mauve/notification'  require 'mauve/alert_group'  require 'mauve/people_list'  require 'mauve/source_list' +require 'mauve/heartbeat'  # Seconds, minutes, hours, days, and weeks... More than that, we   # really should not need it. @@ -64,8 +65,8 @@ module Mauve      attr_reader :alert_groups      attr_reader :people_lists      attr_reader :source_lists -    attr_reader :logger -     +    attr_reader :logger    +       def initialize        @notification_methods = {}        @people = {} @@ -115,6 +116,7 @@ module Mauve      is_builder "outputter", LoggerOutputterBuilder +      def builder_setup        logger = Log4r::Logger.new("Mauve")        @default_format = nil @@ -166,14 +168,11 @@ module Mauve    class ProcessorBuilder < ObjectBuilder      is_attribute "sleep_interval" +    is_attribute "transmission_cache_expire_time"      def builder_setup        @result = Processor.instance      end - -    def method_missing(name, value) -      @args[name] = value -    end    end    class UDPServerBuilder < ObjectBuilder @@ -184,10 +183,6 @@ module Mauve      def builder_setup        @result = UDPServer.instance      end - -    def method_missing(name, value) -      @args[name] = value -    end    end    class TimerBuilder < ObjectBuilder @@ -196,12 +191,15 @@ module Mauve      def builder_setup        @result = Timer.instance      end +  end -    def method_missing(name, value) -      @args[name] = value +  class HeartbeatBuilder < ObjectBuilder +    is_attribute "destination" +    is_attribute "interval" +     +    def builder_setup +      @result = Heartbeat.instance      end - -    end    class HTTPServerBuilder < ObjectBuilder @@ -210,15 +208,11 @@ module Mauve      is_attribute "ip"      is_attribute "document_root"      is_attribute "session_secret" +    is_attribute "base_url"      def builder_setup        @result = HTTPServer.instance      end - -    def method_missing(name, value) -      @args[name] = value -    end -    end    class NotifierBuilder < ObjectBuilder @@ -227,11 +221,6 @@ module Mauve      def builder_setup        @result = Notifier.instance      end - -    def method_missing(name, value) -      @args[name] = value -    end -    end    class ServerBuilder < ObjectBuilder @@ -241,22 +230,22 @@ module Mauve      is_builder "processor",     ProcessorBuilder      is_builder "timer",         TimerBuilder      is_builder "notifier",      NotifierBuilder -     +    is_builder "heartbeat",     HeartbeatBuilder + +    is_attribute "hostname" +    is_attribute "database" +    is_attribute "initial_sleep" +       def builder_setup +      @result = Mauve::Server.instance        @args = {}      end      def result -      @result = Mauve::Server.instance        @result.configure(@args) -      @result.web_interface = @web_interface        @result      end -    def method_missing(name, value) -      @args[name] = value -    end -          def created_web_interface(web_interface)        @web_interface = web_interface      end @@ -272,6 +261,10 @@ module Mauve      def created_notifier(notifier)        @notifier = notifier      end +   +    def created_heartbeat(heartbeat) +      @heartbeat = heartbeat +    end    end    class NotificationMethodBuilder < ObjectBuilder @@ -282,7 +275,6 @@ module Mauve        provider("Default")      end -      def provider(name)        notifiers_base = Mauve::Notifiers        notifiers_type = notifiers_base.const_get(@notification_type) diff --git a/lib/mauve/heartbeat.rb b/lib/mauve/heartbeat.rb new file mode 100644 index 0000000..0f51f80 --- /dev/null +++ b/lib/mauve/heartbeat.rb @@ -0,0 +1,64 @@ +require 'mauve/sender' +require 'mauve/proto' +require 'mauve/mauve_thread' +require 'log4r' + +# +# This class is responsible for sending a heartbeat to another mauve instance elsewhere. +# +module Mauve + +  class Heartbeat < MauveThread + +    include Singleton + +    attr_accessor :destination, :summary, :detail +    attr_reader   :sleep_interval, :raise_at  + +    def initialize +      super + +      @destination    = nil +      @summary        = "Mauve alert server down." +      @detail         = "The Mauve server at #{Server.instance.hostname} has failed to send a heartbeat." +      self.raise_at   = 600 +    end + +    def raise_at=(i) +      @raise_at = i +      @sleep_interval = ((i.to_f)/2.5).round.to_i +    end + +    def logger +      @logger ||= Log4r::Logger.new(self.class.to_s) +    end + +    def main_loop +      # +      # Don't send if no destination set. +      # +      return if @destination.nil? + +      update = Mauve::Proto::AlertUpdate.new +      update.replace = false +      update.alert = [] +      update.source = Server.instance.hostname +      update.transmission_id = rand(2**63) + +      message = Mauve::Proto::Alert.new +      message.id = "mauve-heartbeat" +      message.summary = self.summary +      message.detail = self.detail +      message.raise_time = (MauveTime.now.to_f+self.raise_at).to_i +      message.clear_time = MauveTime.now.to_i + +      update.alert << message + +      Mauve::Sender.new(self.destination).send(update) +    end + +  end + +end + + diff --git a/lib/mauve/http_server.rb b/lib/mauve/http_server.rb index 71261c8..7bd4467 100644 --- a/lib/mauve/http_server.rb +++ b/lib/mauve/http_server.rb @@ -87,7 +87,7 @@ module Mauve      include Singleton -    attr_accessor :port, :ip, :document_root +    attr_accessor :port, :ip, :document_root, :base_url      attr_accessor :session_secret       def initialize @@ -96,6 +96,7 @@ module Mauve        @ip = "127.0.0.1"        @document_root = "/usr/share/mauvealert"        @session_secret = "%x" % rand(2**100) +      @server_name = nil      end      def main_loop @@ -105,6 +106,10 @@ module Mauve        @server = ::Thin::Server.new(@ip, @port, Rack::Session::Cookie.new(WebInterface.new, {:key => "mauvealert", :secret => @session_secret, :expire_after => 691200}), :signals => false)        @server.start      end + +    def base_url +      @base_url ||= "http://"+Server.instance.hostname +    end      def stop        @server.stop if @server diff --git a/lib/mauve/mauve_thread.rb b/lib/mauve/mauve_thread.rb index f6c0cbc..6409f98 100644 --- a/lib/mauve/mauve_thread.rb +++ b/lib/mauve/mauve_thread.rb @@ -5,9 +5,11 @@ module Mauve    class MauveThread +    attr_reader :state +      def initialize        @thread = nil -      @stop = true +      @state = :stopped      end      def logger @@ -18,62 +20,77 @@ module Mauve        #        # Good to go.        # -      @frozen = false -      @stop = false - -      logger.debug("Started") +      self.state = :starting        @sleep_interval ||= interval -      while !@stop do +      sleep_loops = (@sleep_interval.to_f / 0.1).round.to_i +      sleep_loops = 1 if sleep_loops.nil? or sleep_loops < 1 + +      while self.state != :stopping do + +        self.state = :started if self.state == :starting +          #          # Schtop!          # -        if @frozen -          logger.debug("Frozen") +        if self.state == :freezing +          self.state = :frozen            Thread.stop -          logger.debug("Thawed") +          self.state = :started          end          yield -        next if self.should_stop? +        # +        # Ah-ha! Sleep with a break clause. +        # +        sleep_loops.times do + +          break if self.should_stop? -        Kernel.sleep(@sleep_interval) +          # +          # This is a rate-limiting step +          # +          Kernel.sleep 0.1 +        end        end -      logger.debug("Stopped") +      self.state = :stopped      end      def should_stop? -      @frozen or @stop +      [:freezing, :stopping].include?(self.state) +    end + +    def state=(s) +      raise "Bad state for mauve_thread #{s.inspect}" unless [:stopped, :starting, :started, :freezing, :frozen, :stopping, :killing, :killed].include?(s) +      unless @state == s +        @state = s +        logger.debug(s.to_s.capitalize)  +      end      end      def freeze -      logger.debug("Freezing") unless @frozen  +      self.state = :freezing -      @frozen = true -        20.times { Kernel.sleep 0.1 ; break if @thread.stop? }        logger.debug("Thread has not frozen!") unless @thread.stop?      end      def frozen? -      self.stop? +      self.stop? and self.state == :frozen      end      def run        if self.alive?  -        if self.stop?  -          logger.debug("Thawing") if @frozen -          @frozen = false +        if self.stop?            @thread.wakeup           end        else          @logger = nil -        logger.debug("Starting") if @stop -        @stop   = false +        self.state = :starting          @thread = Thread.new{ self.run_thread { self.main_loop } }        end      end @@ -107,9 +124,7 @@ module Mauve      end      def stop -      logger.debug("Stopping") unless @stop - -      @stop = true +      self.state = :stopping        10.times do           break unless self.alive? @@ -127,10 +142,9 @@ module Mauve      alias exit stop      def kill -      logger.debug("Killing") -      @frozen = @stop = true +      self.state = :killing        @thread.kill -      logger.debug("Killed") +      self.state = :killed      end      def thread diff --git a/lib/mauve/notifier.rb b/lib/mauve/notifier.rb index 5bedeb9..6099457 100644 --- a/lib/mauve/notifier.rb +++ b/lib/mauve/notifier.rb @@ -5,17 +5,11 @@ require 'mauve/notifiers/xmpp'  module Mauve    class Notifier < MauveThread - -    DEFAULT_XMPP_MESSAGE = "Mauve server started."      include Singleton      attr_accessor :sleep_interval -    def initialize -      super -    end -      def main_loop        #         # Cycle through the buffer. diff --git a/lib/mauve/notifiers/templates/email.html.erb b/lib/mauve/notifiers/templates/email.html.erb index f0ce1be..24b5e7b 100644 --- a/lib/mauve/notifiers/templates/email.html.erb +++ b/lib/mauve/notifiers/templates/email.html.erb @@ -1,12 +1,12 @@  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  <html xmlns="http://www.w3.org/1999/xhtml"><head><title></title></head><body> -<p><strong><%= alert.update_type.upcase %>:</strong> <%  +<p><a href="<%= WebInterface.url_for(alert) %>"><%= alert.update_type.upcase %></a>:<%   case alert.update_type  when "cleared"   %><%= alert.cleared_at.to_s_relative %><%   when "acknowledged" -%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by%><%  +%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by%> until <%= alert.will_unacknowledge_at.to_s_human %><%   else  %><%= alert.raised_at.to_s_relative %><%   end  diff --git a/lib/mauve/notifiers/templates/email.txt.erb b/lib/mauve/notifiers/templates/email.txt.erb index dc5762b..aab44a8 100644 --- a/lib/mauve/notifiers/templates/email.txt.erb +++ b/lib/mauve/notifiers/templates/email.txt.erb @@ -3,7 +3,7 @@ case alert.update_type  when "cleared"  %><%= alert.cleared_at.to_s_relative %><%  when "acknowledged" -%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %><%  +%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %> until <%= alert.will_unacknowledge_at.to_s_human %><%   else  %><%= alert.raised_at.to_s_relative %><%   end  diff --git a/lib/mauve/notifiers/templates/xmpp.html.erb b/lib/mauve/notifiers/templates/xmpp.html.erb new file mode 100644 index 0000000..c6bfaed --- /dev/null +++ b/lib/mauve/notifiers/templates/xmpp.html.erb @@ -0,0 +1,21 @@ +  <html xmlns="http://jabber.org/protocol/xhtml-im"><body xmlns="OAhttp://www.w3.org/1999/xhtml"> +<a href="<%=WebInterface.url_for(alert)%>"><%= alert.update_type.upcase %></a>: <%  +case alert.update_type +when "cleared" +%><%= alert.cleared_at.to_s_relative %><%  +when "acknowledged" +%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %> until <%= alert.will_unacknowledge_at.to_s_human %><%  +else +%><%= alert.raised_at.to_s_relative %><%  +end  +%>: <%= alert.subject %>: <%= alert.summary %><% +if alert.source != alert.subject  +%> -- from <%= alert.source %><% +end  +%>.<% +if was_suppressed and not is_suppressed  +%><em> Normal service has resumed.</em><% +elsif is_suppressed and not was_suppressed +%><em> Further alerts suppressed until things calm down.</em><% +end  +%></body></html> diff --git a/lib/mauve/notifiers/templates/xmpp.txt.erb b/lib/mauve/notifiers/templates/xmpp.txt.erb index 837fd67..a73f41f 100644 --- a/lib/mauve/notifiers/templates/xmpp.txt.erb +++ b/lib/mauve/notifiers/templates/xmpp.txt.erb @@ -3,7 +3,7 @@ case alert.update_type  when "cleared"  %><%= alert.cleared_at.to_s_relative %><%   when "acknowledged" -%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %><%  +%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %> until <%= alert.will_unacknowledge_at.to_s_human %><%   else  %><%= alert.raised_at.to_s_relative %><%   end  @@ -11,10 +11,10 @@ end  if alert.source != alert.subject   %> -- from <%= alert.source %><%  end  -%>.<% +%>. <%=WebInterface.url_for(alert)%><%  if was_suppressed and not is_suppressed  -%> Normal service has resumed.<% +%> (Normal service has resumed.)<%  elsif is_suppressed and not was_suppressed -%> Further alerts suppressed until things calm down.<% +%> (Further alerts suppressed until things calm down.)<%  end   %> diff --git a/lib/mauve/notifiers/xmpp.rb b/lib/mauve/notifiers/xmpp.rb index 7fe1e39..991194d 100644 --- a/lib/mauve/notifiers/xmpp.rb +++ b/lib/mauve/notifiers/xmpp.rb @@ -34,9 +34,11 @@ module Jabber        # mean the other parts of the method fail to execute.         # That would be bad. So kill parser_thread last        @tbcbmutex.synchronize { @processing = 0 } -      @fd.close if @fd and !@fd.closed? +      if @fd and !@fd.closed? +        @fd.close  +        stop  +      end        @status = DISCONNECTED -      stop      end    end  end @@ -66,15 +68,15 @@ module Mauve          include Jabber          # Atrtribute. -        attr_reader :name, :jid +        attr_reader :name          # Atrtribute.          attr_accessor :password          def initialize(name)            Jabber::logger = self.logger        -          #Jabber::debug = true -          #Jabber::warnings = true +#         Jabber::debug = true +#          Jabber::warnings = true            @name = name            @mucs = {} @@ -138,10 +140,11 @@ module Mauve              #              unless ex.nil? or @closing                 logger.warn(["Caught",ex.class,ex.to_s,"during XMPP",where].join(" ")) +              @closing = true                connect                @mucs.each do |jid, muc|                  @mucs.delete(jid) -                join_muc(jid) +                join_muc(muc[:jid], muc[:password])                end              end            end @@ -156,11 +159,13 @@ module Mauve          def close            @closing = true -          if @client and @client.is_connected? -            @mucs.each do |jid, muc| -              muc.exit("Goodbye!") if muc.active? -            end  -            @client.send(Presence.new(nil, "Goodbye!").set_type(:unavailable)) +          if @client  +            if  @client.is_connected? +              @mucs.each do |jid, muc| +                muc[:client].exit("Goodbye!") if muc[:client].active? +              end  +              @client.send(Presence.new(nil, "Goodbye!").set_type(:unavailable)) +            end              @client.close!            end          end @@ -202,7 +207,16 @@ module Mauve              alert.to_s            end -          send_message(destination_jid, txt) +          template_file = File.join(File.dirname(__FILE__),"templates","xmpp.html.erb") + +          xhtml = if File.exists?(template_file) +            ERB.new(File.read(template_file)).result(binding).chomp +          else +            logger.error("Could not find xmpp.txt.erb template") +            alert.to_s +          end + +          send_message(destination_jid, txt, xhtml)          end          # Sends a message to the destionation. @@ -210,20 +224,23 @@ module Mauve          # @param [String] destination The (full) JID to send to.          # @param [String] msg The (formatted) message to send.          # @return [NIL] nada. -        def send_message(jid, msg) +        def send_message(jid, msg, html_msg=nil)            jid = JID.new(jid) unless jid.is_a?(JID)            message = Message.new(jid) - -          #if msg.is_a?(XHTML::HTML) -          #  message.add_element(msg) -          #else            message.body = msg -          #end +          if html_msg  +            begin +              html_msg = REXML::Document.new(html_msg) unless html_msg.is_a?(REXML::Document) +              message.add_element(html_msg)  +            rescue REXML::ParseException +              logger.error "Bad XHTML: #{html_msg.inspect}" +            end +          end            if is_muc?(jid)              jid = join_muc(jid.strip) -            muc = @mucs[jid] +            muc = @mucs[jid][:client]              if muc                message.to   = muc.jid @@ -272,28 +289,28 @@ module Mauve              logger.debug("Adding new MUC client for #{jid}") -            @mucs[jid.strip] = Jabber::MUC::MUCClient.new(@client) +            @mucs[jid.strip] = {:jid => jid, :password => password, :client => Jabber::MUC::MUCClient.new(@client)}              # Add some callbacks -            @mucs[jid.strip].add_message_callback do |m| +            @mucs[jid.strip][:client].add_message_callback do |m|                receive_message(m)              end -            @mucs[jid.strip].add_private_message_callback do |m| +            @mucs[jid.strip][:client].add_private_message_callback do |m|                receive_message(m)              end            end -          if !@mucs[jid.strip].active? +          if !@mucs[jid.strip][:client].active?              #              # Make sure we have a resource.              # -            @mucs[jid.strip].join(jid, password) +            @mucs[jid.strip][:client].join(jid, password) -            logger.info("Joined #{jid}") +            logger.info("Joined #{jid.strip}")            else -            logger.debug("Already joined #{jid}.") +            logger.debug("Already joined #{jid.strip}.")            end            # @@ -410,10 +427,10 @@ module Mauve            # that we've not sent ourselves, that are not historical, and that            # match our resource or node in the body.            # -          if @mucs[msg.from.strip].is_a?(MUC::MUCClient) and -                msg.from != @mucs[msg.from.strip].jid and +          if @mucs[msg.from.strip][:client].is_a?(MUC::MUCClient) and +                msg.from != @mucs[msg.from.strip][:client].jid and                  msg.x("jabber:x:delay") == nil and  -                (msg.body =~ /\b#{Regexp.escape(@mucs[msg.from.strip].jid.resource)}\b/i or +                (msg.body =~ /\b#{Regexp.escape(@mucs[msg.from.strip][:client].jid.resource)}\b/i or                  msg.body =~ /\b#{Regexp.escape(@client.jid.node)}\b/i)              receive_normal_message(msg)             end diff --git a/lib/mauve/server.rb b/lib/mauve/server.rb index 57ddca8..20f7045 100644 --- a/lib/mauve/server.rb +++ b/lib/mauve/server.rb @@ -11,29 +11,23 @@ require 'mauve/timer'  require 'mauve/udp_server'  require 'mauve/processor'  require 'mauve/http_server' +require 'mauve/heartbeat'  require 'log4r'  module Mauve    class Server  -    DEFAULT_CONFIGURATION = { -      :ip => "127.0.0.1", -      :port => 32741, -      :database => "sqlite3:///./mauvealert.db", -      :log_file => "stdout", -      :log_level => 1, -      :transmission_cache_expire_time => 600 -    } +    DEFAULT_CONFIGURATION = { }      #      # This is the order in which the threads should be started.      # -    THREAD_CLASSES = [UDPServer, HTTPServer, Processor, Timer, Notifier] +    THREAD_CLASSES = [UDPServer, HTTPServer, Processor, Timer, Notifier, Heartbeat] -    attr_accessor :web_interface -    attr_reader   :stopped_at, :started_at, :initial_sleep, :packet_buffer, :notification_buffer +    attr_accessor :hostname, :database, :initial_sleep +    attr_reader   :stopped_at, :started_at, :packet_buffer, :notification_buffer      include Singleton @@ -43,8 +37,11 @@ module Mauve        # Sleep time between pooling the @buffer buffer.        @sleep = 1 -      @frozen     = false -      @stop       = false +      @frozen      = false +      @stop        = false +      @hostname    = "localhost" +      @database    = "sqlite3:///./mauvealert.db" +              @stopped_at = MauveTime.now        @started_at = MauveTime.now @@ -79,7 +76,7 @@ module Mauve        end        # -      DataMapper.setup(:default, @config[:database]) +      DataMapper.setup(:default, @database)        # DataObjects::Sqlite3.logger = Log4r::Logger.new("Mauve::DataMapper")         # diff --git a/lib/mauve/timer.rb b/lib/mauve/timer.rb index f6ada88..91dea18 100644 --- a/lib/mauve/timer.rb +++ b/lib/mauve/timer.rb @@ -13,12 +13,6 @@ module Mauve      attr_accessor :sleep_interval, :last_run_at -    def initialize -      super -      @initial_sleep = 300 -      @initial_sleep_threshold = 300 -    end -      def main_loop        #        # Get the next alert. @@ -51,22 +45,27 @@ module Mauve          #          # Sleep indefinitely          # -        logger.info("Nothing to notify about -- snoozing indefinitely.") +        logger.info("Nothing to notify about -- snoozing for a while.") +        sleep_loops = 600        else          #          # La la la nothing to do.          #          logger.info("Next to notify: #{next_to_notify} -- snoozing until #{next_to_notify.due_at}") +        sleep_loops = ((next_to_notify.due_at - MauveTime.now).to_f / 0.1).round.to_i        end +      sleep_loops = 1 if sleep_loops.nil? or sleep_loops < 1 +        #        # Ah-ha! Sleep with a break clause.        # -      while next_to_notify.nil? or MauveTime.now <= next_to_notify.due_at +      sleep_loops.times do          #          # Start again if the situation has changed.          #          break if self.should_stop? +          #          # This is a rate-limiting step for alerts.          # diff --git a/lib/mauve/version.rb b/lib/mauve/version.rb index 139b8eb..a4be324 100644 --- a/lib/mauve/version.rb +++ b/lib/mauve/version.rb @@ -1,5 +1,5 @@  module Mauve -  VERSION="3.1.5" +  VERSION="3.1.6"  end diff --git a/lib/mauve/web_interface.rb b/lib/mauve/web_interface.rb index 4594e10..4bb9517 100644 --- a/lib/mauve/web_interface.rb +++ b/lib/mauve/web_interface.rb @@ -22,7 +22,14 @@ module Mauve      def self._logger        Log4r::Logger.new(self.to_s)      end -   +     +    # +    # Generic URL for  +    # +    def self.url_for(obj) +      [Mauve::HTTPServer.instance.base_url, obj.class.to_s.split("::").last.downcase, obj.id.to_s].join("/") +    end +          use Rack::CommonLogger      use Rack::Chunked      use Rack::ContentLength @@ -519,7 +526,6 @@ EOF        env['rack.errors'] = RackErrorsProxy.new(logger)        super(env)      end -     -  end +  end  end | 
