lib/ezmlm/listdaemon.rb
changeset 6 66beb495a861
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/ezmlm/listdaemon.rb	Wed Aug 06 17:24:00 2008 +0000
@@ -0,0 +1,153 @@
+#!/usr/bin/ruby
+#
+# A DRb interface to one or more ezmlm-idx mailing lists.
+#
+# == Version
+#
+#  $Id$
+#
+# == Authors
+#
+# * Michael Granger <mgranger@laika.com>
+# * Jeremiah Jordan <jjordan@laika.com>
+#
+# :include: LICENSE
+# 
+#---
+#
+# Please see the file LICENSE in the base directory for licensing details.
+#
+
+require 'pathname'
+require 'ezmlm'
+require 'ezmlm/list'
+require 'drb'
+require 'ostruct'
+
+
+### A DRb interface to one or more ezmlm-idx mailing lists
+class Ezmlm::ListDaemon
+	
+	# The default port to listen on
+	DEFAULT_PORT = 32315
+	
+	# The default address to bind to
+	DEFAULT_ADDRESS = '127.0.0.1'
+	
+	
+	### The interface that is presented to DRb
+	class Service
+		include Enumerable
+		
+		### Create a new service endpoint for the specified +listsdir+, which is a directory
+		### which contains ezmlm-idx list directories.
+		def initialize( listsdir )
+			listsdir = Pathname.new( listsdir )
+			@listsdir = listsdir
+		end
+
+
+		######
+		public
+		######
+
+		# The directory which contains the list directories that should be served.
+		attr_reader :listsdir
+		
+		
+		### Create a new Ezmlm::List object for the list directory with the specified +name+.
+		def get_list( name )
+			name = validate_listdir_name( name )
+			return Ezmlm::List.new( self.listsdir + name )
+		end
+
+
+		### Iterate over each current list in the Service's listsdir, yielding an Ezmlm::List object
+		### for each one.
+		def each_list( &block ) # :yields: list_object
+			Ezmlm.each_list( self.listsdir, &block )
+		end
+		alias_method :each, :each_list
+		
+
+		#######
+		private
+		#######
+
+		VALID_LISTNAME_PATTERN = /^[a-z0-9.-]+$/i
+
+		### Ensure that the given +name+ is a valid list name, raising an exception if not. Returns
+		### an untainted copy of +name+.
+		def validate_listdir_name( name )
+			unless match = VALID_LISTNAME_PATTERN.match( name )
+				raise ArgumentError, "invalid list name %p" % [ name ]
+			end
+			
+			return match[0].untaint
+		end
+		
+	end # class Service
+	
+	
+
+	### Return an OpenStruct that contains the default options
+	def self::default_options
+		opts = OpenStruct.new
+
+		opts.bind_addr  = DEFAULT_ADDRESS
+		opts.bind_port  = DEFAULT_PORT
+		opts.debugmode  = false
+		opts.helpmode   = false
+		opts.foreground = false
+
+		return opts
+	end
+	
+	
+	#################################################################
+	###	I N S T A N C E   M E T H O D S
+	#################################################################
+
+	### Create a new Ezmlm::ListDaemon that will serve objects for the list directories
+	### contained in +listsdir+. The +options+ argument, if given, is an object (such as the one
+	### returned from ::default_options) that contains values for the following methods:
+	### 
+	### bind_addr::
+	###   The address to bind to. Defaults to DEFAULT_ADDRESS.
+	### bind_port::
+	###   The port to listen on. Defaults to DEFAULT_PORT.
+	### debugmode:: 
+	###   Whether to run in debugging mode, which causes the daemon to run in the foreground
+	###   and send any output to STDERR. Defaults to +false+.
+	### foreground::
+	###   Don't go into the background.
+	def initialize( listsdir, options=nil )
+		@service = Service.new( listsdir )
+		@options = options || self.class.default_options
+	end
+
+
+	######
+	public
+	######
+
+	# The daemon's configuration options
+	attr_reader :options
+	
+	# The Ezmlm::ListDaemon::Service object that serves as the DRb interface
+	attr_reader :service
+
+
+	### Daemonize unless configured otherwise, start the DRb service and return the listening 
+	### Thread object
+	def start
+		uri = "druby://%s:%d" % [ self.options.bind_addr, self.options.bind_port ]
+		DRb.start_service( uri, @service )
+		
+		return DRb.thread
+	end
+	
+
+end # class Ezmlm::ListDaemon
+
+# vim: set nosta noet ts=4 sw=4: