Remove HTML docs with `rake clean`.
[mailshears.git] / lib / common / plugin.rb
1 require 'common/domain'
2 require 'common/user'
3
4 # Methods that all plugins must provide. Certain operations -- for
5 # example, user listing -- must be supported by all plugins. These
6 # operations are defined here, often with naive default
7 # implementations, but it is up to each individual plugin to ensure
8 # that they are in fact implemented (well).
9 #
10 module Plugin
11
12 # These are class methods for runnable plugins, meant to be
13 # _extended_. Those runnable plugins get a magic *run* method but
14 # need to define their own *runner* and *dummy_runner* to make it
15 # work.
16 #
17 module Run
18
19 # A callback function, called whenever another class or module
20 # includes this one. This is used to build a list of all things
21 # that inherited this class. Having such a list lets us run a
22 # collection of plugins without knowing in advance what they are.
23 #
24 # @param c [Class,Module] the name of the class or module that
25 # included us.
26 #
27 def included(c)
28 puts c.class().to_s()
29 @includers ||= []
30 @includers << c
31 end
32
33
34 # Obtain the list of classes and modules that have included this one.
35 #
36 # @return [Array<Class,Module>] the list of classes and modules
37 # that have included this one.
38 #
39 def includers()
40 @includers ||= []
41 return @includers
42 end
43
44
45 # The runner class associated with this plugin. This method must
46 # be supplied by the child class, since they will all have
47 # different runners.
48 #
49 # @return [Class] the runner class associated with this plugin.
50 #
51 def runner()
52 raise NotImplementedError
53 end
54
55
56 # The "dummy" runner class associated with this plugin. This method
57 # must be supplied by the child class, since they will all have
58 # different dummy runners.
59 #
60 # @return [Class] the dummy runner class associated with this
61 # plugin.
62 #
63 def dummy_runner()
64 raise NotImplementedError
65 end
66
67
68 # Run all of the plugins that have included this module.
69 #
70 # @param cfg [Configuration] the configuration options to pass to
71 # each of the plugins.
72 #
73 # @param args [Array<Object>] a variable number of additional
74 # arguments to be passed to the plugins we're running.
75 #
76 def run(cfg, *args)
77 includers().each do |includer|
78 plugin = includer.new(cfg)
79
80 if cfg.i_mean_business then
81 runner = runner().new()
82 else
83 runner = dummy_runner().new()
84 end
85
86 # The splat passes the correct (we hope) number of arguments to the
87 # appropriate runner. The Rm(Dummy)Runner have splats on their
88 # *target arguments as well, to turn ARGV back into an array.
89 runner.run(cfg, plugin, *args)
90 end
91 end
92 end
93
94
95 # A generic version of {#describe_user}/{#describe_domain} that
96 # dispatches base on the class of the target.
97 #
98 # @param target [User,Domain] either a user or a domain to describe.
99 #
100 # @return [String] a string describing the *target*. The contents of
101 # the string depend on the plugin.
102 #
103 def describe(target)
104 if target.is_a?(User)
105 if user_exists(target) then
106 return describe_user(target)
107 else
108 return 'User not found'
109 end
110 elsif target.is_a?(Domain)
111 if domain_exists(target) then
112 return describe_domain(target)
113 else
114 return 'Domain not found'
115 end
116 else
117 raise NotImplementedError
118 end
119 end
120
121
122 # Provide a description of the given *domain*. This is output along
123 # with the domain name and can be anything of use to the system
124 # administrator. The default doesn't do anything useful and should
125 # be overridden if possible.
126 #
127 # @param domain [Domain] the domain to describe.
128 #
129 # @return [String] a string description of *domain*.
130 #
131 def describe_domain(domain)
132 return domain.to_s()
133 end
134
135
136 # Provide a description of the given *user*. This is output along
137 # with the username and can be anything of use to the system
138 # administrator. The default doesn't do anything useful and should
139 # be overridden if possible.
140 #
141 # @param user [User] the domain to describe.
142 #
143 # @return [String] a string description of *user*.
144 #
145 def describe_user(user)
146 return user.to_s()
147 end
148
149
150 # Return a list of all users managed by this plugin. This must be
151 # supplied by the individual plugins (who know how to find their
152 # users).
153 #
154 # @return [Array<User>] a list of the users that this plugin knows
155 # about.
156 #
157 def list_users()
158 raise NotImplementedError
159 end
160
161
162 # Return a list of all domains managed by this plugin. This must be
163 # supplied by the individual plugins (who know how to find their
164 # domains). Many plugins will not have a separate concept of
165 # "domain", so the default implementation constructs a list of
166 # domains resulting from {#list_users}.
167 #
168 # For plugins that do know about domains, smarter implementations
169 # are surely possible.
170 #
171 # @return [Array<Domain>] a list of the domains that this plugin knows
172 # about.
173 #
174 def list_domains()
175 users = list_users()
176 domains = users.map{ |u| u.domain() }
177 return domains.uniq()
178 end
179
180
181 # Does the given *user* exist for this plugin? We use a naive
182 # implementation here based on {#list_users}. Plugins should override
183 # this with something faster.
184 #
185 # @param user [User] the user whose existence is in question.
186 #
187 # @return [Boolean] true if *user* exists for this plugin, and
188 # false otherwise.
189 #
190 def user_exists(user)
191 users = list_users()
192 return users.include?(user)
193 end
194
195
196 # Does the given *domain* exist for this plugin? We use a naive
197 # implementation here based on {#list_domains}. Plugins that know
198 # about domains should override this with something fast.
199 #
200 # @param domain [Domain] the domain whose existence is in question.
201 #
202 # @return [Boolean] true if *domain* exists for this plugin, and
203 # false otherwise.
204 #
205 def domain_exists(domain)
206 domains = list_domains()
207 return domains.include?(domain)
208 end
209
210
211 # List all users belonging to the given domains. We say that a user
212 # belongs to a domain "example.com" if the domain part of the user's
213 # email address is "example.com".
214 #
215 # This uses a naive loop, but relies only on the existence of
216 # {#list_users}. Plugins that know about domains should provide a
217 # more efficient implementation.
218 #
219 # @param domains [Array<Domain>] the domains whose users we want.
220 #
221 # @return [Array<User>] a list of {User} objects belonging to
222 # *domains* for this plugin.
223 #
224 def list_domains_users(domains)
225 domains_users = []
226
227 users = list_users();
228 domains.each do |d|
229 matches = users.select do |user|
230 user.domainpart() == d.to_s()
231 end
232
233 domains_users += matches
234 end
235
236 return domains_users
237 end
238
239 end