Multiple changes.
- Remove the runtime dependency on rake-compiler. - Use #rb_str_new instead of #rb_str_new2, the hash character array isn't null terminated. - Add various safeguards for object instantiations. - Remove the 'threaded' options for messages, folding them into 'archived'. If archiving is enabled, so is threading. - Return nil for lookups from the list object instead of raising exceptions. - Open subject indexes with the proper encodings (thanks Michael Granger!) - Allow touching and unlinking files to operate on multiple paths at once, within a single safety() wrap.
This commit is contained in:
parent
c99bdfe747
commit
3871084daa
17 changed files with 257 additions and 110 deletions
|
|
@ -1,8 +1,12 @@
|
|||
#!/usr/bin/ruby
|
||||
# vim: set nosta noet ts=4 sw=4:
|
||||
#
|
||||
|
||||
|
||||
# A Ruby interface to a single Ezmlm-idx mailing list directory.
|
||||
#
|
||||
# list = Ezmlm::List.new( '/path/to/listdir' )
|
||||
#
|
||||
#
|
||||
# == Version
|
||||
#
|
||||
# $Id$
|
||||
|
|
@ -28,6 +32,9 @@ class Ezmlm::List
|
|||
###
|
||||
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 @@ class Ezmlm::List
|
|||
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 @@ class Ezmlm::List
|
|||
### 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 @@ class Ezmlm::List
|
|||
### 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 @@ class Ezmlm::List
|
|||
### 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 @@ class Ezmlm::List
|
|||
### 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 @@ class Ezmlm::List
|
|||
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[1].match( /^(?<date>[^;]+);(?<author_id>\w+) / )
|
||||
next unless match
|
||||
author_id = match[ :author_id ]
|
||||
date = match[ :date ]
|
||||
match = message[0].match( /^(?<message_id>\d+): (?<thread_id>\w+)/ )
|
||||
next unless match
|
||||
thread_id = match[ :thread_id ]
|
||||
|
||||
metadata = {
|
||||
date: Time.parse( date ),
|
||||
thread: thread_id,
|
||||
author: author_id
|
||||
}
|
||||
acc << metadata
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -757,18 +745,25 @@ class Ezmlm::List
|
|||
|
||||
### 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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/ruby
|
||||
# vim: set nosta noet ts=4 sw=4:
|
||||
#
|
||||
|
||||
|
||||
# A collection of messages authored from a unique user.
|
||||
#
|
||||
# Note that Ezmlm uses the "real name" part of an address
|
||||
|
|
@ -27,12 +28,12 @@ class Ezmlm::List::Author
|
|||
include Enumerable
|
||||
|
||||
### Instantiate a new list of messages given
|
||||
### a +list+ and a +author_id+.
|
||||
### a +list+ and an +author_id+.
|
||||
###
|
||||
def initialize( list, author_id )
|
||||
raise ArgumentError, "Unknown list object." unless list.respond_to?( :listdir )
|
||||
raise ArgumentError, "Malformed Author ID." unless author_id =~ /^\w{20}$/
|
||||
raise "Thread indexing is not enabled." unless list.threaded?
|
||||
raise "Archiving is not enabled." unless list.archived?
|
||||
|
||||
@list = list
|
||||
@id = author_id
|
||||
|
|
@ -67,6 +68,7 @@ class Ezmlm::List::Author
|
|||
yield Ezmlm::List::Message.new( self.list, id )
|
||||
end
|
||||
end
|
||||
alias_method :each_message, :each
|
||||
|
||||
|
||||
### Lazy load each thread ID as a Ezmlm::List::Thread, yielding it to the block.
|
||||
|
|
@ -92,13 +94,15 @@ class Ezmlm::List::Author
|
|||
path = self.author_path
|
||||
raise "Unknown author: %p" % [ self.id ] unless path.exist?
|
||||
|
||||
path.each_line.with_index do |line, i|
|
||||
if i.zero?
|
||||
@name = line.match( /^\w+ (.+)/ )[1]
|
||||
else
|
||||
match = line.match( /^(\d+):\d+:(\w+) / ) or next
|
||||
self.messages << match[1].to_i
|
||||
self.threads << match[2]
|
||||
path.open( 'r', encoding: Encoding::ISO8859_1 ) do |fh|
|
||||
fh.each_line.with_index do |line, i|
|
||||
if i.zero?
|
||||
@name = line.match( /^\w+ (.+)/ )[1]
|
||||
else
|
||||
match = line.match( /^(\d+):\d+:(\w+) / ) or next
|
||||
self.messages << match[1].to_i
|
||||
self.threads << match[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/ruby
|
||||
# vim: set nosta noet ts=4 sw=4:
|
||||
#
|
||||
|
||||
|
||||
# An individual list message.
|
||||
#
|
||||
# message = Ezmlm::List::Message.new( list, 24 )
|
||||
|
|
@ -31,6 +32,7 @@ class Ezmlm::List::Message
|
|||
def initialize( list, message_number=0 )
|
||||
raise ArgumentError, "Unknown list object." unless list.respond_to?( :listdir )
|
||||
raise ArgumentError, "Invalid message number (impossible)" if message_number < 1
|
||||
raise "Archiving is not enabled." unless list.archived?
|
||||
raise ArgumentError, "Invalid message number (out of list bounds)" if message_number > list.message_count
|
||||
|
||||
@list = list
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/ruby
|
||||
# vim: set nosta noet ts=4 sw=4:
|
||||
#
|
||||
|
||||
|
||||
# A collection of messages for a specific archive thread.
|
||||
#
|
||||
# thread = Ezmlm::List::Thread.new( list, 'acgcbmbmeapgpfckcdol' )
|
||||
|
|
@ -29,7 +30,7 @@ class Ezmlm::List::Thread
|
|||
def initialize( list, thread_id )
|
||||
raise ArgumentError, "Unknown list object." unless list.respond_to?( :listdir )
|
||||
raise ArgumentError, "Malformed Thread ID." unless thread_id =~ /^\w{20}$/
|
||||
raise "Thread indexing is not enabled." unless list.threaded?
|
||||
raise "Archiving is not enabled." unless list.archived?
|
||||
|
||||
@list = list
|
||||
@id = thread_id
|
||||
|
|
@ -65,6 +66,18 @@ class Ezmlm::List::Thread
|
|||
yield Ezmlm::List::Message.new( self.list, id )
|
||||
end
|
||||
end
|
||||
alias_method :each_message, :each
|
||||
|
||||
|
||||
### Lazy load each author ID as a Ezmlm::List::Author, yielding it
|
||||
### to the block.
|
||||
###
|
||||
def each_author
|
||||
self.load_thread # refresh for any thread updates since object was created
|
||||
self.authors.each do |id|
|
||||
yield Ezmlm::List::Author.new( self.list, id )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#########
|
||||
|
|
@ -79,13 +92,15 @@ class Ezmlm::List::Thread
|
|||
path = self.thread_path
|
||||
raise "Unknown thread: %p" % [ self.id ] unless path.exist?
|
||||
|
||||
path.each_line.with_index do |line, i|
|
||||
if i.zero?
|
||||
@subject = line.match( /^\w+ (.+)/ )[1]
|
||||
else
|
||||
match = line.match( /^(\d+):\d+:(\w+) / ) or next
|
||||
self.messages << match[1].to_i
|
||||
self.authors << match[2]
|
||||
path.open( 'r', encoding: Encoding::ISO8859_1 ) do |fh|
|
||||
fh.each_line.with_index do |line, i|
|
||||
if i.zero?
|
||||
@subject = line.match( /^\w+ (.+)/ )[1]
|
||||
else
|
||||
match = line.match( /^(\d+):\d+:(\w+) / ) or next
|
||||
self.messages << match[1].to_i
|
||||
self.authors << match[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue