-# All plugins should include this module. It defines the basic
-# operations that all plugins are supposed to support.
+require 'common/domain'
+require 'common/user'
+
+# Methods that all plugins must provide. Certain operations -- for
+# example, user listing -- must be supported by all plugins. These
+# operations are defined here, often with naive default
+# implementations, but it is up to each individual plugin to ensure
+# that they are in fact implemented (well).
+#
module Plugin
- def Plugin.included(c)
- # Callback, called whenever another class or module includes this
- # one. The parameter given is the name of the class or module
- # that included us.
- @includers ||= []
- @includers << c
+ # These are class methods for runnable plugins, meant to be
+ # _extended_. Those runnable plugins get a magic *run* method but
+ # need to define their own *runner* and *dummy_runner* to make it
+ # work.
+ #
+ module Run
+
+ # A callback function, called whenever another class or module
+ # includes this one. This is used to build a list of all things
+ # that inherited this class. Having such a list lets us run a
+ # collection of plugins without knowing in advance what they are.
+ #
+ # @param c [Class,Module] the name of the class or module that
+ # included us.
+ #
+ def included(c)
+ @includers ||= []
+ @includers << c
+ end
+
+
+ # Obtain the list of classes and modules that have included this one.
+ #
+ # @return [Array<Class,Module>] the list of classes and modules
+ # that have included this one.
+ #
+ def includers()
+ @includers ||= []
+ return @includers
+ end
+
+
+ # The runner class associated with this plugin. This method must
+ # be supplied by the child class, since they will all have
+ # different runners.
+ #
+ # @return [Class] the runner class associated with this plugin.
+ #
+ def runner()
+ raise NotImplementedError
+ end
+
+
+ # The "dummy" runner class associated with this plugin. This method
+ # must be supplied by the child class, since they will all have
+ # different dummy runners.
+ #
+ # @return [Class] the dummy runner class associated with this
+ # plugin.
+ #
+ def dummy_runner()
+ raise NotImplementedError
+ end
+
+
+ # Run all of the plugins that have included this module.
+ #
+ # @param cfg [Configuration] the configuration options to pass to
+ # each of the plugins.
+ #
+ # @param args [Array<Object>] a variable number of additional
+ # arguments to be passed to the plugins we're running.
+ #
+ def run(cfg, *args)
+ includers().each do |includer|
+ plugin = includer.new(cfg)
+
+ if cfg.i_mean_business then
+ runner = runner().new()
+ else
+ runner = dummy_runner().new()
+ end
+
+ # The splat passes the correct (we hope) number of arguments to the
+ # appropriate runner. The Rm(Dummy)Runner have splats on their
+ # *target arguments as well, to turn ARGV back into an array.
+ runner.run(cfg, plugin, *args)
+ end
+ end
end
- def Plugin.includers
- return @includers
+
+ # A generic version of {#describe_user}/{#describe_domain} that
+ # dispatches base on the class of the target.
+ #
+ # @param target [User,Domain] either a user or a domain to describe.
+ #
+ # @return [String] a string describing the *target*. The contents of
+ # the string depend on the plugin.
+ #
+ def describe(target)
+ if target.is_a?(User)
+ if user_exists(target) then
+ return describe_user(target)
+ else
+ return 'User not found'
+ end
+ elsif target.is_a?(Domain)
+ if domain_exists(target) then
+ return describe_domain(target)
+ else
+ return 'Domain not found'
+ end
+ else
+ raise NotImplementedError
+ end
end
+
+ # Provide a description of the given *domain*. This is output along
+ # with the domain name and can be anything of use to the system
+ # administrator. The default doesn't do anything useful and should
+ # be overridden if possible.
+ #
+ # @param domain [Domain] the domain to describe.
+ #
+ # @return [String] a string description of *domain*.
+ #
def describe_domain(domain)
- # Provide a "description" of the domain. This is output along
- # with the domain name and can be anything of use to the system
- # administrator.
- raise NotImplementedError
+ return domain.to_s()
end
- def describe_account(account)
- # Provide a "description" of the account. This is output along
- # with the domain name and can be anything of use to the system
- # administrator.
- raise NotImplementedError
+
+ # Provide a description of the given *user*. This is output along
+ # with the username and can be anything of use to the system
+ # administrator. The default doesn't do anything useful and should
+ # be overridden if possible.
+ #
+ # @param user [User] the domain to describe.
+ #
+ # @return [String] a string description of *user*.
+ #
+ def describe_user(user)
+ return user.to_s()
end
+
+ # Return a list of all users managed by this plugin. This must be
+ # supplied by the individual plugins (who know how to find their
+ # users).
+ #
+ # @return [Array<User>] a list of the users that this plugin knows
+ # about.
+ #
def list_users()
- # Return a list of all users managed by this plugin.
raise NotImplementedError
end
+
+ # Return a list of all domains managed by this plugin. This must be
+ # supplied by the individual plugins (who know how to find their
+ # domains). Many plugins will not have a separate concept of
+ # "domain", so the default implementation constructs a list of
+ # domains resulting from {#list_users}.
+ #
+ # For plugins that do know about domains, smarter implementations
+ # are surely possible.
+ #
+ # @return [Array<Domain>] a list of the domains that this plugin knows
+ # about.
+ #
+ def list_domains()
+ users = list_users()
+ domains = users.map{ |u| u.domain() }
+ return domains.uniq()
+ end
+
+
+ # Does the given *user* exist for this plugin? We use a naive
+ # implementation here based on {#list_users}. Plugins should override
+ # this with something faster.
+ #
+ # @param user [User] the user whose existence is in question.
+ #
+ # @return [Boolean] true if *user* exists for this plugin, and
+ # false otherwise.
+ #
+ def user_exists(user)
+ users = list_users()
+ return users.include?(user)
+ end
+
+
+ # Does the given *domain* exist for this plugin? We use a naive
+ # implementation here based on {#list_domains}. Plugins that know
+ # about domains should override this with something fast.
+ #
+ # @param domain [Domain] the domain whose existence is in question.
+ #
+ # @return [Boolean] true if *domain* exists for this plugin, and
+ # false otherwise.
+ #
+ def domain_exists(domain)
+ domains = list_domains()
+ return domains.include?(domain)
+ end
+
+
+ # List all users belonging to the given domains. We say that a user
+ # belongs to a domain "example.com" if the domain part of the user's
+ # email address is "example.com".
+ #
+ # This uses a naive loop, but relies only on the existence of
+ # {#list_users}. Plugins that know about domains should provide a
+ # more efficient implementation.
+ #
+ # @param domains [Array<Domain>] the domains whose users we want.
+ #
+ # @return [Array<User>] a list of {User} objects belonging to
+ # *domains* for this plugin.
+ #
def list_domains_users(domains)
- # Get all usernames belonging to the given domains. If a username
- # ends in @example.com, it belongs to the domain example.com
- #
- # This uses a naive loop, but relies only on the existence of a
- # list_users() method which is guaranteed above. More efficient
- # implementations can usually be made within the plugin.
domains_users = []
- usernames = list_users();
+ users = list_users();
domains.each do |d|
- matches = usernames.select do |username|
- username =~ /@#{d}$/
+ matches = users.select do |user|
+ user.domainpart() == d.to_s()
end
domains_users += matches