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 # 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] 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] 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 # 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) return domain.to_s() end # 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] a list of the users that this plugin knows # about. # def list_users() 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] 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] the domains whose users we want. # # @return [Array] a list of {User} objects belonging to # *domains* for this plugin. # def list_domains_users(domains) domains_users = [] users = list_users(); domains.each do |d| matches = users.select do |user| user.domainpart() == d.to_s() end domains_users += matches end return domains_users end end