X-Git-Url: http://gitweb.michael.orlitzky.com/?p=mailshears.git;a=blobdiff_plain;f=lib%2Fcommon%2Fplugin.rb;h=1178e2fb9a238293b22e72c3620d03b44b6e8782;hp=633c8fd26d0c2f4bd467ff3d33aa3a48cc1a93fc;hb=58849f9ba58bd0804ffe6c6d8248caf2ab66dc66;hpb=483d14dc8228a81d12fb109d3ed6510e2d2b03c1;ds=sidebyside diff --git a/lib/common/plugin.rb b/lib/common/plugin.rb index 633c8fd..1178e2f 100644 --- a/lib/common/plugin.rb +++ b/lib/common/plugin.rb @@ -1,51 +1,232 @@ -# 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] 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 - 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] 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] 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) - # 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