lib/ezmlm/list.rb
changeset 17 23c7f5c8ee39
parent 16 e135ccae6783
child 20 9d59d30685cb
--- a/lib/ezmlm/list.rb	Fri May 12 16:17:41 2017 -0700
+++ b/lib/ezmlm/list.rb	Tue May 16 13:58:34 2017 -0700
@@ -1,7 +1,11 @@
 #!/usr/bin/ruby
 # vim: set nosta noet ts=4 sw=4:
+
+
+# A Ruby interface to a single Ezmlm-idx mailing list directory.
 #
-# A Ruby interface to a single Ezmlm-idx mailing list directory.
+#    list = Ezmlm::List.new( '/path/to/listdir' )
+#
 #
 # == Version
 #
@@ -28,6 +32,9 @@
 	###
 	def initialize( listdir )
 		listdir = Pathname.new( listdir ) unless listdir.is_a?( Pathname )
+		unless listdir.directory? && ( listdir + 'mailinglist' ).exist?
+			raise ArgumentError, "%p doesn't appear to be an ezmlm-idx list." % [ listdir.to_s ]
+		end
 		@listdir = listdir
 	end
 
@@ -219,27 +226,6 @@
 	end
 
 
-	### Returns +true+ if message threading is enabled.
-	###
-	def threaded?
-		return ( self.listdir + 'threaded' ).exist?
-	end
-
-	### Disable or enable message threading.
-	###
-	### This automatically builds message indexes and thread
-	### information on an incoming message.
-	###
-	def threaded=( enable=true )
-		if enable
-			self.touch( 'threaded' )
-		else
-			self.unlink( 'threaded' )
-		end
-	end
-	alias_method :threaded, :threaded=
-
-
 	### Returns +true+ if the list is configured to respond
 	### to remote management requests.
 	###
@@ -380,21 +366,23 @@
 	### Returns +true+ if message archival is enabled.
 	###
 	def archived?
-		return ( self.listdir + 'archived' ).exist? || ( self.listdir + 'indexed' ).exist?
+		test = %w[ archived indexed threaded ].each_with_object( [] ) do |f, acc|
+			acc << self.listdir + f
+		end
+
+		return test.all?( &:exist? )
 	end
 
-	### Disable or enable message archiving (and indexing.)
+	### Disable or enable message archiving (and indexing/threading.)
 	###
-	def archive=( enable=true )
+	def archived=( enable=true )
 		if enable
-			self.touch( 'archived' )
-			self.touch( 'indexed' )
+			self.touch( 'archived', 'indexed', 'threaded' )
 		else
-			self.unlink( 'archived' )
-			self.unlink( 'indexed' )
+			self.unlink( 'archived', 'indexed', 'threaded' )
 		end
 	end
-	alias_method :archive, :archive=
+	alias_method :archived, :archived=
 
 	### Returns +true+ if the message archive is accessible only to
 	### moderators.
@@ -653,9 +641,8 @@
 	### Returns an individual message if archiving was enabled.
 	###
 	def message( message_id )
-		raise "Archiving is not enabled." unless self.archived?
 		raise "Message archive is empty." if self.message_count.zero?
-		return Ezmlm::List::Message.new( self, message_id )
+		return Ezmlm::List::Message.new( self, message_id ) rescue nil
 	end
 
 	### Lazy load each message ID as a Ezmlm::List::Message,
@@ -671,8 +658,7 @@
 	### Return a Thread object for the given +thread_id+.
 	###
 	def thread( thread_id )
-		raise "Archiving is not enabled." unless self.archived?
-		return Ezmlm::List::Thread.new( self, thread_id )
+		return Ezmlm::List::Thread.new( self, thread_id ) rescue nil
 	end
 
 
@@ -680,9 +666,8 @@
 	### could also be an email address.
 	###
 	def author( author_id )
-		raise "Archiving is not enabled." unless self.archived?
 		author_id = Ezmlm::Hash.address(author_id) if author_id.index( '@' )
-		return Ezmlm::List::Author.new( self, author_id )
+		return Ezmlm::List::Author.new( self, author_id ) rescue nil
 	end
 
 
@@ -700,22 +685,25 @@
 			index = archivedir + dir.to_s + 'index'
 			next unless index.exist?
 
-			index.each_line.lazy.slice_before( /^\d+:/ ).each do |message|
-				match = message[0].match( /^(?<message_id>\d+): (?<thread_id>\w+)/ )
-				next unless match
-				thread_id  = match[ :thread_id ]
+			index.open( 'r', encoding: Encoding::ISO8859_1 ) do |fh|
+				fh.each_line.lazy.slice_before( /^\d+:/ ).each do |message|
+
+					match = message[0].match( /^(?<message_id>\d+): (?<thread_id>\w+)/ )
+					next unless match
+					thread_id  = match[ :thread_id ]
 
-				match = message[1].match( /^(?<date>[^;]+);(?<author_id>\w+) / )
-				next unless match
-				author_id  = match[ :author_id ]
-				date       = match[ :date ]
+					match = message[1].match( /^(?<date>[^;]+);(?<author_id>\w+) / )
+					next unless match
+					author_id  = match[ :author_id ]
+					date       = match[ :date ]
 
-				metadata = {
-					date:   Time.parse( date ),
-					thread: thread_id,
-					author: author_id
-				}
-				acc << metadata
+					metadata = {
+						date:   Time.parse( date ),
+						thread: thread_id,
+						author: author_id
+					}
+					acc << metadata
+				end
 			end
 		end
 
@@ -757,18 +745,25 @@
 
 	### Simply create an empty file, safely.
 	###
-	def touch( file )
-		self.write( file ) {}
+	def touch( *file )
+		self.with_safety do
+			Array( file ).flatten.each do |f|
+				f = self.listdir + f unless f.is_a?( Pathname )
+				f.open( 'w' ) {}
+			end
+		end
 	end
 
 
 	### Delete +file+ safely.
 	###
-	def unlink( file )
-		file = self.listdir + file unless file.is_a?( Pathname )
-		return unless file.exist?
+	def unlink( *file )
 		self.with_safety do
-			file.unlink
+			Array( file ).flatten.each do |f|
+				f = self.listdir + f unless f.is_a?( Pathname )
+				next unless f.exist?
+				f.unlink
+			end
 		end
 	end