summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorJérémy Zurcher <jeremy@asynk.ch>2011-06-30 07:34:27 +0200
committerJérémy Zurcher <jeremy@asynk.ch>2011-06-30 08:09:05 +0200
commit3b4e52e4a702c00728fff51f4497ebf8c85d8738 (patch)
treedcea252f8a893d194c20bf5076f11f5b9459edb5 /lib
parente807f70bfa78f54ff071985e8c727c8f08ebb69c (diff)
downloadayk-3b4e52e4a702c00728fff51f4497ebf8c85d8738.zip
ayk-3b4e52e4a702c00728fff51f4497ebf8c85d8738.tar.gz
add dispatcher and math
Diffstat (limited to 'lib')
-rw-r--r--lib/ayk/dispatcher.rb282
-rw-r--r--lib/ayk/math.rb111
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
+#