require 'webrick' require 'yaml' if $DEBUG Thread.abort_on_exception else exit if fork $stdout = File.new('/dev/null', 'w') $stderr = File.new('/dev/null', 'w') end Thread.new do while(true) sleep(300) GC.start end end class NilClass def to_json "null" end end class TrueClass def to_json "true" end end class FalseClass def to_json "false" end end class String def underscore self.gsub(/::/, '/'). gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2'). tr("-", "_"). downcase end alias_method :to_json, :inspect end class Numeric def formatted(precision = 1) rounded_number = (Float(self) * (10 ** precision)).round.to_f / 10 ** precision parts = ("%01.#{precision}f" % rounded_number).to_s.split('.') parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,") parts.join(".") end alias_method :to_json, :inspect end class Array def to_json "[#{map { |e| e.to_json }.join(',')}]" end end class Hash def formatted out = self.dup out.each_pair { |k, v| out[k] = v.formatted } out end def to_json arr = [] each_pair { |k, v| arr << "#{k.to_json}:#{v.to_json}" } "{#{arr.join(',')}}" end end class Symbol def to_json to_s.inspect end end module DataProviders DATA_SOURCES_CLASSES = {} DATA_SOURCES = {} def self.preload Dir.glob("#{File.dirname(__FILE__)}/data_providers/*.rb").each { |file| load file unless file =~ /extconf.rb$/ } DataProviders.constants.each do |c| c = DataProviders.const_get(c) DATA_SOURCES_CLASSES[c.to_s.gsub(/^DataProviders::/, '').underscore] = c if c.is_a? Class end end def self.setup(settings) DATA_SOURCES_CLASSES.each_pair { |k, v| DATA_SOURCES[k] = v.new(settings[k]) } end end DataProviders.preload WEBSTATS_PATH = File.expand_path("~/.webstats") settings = {} if File.exists?(WEBSTATS_PATH) settings = YAML.load(IO.read(WEBSTATS_PATH)) else DataProviders::DATA_SOURCES_CLASSES.each_pair do |k, v| settings[k] = v.default_settings end File.open(WEBSTATS_PATH, "w") do |f| YAML.dump(settings, f) end end DataProviders.setup(settings) class Webstats < WEBrick::HTTPServlet::AbstractServlet def do_GET(req, res) WEBrick::HTTPAuth.basic_auth(req, res, "Webstats") { |user, pass| user == 'webstats' and pass == ARGV[0] } unless ARGV.empty? body = "" if req.path_info == '/' body << <<-EOF Webstats

Stats for #{req.host}

EOF DataProviders::DATA_SOURCES.sort { |a, b| b[1].information[:importance] <=> a[1].information[:importance] }.each do |(k, v)| r = v.renderer body << %{

#{r[:name]}

Loading...
} end body << <<-EOF
EOF elsif req.path_info == '/update' out = {} DataProviders::DATA_SOURCES.each_pair do |k, v| out[k] = v.get.dup end fix_leaves_hash(out) body << out.to_json elsif req.path_info == '/information' out = {} DataProviders::DATA_SOURCES.each_pair { |k, v| out[k] = v.information } body << out.to_json end res.body = body res['Content-Type'] = "text/html" end private def fix_leaves_array(array) array.each_with_index do |v, i| if v.is_a? Numeric array[i] = v.formatted elsif v.is_a? Hash array[i] = fix_leaves_hash(array[i].dup) elsif v.is_a? Array array[i] = fix_leaves_array(array[i].dup) end end end def fix_leaves_hash(hash) hash.each_pair do |k, v| if v.is_a? Numeric hash[k] = v.formatted elsif v.is_a? Hash hash[k] = fix_leaves_hash(hash[k].dup) elsif v.is_a? Array hash[k] = fix_leaves_array(hash[k].dup) end end end end s = WEBrick::HTTPServer.new(:Port => 9970) death = proc do s.shutdown DataProviders::DATA_SOURCES.each_pair { |k, v| v.kill } end trap("INT", death) trap("TERM", death) s.mount("/", Webstats) s.start