Added more list-config accessors
This commit is contained in:
parent
00d3974363
commit
132d7471a2
2 changed files with 431 additions and 1 deletions
|
|
@ -25,6 +25,108 @@ require 'ezmlm'
|
||||||
### A Ruby interface to an ezmlm-idx mailing list directory
|
### A Ruby interface to an ezmlm-idx mailing list directory
|
||||||
class Ezmlm::List
|
class Ezmlm::List
|
||||||
|
|
||||||
|
### Create a new Ezmlm::List object for the specified +listdir+, which should be
|
||||||
|
### an ezmlm-idx mailing list directory.
|
||||||
|
def initialize( listdir )
|
||||||
|
listdir = Pathname.new( listdir ) if !listdir.is_a?( Pathname )
|
||||||
|
@listdir = listdir
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
######
|
||||||
|
public
|
||||||
|
######
|
||||||
|
|
||||||
|
# The Pathname object for the list directory
|
||||||
|
attr_reader :listdir
|
||||||
|
|
||||||
|
|
||||||
|
### Return the email address of the list's owner.
|
||||||
|
def owner
|
||||||
|
config = self.listdir + 'config'
|
||||||
|
if config.read =~ /^5:([^\n]+)$/m
|
||||||
|
return $1
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
### Fetch an Array of the email addresses for all of the list's subscribers.
|
||||||
|
def subscribers
|
||||||
|
subscribers_dir = self.listdir + 'subscribers'
|
||||||
|
return self.read_subscriber_dir( subscribers_dir )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
### Returns +true+ if subscription to the list is moderated.
|
||||||
|
def closed?
|
||||||
|
return (self.listdir + 'modsub').exist? || (self.listdir + 'remote').exist?
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
### Returns +true+ if posting to the list is moderated.
|
||||||
|
def moderated?
|
||||||
|
return (self.listdir + 'modpost').exist?
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
### Returns an Array of email addresses of people responsible for moderating subscription
|
||||||
|
### of a closed list.
|
||||||
|
def subscription_moderators
|
||||||
|
return [] unless self.closed?
|
||||||
|
|
||||||
|
modsubfile = self.listdir + 'modsub'
|
||||||
|
remotefile = self.listdir + 'remote'
|
||||||
|
|
||||||
|
subdir = nil
|
||||||
|
if modsubfile.exist? && modsubfile.read(1) == '/'
|
||||||
|
subdir = Pathname.new( modsubfile.read.chomp )
|
||||||
|
elsif remotefile.exist? && remotefile.read(1) == '/'
|
||||||
|
subdir = Pathname.new( remotefile.read.chomp )
|
||||||
|
else
|
||||||
|
subdir = self.listdir + 'mod/subscribers'
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.read_subscriber_dir( subdir )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
### Returns an Array of email addresses of people responsible for moderating posts
|
||||||
|
### sent to the list.
|
||||||
|
def message_moderators
|
||||||
|
return [] unless self.moderated?
|
||||||
|
|
||||||
|
modpostfile = self.listdir + 'modpost'
|
||||||
|
subdir = nil
|
||||||
|
|
||||||
|
if modpostfile.exist? && modpostfile.read(1) == '/'
|
||||||
|
subdir = Pathname.new( modpostfile.read.chomp )
|
||||||
|
else
|
||||||
|
subdir = self.listdir + 'mod/subscribers'
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.read_subscriber_dir( subdir )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#########
|
||||||
|
protected
|
||||||
|
#########
|
||||||
|
|
||||||
|
### Read the hashed subscriber email addresses from the specified +directory+ and return them in
|
||||||
|
### an Array.
|
||||||
|
def read_subscriber_dir( directory )
|
||||||
|
rval = []
|
||||||
|
Pathname.glob( directory + '*' ) do |hashfile|
|
||||||
|
rval.push( hashfile.read.scan(/T([^\0]+)\0/) )
|
||||||
|
end
|
||||||
|
|
||||||
|
return rval.flatten
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# vim: set nosta noet ts=4 sw=4:
|
# vim: set nosta noet ts=4 sw=4:
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,335 @@ describe Ezmlm::List do
|
||||||
include Ezmlm::SpecHelpers
|
include Ezmlm::SpecHelpers
|
||||||
|
|
||||||
|
|
||||||
it "is well-tested"
|
LISTDIR = Pathname.new( 'list' )
|
||||||
|
|
||||||
|
TEST_SUBSCRIBERS = %w[
|
||||||
|
pete.chaffee@toadsmackers.com
|
||||||
|
dolphinzombie@alahalohamorra.com
|
||||||
|
piratebanker@yahoo.com
|
||||||
|
]
|
||||||
|
|
||||||
|
TEST_MODERATORS = %w[
|
||||||
|
dolphinzombie@alahalohamorra.com
|
||||||
|
]
|
||||||
|
|
||||||
|
TEST_OWNER = 'listowner@rumpus-the-whale.info'
|
||||||
|
|
||||||
|
TEST_CUSTOM_MODERATORS_DIR = '/foo/bar/clowns'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
it "can create a new list"
|
||||||
|
|
||||||
|
###
|
||||||
|
### List manager functions
|
||||||
|
###
|
||||||
|
describe "list manager functions" do
|
||||||
|
|
||||||
|
before( :each ) do
|
||||||
|
@listpath = LISTDIR.dup
|
||||||
|
@list = Ezmlm::List.new( @listpath )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "can return a list of subscribers' email addresses" do
|
||||||
|
subscribers_dir = LISTDIR + 'subscribers'
|
||||||
|
|
||||||
|
expectation = Pathname.should_receive( :glob ).with( subscribers_dir + '*' )
|
||||||
|
|
||||||
|
TEST_SUBSCRIBERS.each do |email|
|
||||||
|
mock_subfile = mock( "Mock subscribers file for '#{email}'" )
|
||||||
|
mock_subfile.should_receive( :read ).and_return( "T#{email}\0" )
|
||||||
|
|
||||||
|
expectation.and_yield( mock_subfile )
|
||||||
|
end
|
||||||
|
|
||||||
|
subscribers = @list.subscribers
|
||||||
|
|
||||||
|
subscribers.should have(TEST_SUBSCRIBERS.length).members
|
||||||
|
subscribers.should include( *TEST_SUBSCRIBERS )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
### Subscriber moderation
|
||||||
|
|
||||||
|
it "knows that subscription moderation is enabled if the dir/modsub file exists" do
|
||||||
|
modsub_path_obj = mock( "Mock 'modsub' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj )
|
||||||
|
modsub_path_obj.should_receive( :exist? ).and_return( true )
|
||||||
|
|
||||||
|
@list.should be_closed()
|
||||||
|
end
|
||||||
|
|
||||||
|
it "knows that subscription moderation is enabled if the dir/remote file exists" do
|
||||||
|
modsub_path_obj = mock( "Mock 'modsub' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj )
|
||||||
|
modsub_path_obj.should_receive( :exist? ).and_return( false )
|
||||||
|
|
||||||
|
remote_path_obj = mock( "Mock 'remote' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'remote' ).and_return( remote_path_obj )
|
||||||
|
remote_path_obj.should_receive( :exist? ).and_return( true )
|
||||||
|
|
||||||
|
@list.should be_closed()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "knows that subscription moderation is disabled if neither the dir/modsub nor " +
|
||||||
|
"dir/remote files exist" do
|
||||||
|
modsub_path_obj = mock( "Mock 'modsub' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj )
|
||||||
|
modsub_path_obj.should_receive( :exist? ).and_return( false )
|
||||||
|
|
||||||
|
remote_path_obj = mock( "Mock 'remote' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'remote' ).and_return( remote_path_obj )
|
||||||
|
remote_path_obj.should_receive( :exist? ).and_return( false )
|
||||||
|
|
||||||
|
@list.should_not be_closed()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "returns an empty array of subscription moderators for an open list" do
|
||||||
|
modsub_path_obj = mock( "Mock 'modsub' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modsub' ).and_return( modsub_path_obj )
|
||||||
|
modsub_path_obj.should_receive( :exist? ).and_return( false )
|
||||||
|
|
||||||
|
remote_path_obj = mock( "Mock 'remote' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'remote' ).and_return( remote_path_obj )
|
||||||
|
remote_path_obj.should_receive( :exist? ).and_return( false )
|
||||||
|
|
||||||
|
@list.subscription_moderators.should be_empty()
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can return a list of subscription moderators' email addresses" do
|
||||||
|
# Test the moderation config files for existence
|
||||||
|
modsub_path_obj = mock( "Mock 'modsub' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modsub' ).twice.and_return( modsub_path_obj )
|
||||||
|
modsub_path_obj.should_receive( :exist? ).twice.and_return( true )
|
||||||
|
remote_path_obj = mock( "Mock 'remote' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'remote' ).and_return( remote_path_obj )
|
||||||
|
remote_path_obj.should_receive( :exist? ).once.and_return( true )
|
||||||
|
|
||||||
|
# Try to read directory names from both config files
|
||||||
|
modsub_path_obj.should_receive( :read ).with( 1 ).and_return( nil )
|
||||||
|
remote_path_obj.should_receive( :read ).with( 1 ).and_return( nil )
|
||||||
|
|
||||||
|
# Read subscribers from the default directory
|
||||||
|
subscribers_dir = mock( "Mock moderator subscribers directory" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'mod/subscribers' ).and_return( subscribers_dir )
|
||||||
|
subscribers_dir.should_receive( :+ ).with( '*' ).and_return( :mod_sub_dir )
|
||||||
|
expectation = Pathname.should_receive( :glob ).with( :mod_sub_dir )
|
||||||
|
|
||||||
|
TEST_MODERATORS.each do |email|
|
||||||
|
mock_subfile = mock( "Mock subscribers file for '#{email}'" )
|
||||||
|
mock_subfile.should_receive( :read ).and_return( "T#{email}\0" )
|
||||||
|
|
||||||
|
expectation.and_yield( mock_subfile )
|
||||||
|
end
|
||||||
|
|
||||||
|
mods = @list.subscription_moderators
|
||||||
|
mods.should have(TEST_MODERATORS.length).members
|
||||||
|
mods.should include( *TEST_MODERATORS )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "can return a list of subscription moderators' email addresses when the moderators " +
|
||||||
|
"directory has been customized" do
|
||||||
|
# Test the moderation config files for existence
|
||||||
|
modsub_path_obj = mock( "Mock 'modsub' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modsub' ).twice.and_return( modsub_path_obj )
|
||||||
|
modsub_path_obj.should_receive( :exist? ).twice.and_return( true )
|
||||||
|
@listpath.should_receive( :+ ).with( 'remote' )
|
||||||
|
|
||||||
|
# Try to read directory names from both config files
|
||||||
|
modsub_path_obj.should_receive( :read ).with( 1 ).and_return( '/' )
|
||||||
|
modsub_path_obj.should_receive( :read ).with().and_return( TEST_CUSTOM_MODERATORS_DIR )
|
||||||
|
|
||||||
|
custom_mod_path = mock( "Mock path object for customized moderator dir" )
|
||||||
|
Pathname.should_receive( :new ).with( TEST_CUSTOM_MODERATORS_DIR ).and_return( custom_mod_path )
|
||||||
|
|
||||||
|
# Read subscribers from the default file
|
||||||
|
custom_mod_path.should_receive( :+ ).with( '*' ).and_return( :mod_sub_dir )
|
||||||
|
expectation = Pathname.should_receive( :glob ).with( :mod_sub_dir )
|
||||||
|
|
||||||
|
TEST_MODERATORS.each do |email|
|
||||||
|
mock_subfile = mock( "Mock subscribers file for '#{email}'" )
|
||||||
|
mock_subfile.should_receive( :read ).and_return( "T#{email}\0" )
|
||||||
|
|
||||||
|
expectation.and_yield( mock_subfile )
|
||||||
|
end
|
||||||
|
|
||||||
|
mods = @list.subscription_moderators
|
||||||
|
mods.should have(TEST_MODERATORS.length).members
|
||||||
|
mods.should include( *TEST_MODERATORS )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
### Message moderation
|
||||||
|
|
||||||
|
it "knows that subscription moderation is enabled if the dir/modpost file exists" do
|
||||||
|
modpost_path_obj = mock( "Mock 'modpost' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modpost' ).and_return( modpost_path_obj )
|
||||||
|
modpost_path_obj.should_receive( :exist? ).and_return( true )
|
||||||
|
|
||||||
|
@list.should be_moderated()
|
||||||
|
end
|
||||||
|
|
||||||
|
it "knows that subscription moderation is disabled if the dir/modpost file doesn't exist" do
|
||||||
|
modpost_path_obj = mock( "Mock 'modpost' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modpost' ).and_return( modpost_path_obj )
|
||||||
|
modpost_path_obj.should_receive( :exist? ).and_return( false )
|
||||||
|
|
||||||
|
@list.should_not be_moderated()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "returns an empty array of message moderators for an open list" do
|
||||||
|
modpost_path_obj = mock( "Mock 'modpost' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modpost' ).and_return( modpost_path_obj )
|
||||||
|
modpost_path_obj.should_receive( :exist? ).and_return( false )
|
||||||
|
|
||||||
|
@list.message_moderators.should be_empty()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "can return a list of message moderators' email addresses" do
|
||||||
|
# Test the moderation config file for existence
|
||||||
|
modpost_path_obj = mock( "Mock 'modpost' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modpost' ).twice.and_return( modpost_path_obj )
|
||||||
|
modpost_path_obj.should_receive( :exist? ).twice.and_return( true )
|
||||||
|
|
||||||
|
# Try to read directory names from the config file
|
||||||
|
modpost_path_obj.should_receive( :read ).with( 1 ).and_return( nil )
|
||||||
|
|
||||||
|
# Read subscribers from the default directory
|
||||||
|
subscribers_dir = mock( "Mock moderator subscribers directory" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'mod/subscribers' ).and_return( subscribers_dir )
|
||||||
|
subscribers_dir.should_receive( :+ ).with( '*' ).and_return( :mod_sub_dir )
|
||||||
|
expectation = Pathname.should_receive( :glob ).with( :mod_sub_dir )
|
||||||
|
|
||||||
|
TEST_MODERATORS.each do |email|
|
||||||
|
mock_subfile = mock( "Mock subscribers file for '#{email}'" )
|
||||||
|
mock_subfile.should_receive( :read ).and_return( "T#{email}\0" )
|
||||||
|
|
||||||
|
expectation.and_yield( mock_subfile )
|
||||||
|
end
|
||||||
|
|
||||||
|
mods = @list.message_moderators
|
||||||
|
mods.should have(TEST_MODERATORS.length).members
|
||||||
|
mods.should include( *TEST_MODERATORS )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "can return a list of message moderators' email addresses when the moderators " +
|
||||||
|
"directory has been customized" do
|
||||||
|
# Test the moderation config files for existence
|
||||||
|
modpost_path_obj = mock( "Mock 'modpost' path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'modpost' ).twice.and_return( modpost_path_obj )
|
||||||
|
modpost_path_obj.should_receive( :exist? ).twice.and_return( true )
|
||||||
|
|
||||||
|
# Try to read directory names from both config files
|
||||||
|
modpost_path_obj.should_receive( :read ).with( 1 ).and_return( '/' )
|
||||||
|
modpost_path_obj.should_receive( :read ).with().and_return( TEST_CUSTOM_MODERATORS_DIR )
|
||||||
|
|
||||||
|
custom_mod_path = mock( "Mock path object for customized moderator dir" )
|
||||||
|
Pathname.should_receive( :new ).with( TEST_CUSTOM_MODERATORS_DIR ).and_return( custom_mod_path )
|
||||||
|
|
||||||
|
# Read subscribers from the default file
|
||||||
|
custom_mod_path.should_receive( :+ ).with( '*' ).and_return( :mod_sub_dir )
|
||||||
|
expectation = Pathname.should_receive( :glob ).with( :mod_sub_dir )
|
||||||
|
|
||||||
|
TEST_MODERATORS.each do |email|
|
||||||
|
mock_subfile = mock( "Mock subscribers file for '#{email}'" )
|
||||||
|
mock_subfile.should_receive( :read ).and_return( "T#{email}\0" )
|
||||||
|
|
||||||
|
expectation.and_yield( mock_subfile )
|
||||||
|
end
|
||||||
|
|
||||||
|
mods = @list.message_moderators
|
||||||
|
mods.should have(TEST_MODERATORS.length).members
|
||||||
|
mods.should include( *TEST_MODERATORS )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
### List owner
|
||||||
|
|
||||||
|
TEST_CONFIG_WITHOUT_OWNER = <<-"EOF".gsub( /^\t+/, '' )
|
||||||
|
F:-aBCDeFGHijKlMnOpQrStUVWXYZ
|
||||||
|
X:
|
||||||
|
D:/var/qmail/alias/lists/waffle-lovers/
|
||||||
|
T:/var/qmail/alias/.qmail-waffle-lovers
|
||||||
|
L:waffle-lovers
|
||||||
|
H:lists.syrup.info
|
||||||
|
C:
|
||||||
|
0:
|
||||||
|
3:
|
||||||
|
4:
|
||||||
|
5:
|
||||||
|
6:
|
||||||
|
7:
|
||||||
|
8:
|
||||||
|
9:
|
||||||
|
EOF
|
||||||
|
|
||||||
|
it "returns nil when the list doesn't have an owner in its config" do
|
||||||
|
config_path_obj = mock( "Config path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'config' ).and_return( config_path_obj )
|
||||||
|
config_path_obj.should_receive( :read ).and_return( TEST_CONFIG_WITHOUT_OWNER )
|
||||||
|
|
||||||
|
@list.owner.should == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CONFIG_WITH_OWNER = <<-"EOF".gsub( /^\t+/, '' )
|
||||||
|
F:-aBCDeFGHijKlMnOpQrStUVWXYZ
|
||||||
|
X:
|
||||||
|
D:/var/qmail/alias/lists/waffle-lovers/
|
||||||
|
T:/var/qmail/alias/.qmail-waffle-lovers
|
||||||
|
L:waffle-lovers
|
||||||
|
H:lists.syrup.info
|
||||||
|
C:
|
||||||
|
0:
|
||||||
|
3:
|
||||||
|
4:
|
||||||
|
5:#{TEST_OWNER}
|
||||||
|
6:
|
||||||
|
7:
|
||||||
|
8:
|
||||||
|
9:
|
||||||
|
EOF
|
||||||
|
|
||||||
|
it "can return the email address of the list owner" do
|
||||||
|
config_path_obj = mock( "Config path object" )
|
||||||
|
@listpath.should_receive( :+ ).with( 'config' ).and_return( config_path_obj )
|
||||||
|
config_path_obj.should_receive( :read ).and_return( TEST_CONFIG_WITH_OWNER )
|
||||||
|
|
||||||
|
@list.owner.should == TEST_OWNER
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
###
|
||||||
|
### Archive functions
|
||||||
|
###
|
||||||
|
it "can return the count of archived posts"
|
||||||
|
|
||||||
|
it "can return a hash of the subjects of all archived posts to message ids"
|
||||||
|
it "can return an Array of the subjects of all archived posts"
|
||||||
|
|
||||||
|
it "can return a hash of the threads of all archived posts to message ids"
|
||||||
|
it "can return an Array of the threads of all archived posts"
|
||||||
|
|
||||||
|
it "can return a hash of the authors of all archived posts to message ids"
|
||||||
|
it "can return an Array of the authors of all archived posts"
|
||||||
|
|
||||||
|
|
||||||
|
it "can fetch the body of an archived post by message id"
|
||||||
|
it "can fetch the header of an archived post by message id"
|
||||||
|
|
||||||
|
it "can fetch the date of the last archived post"
|
||||||
|
it "can fetch the author of the last archived post"
|
||||||
|
it "can fetch the subject of the last archived post"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue