diff options
author | Jérémy Zurcher <jeremy@asynk.ch> | 2011-06-30 07:34:27 +0200 |
---|---|---|
committer | Jérémy Zurcher <jeremy@asynk.ch> | 2011-06-30 08:09:05 +0200 |
commit | 3b4e52e4a702c00728fff51f4497ebf8c85d8738 (patch) | |
tree | dcea252f8a893d194c20bf5076f11f5b9459edb5 /lib | |
parent | e807f70bfa78f54ff071985e8c727c8f08ebb69c (diff) | |
download | ayk-3b4e52e4a702c00728fff51f4497ebf8c85d8738.zip ayk-3b4e52e4a702c00728fff51f4497ebf8c85d8738.tar.gz |
add dispatcher and math
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ayk/dispatcher.rb | 282 | ||||
-rw-r--r-- | lib/ayk/math.rb | 111 |
2 files changed, 393 insertions, 0 deletions
diff --git a/lib/ayk/dispatcher.rb b/lib/ayk/dispatcher.rb new file mode 100644 index 0000000..2a3d619 --- /dev/null +++ b/lib/ayk/dispatcher.rb @@ -0,0 +1,282 @@ +#! /usr/bin/env ruby +# -*- coding: utf-8 -*- + +#---------------------------------------------------------------------------- +# +# File : dispatcher.rb +# +# Author : Jérémy Zurcher <jeremy@asynk.ch> +# +# Date : 16/10/07 +# +# License : +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Purpose : messages management on a per channel basis with level thresholds, rotations +# +#---------------------------------------------------------------------------- + + +#---------------------------------------------------------------------------- +# messages management on a per channel basis with level thresholds, rotations +module Dispatcher + + module Level + OFF = 0 + ON = 1 + DBG1 = 2 + DBG2 = 3 + DBG3 = 4 + def format_sev( sev ) ['< OFF >','< ON >','< DBG1 >','< DBG2 >','< DBG3 >'][sev] || ' <UNKNOWN>' end + end + + # this class is in charge of dispatching an event to all it's registered sinks + class Channel + attr_reader :id, :sinks + attr_accessor :level + # +id+ is an id + # +level+ is a value used to except or reject messages with level, see Logger for level levels + def initialize( id, level=Level::ON ) + @id = id + @level = level + @sinks = [] + end + # register sink as a sink + def register( sink ) @sinks<<sink if not @sinks.include? sink end + # unregister sink + def unregister( sink ) @sinks.delete sink end + # remove all sinks + def clear; @sinks.clear end + # dispatch +data+ to all of it's sinks if messages's level+ is lower enough + def dispatch( data, level ) + return if level>@level + @sinks.each{ |s| + next if level>s.level + if s.respond_to? :handle_rot + s.handle_rot data, level + else + s.handle data, level + end + } + end + # pretty output + def to_s + ret = "#{self.class} #{@id}\n level : #{@level}\n sinks :" + @sinks.each{ |s| ret += "\n\t#{s.id}" } + ret + end + end + + # this the base class for simple sinks + # you should inherit from Sink and implement +handle+ method + class Sink + attr_reader :id, :channels + attr_accessor :level + # +id+ is only an id + # +channels+ is a list of channels id which this instance belongs to + # +level+ + def initialize( id, channels, level=Level::INFO ) + # TODO raise if not channels.respond_to? :each + @id = id + @channels = channels + @level = level + end + # pretty output + def to_s + ret = "#{self.class} #{@id}\n rotation : #{@rotation_scheme.nil? ? 'never' : @rotation_scheme.to_s + " next on #{@next_rotation}" }\n channels :" + @channels.each{ |c| ret +="\n\t#{c} " } + ret + end + # TO BE IMPLEMENTED, should handle data + def handle( data, level ); raise NotImplementedError.new end + end + + # this the base class for sinks with time based rotation capabilities + # you should inherit from SinkRotate and implement +handle+ and +rotate+ methods + class SinkRotate < Sink + attr_reader :rotation_scheme, :next_rotation + # +id+ is only an id + # +channels+ is a list of channels id which this instance belongs to + # +rotation_scheme+ is one of : +nil, :hourly, :daily, :weekly, :monthly+ + def initialize( id, channels, rotation_scheme, level=Level::ON ) + super( id, channels, level ) + @rotation_scheme = rotation_scheme + @next_rotation = compute_next_rotation + end + # call rotate if data respond_to :time and time is >= then next_rotation + def handle_rot( data, level ) + if data.respond_to? :time and data.time >= @next_rotation + rotate @next_rotation + @next_rotation = compute_next_rotation data.time + end + handle data, level + end + # return computed next rotation time + # +t+ base computation time for simulation purpose + def compute_next_rotation( t=Time.now ) + case @rotation_scheme + when :hourly then + t += 3600 + Time.mktime(t.year, t.month, t.mday, t.hour, 0, 0) + when :daily then + t += 86400 # 24*3600 + Time.mktime(t.year, t.month, t.mday, 0, 0, 0) + when :weekly then + t += (8-t.wday)*86400 # 24*3600 # monday 00:00:00 + Time.mktime(t.year, t.month, t.mday, 0, 0, 0) + when :monthly then + if t.month <12 + Time.mktime(t.year, t.month+1, 1, 0, 0, 0) + else + Time.mktime(t.year+1, 1, 1, 0, 0, 0) + end + else + t + end + end + # TO BE IMPLEMENTED, should handle rotation time + def rotate( t ); raise NotImplementedError.new end + end + + # + class Dispatcher + # + def initialize; + @sinks = [] + @channels = {} + end + # clear channels and sinks + def clear + @channels.clear + @sinks.clear + end + # send +data+ in all channels + def send_all( data, level=Level::ON ) + @channels.each{ |k,c| c.dispatch data, level } + end + # send +data+ in +channels+ + def send( data, channels, level=Level::ON ) + level = data.level if data.respond_to? :level + channels.each{ |c| @channels[c].dispatch data, level if @channels.has_key? c } + end + # find +id+ in +what+ + def find( what, id ) + return @channels[id] if what==:channel + return @sinks.find{ |s| s.id==id } if what==:sink + nil + end + # return sink +s_id+ if exists + + # pretty output + def to_s + ret = "####{self.class}" + ret +="\n\n ## Channels :" + @channels.each{ |i,c| ret += "\n #{c.to_s}" } + ret +="\n\n ## Sinks :" + @sinks.each{ |s| ret += "\n #{s.to_s}" } + ret + end + # set a +channel+ and update it's sinks if exists + def register_channel( channel ) + return if @channels.has_key? channel.id + @channels[channel.id] = channel + @sinks.each{ |s| s.channels.each{ |c| channel.register s if c == channel.id } } + end + # remove +channel+ from channels list + def unregister_channel( channel ) + return if not @channels.has_key? channel.id + @sinks.each{ |s| s.channels.each{ |c| channel.unregister s if c == channel.id } } + @channels.delete channel.id + end + # register +sink+ and update +channels+ + def register_sink( sink ) + return if @sinks.include? sink + @sinks<<sink + sink.channels.each{ |c| @channels[c].register( sink ) if @channels.has_key? c } + end + # unregister +sink+ + def unregister_sink( sink ) + return if not @sinks.include? sink + sink.channels.each{ |c| @channels[c].unregister( sink ) if @channels.has_key? c } + @sinks.delete sink + end + end + + # TEST classes + + # a sink which call a hook when activated + class CallbackSink < Sink + def initialize( id, channels, level=Level::ON, &blk ) + super( id, channels, level ) + @blk = blk + end + # output this event and flush the io + def handle( data, level ); @blk.call( data, level ) end + end + + # an IO based sink for STDOUT and STDERR + class IOSink < Sink + # +id+ is the channel id + # +io+ is an IO object + # +channels+ is a list of channels id + def initialize( id, io, channels, level=Level::ON, &blk) + super( id, channels, level ) + @io = io + @blk = blk + end + # output this event and flush the io + def handle( data, level ); @io << (@blk.nil? ? data.to_s : @blk.call(data,level) ); @io.flush end + end + + # simple file sink + class FileSink < Sink + # +id+ is the channel id + # +fname+ is the file path + # +channels+ is a list of channels id + def initialize( id, fname, channels, level=Level::ON, &blk ) + super( id, channels, level ) + @fname = fname + @blk = blk + end + attr_accessor :fname + # output this event and flush the fname + def handle( data, level ) open( @fname, 'a') { |f| f << ( @blk.nil? ? data.to_s : @blk.call(data,level) ) } end + end + + # a file path based sink with rotation implemented + class FileSinkRotate < SinkRotate + # +id+ is the channel id + # +basename+ is the file path in a strftime like format + # +channels+ is a list of channels id + # +rotation+ is one of : +nil, :hourly, :daily, :weekly, :monthly+ + def initialize( id, basename, channels, rotation, level=Level::ON, &blk ) + super( id, channels, rotation, level ) + @basename = basename + rotate Time.now + @blk = blk + end + attr_accessor :fname + # output this event and flush the fname + def rotate( t ) @fname = t.strftime( @basename ) end + # output this event and flush the fname + def handle( data, level ) open( @fname, 'a') { |f| f << ( @blk.nil? ? data.to_s : @blk.call(data,level) ) } end + end +end + diff --git a/lib/ayk/math.rb b/lib/ayk/math.rb new file mode 100644 index 0000000..90a3b3a --- /dev/null +++ b/lib/ayk/math.rb @@ -0,0 +1,111 @@ +#! /usr/bin/env ruby +# -*- coding: UTF-8 -*- + +#---------------------------------------------------------------------------- +# +# File : math.rb +# Author : Jérémy Zurcher <jeremy@asynk.ch> +# Date : 14/09/09 +# License : +# +# Copyright (c) 2009 Jérémy Zurcher +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +#---------------------------------------------------------------------------- +# +#def prime? +# ('1'*self) !~ /^1?$|^(11+?)\1+$/ +#end +#def Math.gcd(x,y) +# /^(1+)\1*=\1+$/.match('1'*x+'='+'1'*y)[1].length +#end +# +# generate prime numbers < n +def Math.sieve n + sieve = [nil, nil] + (2 .. n).to_a + (2 .. Math.sqrt(n)).each do |i| + next unless sieve[i] + (i*i).step(n, i) do |j| + sieve[j] = nil + end + end + sieve.compact +end +# +# return greater common divisor +def Math.gcd x, y + y,x=x,y if x<y + while y > 0 + r = x % y + x = y + y = r + end + return x +end +# +# return true if is a prime number +def Math.prime? x +# return false if x <0 + if x==3 + return true + elsif x % 2 == 0 + return false + elsif x % 3 == 0 + return false + else + r = (x**0.5).floor + d = 5 + while d<r + if x % d == 0 + return false + end + if x % (d + 2) == 0 + return false + end + d+=6 + end + return true + end +end +# +# return a bits long prime number +def Math.prime bits + bytes_n,bits_n = bits.divmod 8 + bytes = [] + begin + # n = ( (rand()*Time.now.to_f).to_i & 0xff) | 0x80 random char better than rand(255) + 1 ????? + # rand currently uses a modified Mersenne Twister with a period of 2**19937-1 + srand() + bytes.clear + bytes_n.times do + bytes << rand(255)+1 + end + bytes << ( ((rand()*Time.now.to_f).to_i & 0xFF | 0x80) >> (8 - bits_n) ) + n = Bignum.from_bytes( bytes.inject(''){|s,b|s+=b.chr} ) + puts n + n+= 1 if n%2 == 0 + while not Math::prime? n + n += 2 + end + end while n.bits != bits + n +end +# |