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