1 | var fs = require('fs-extra'); |
||
2 | var request = require('request'); |
||
3 | var express = require('express'); |
||
4 | var extend = require('extend'); |
||
5 | |||
6 | // ------------------------------------------ |
||
7 | // CONSTRUCTOR |
||
8 | // ------------------------------------------ |
||
9 | |||
10 | var init = function(){ |
||
11 | info('Starting PluginManager ...'); |
||
12 | |||
13 | // Refresh local plugins |
||
14 | refresh(); |
||
15 | |||
16 | return PluginManager; |
||
17 | } |
||
18 | |||
19 | // ------------------------------------------ |
||
20 | // HELPER: REQUIRE |
||
21 | // ------------------------------------------ |
||
22 | |||
23 | /** |
||
24 | * Removes a module from the cache |
||
25 | */ |
||
26 | require.uncache = function (moduleName) { |
||
27 | // Run over the cache looking for the files |
||
28 | // loaded by the specified module name |
||
29 | require.searchCache(moduleName, function (mod) { |
||
30 | delete require.cache[mod.id]; |
||
31 | }); |
||
32 | }; |
||
33 | |||
34 | /** |
||
35 | * Runs over the cache to search for all the cached files |
||
36 | */ |
||
37 | require.searchCache = function (moduleName, callback) { |
||
38 | // Resolve the module identified by the specified name |
||
39 | var mod = require.resolve(moduleName); |
||
40 | |||
41 | // Check if the module has been resolved and found within |
||
42 | // the cache |
||
43 | if (mod && ((mod = require.cache[mod]) !== undefined)) { |
||
44 | // Recursively go over the results |
||
45 | (function run(mod) { |
||
46 | // Go over each of the module's children and |
||
47 | // run over it |
||
48 | mod.children.forEach(function (child) { |
||
49 | run(child); |
||
50 | }); |
||
51 | |||
52 | // Call the specified callback providing the |
||
53 | // found module |
||
54 | callback(mod); |
||
55 | })(mod); |
||
56 | } |
||
57 | }; |
||
58 | |||
59 | // ------------------------------------------ |
||
60 | // CLASS: PLUGIN |
||
61 | // ------------------------------------------ |
||
62 | |||
63 | var TYPE_MODULES = 'modules'; |
||
64 | var TYPE_PHANTOMS = 'phantoms'; |
||
65 | var TYPE_CRON = 'cron'; |
||
66 | |||
67 | function Plugin(options) { |
||
68 | extend(false, this, options); |
||
69 | |||
70 | // Link configuration |
||
71 | this.config = Config[TYPE_MODULES][this.name]; |
||
72 | this.phantoms = Config[TYPE_PHANTOMS][this.name]; |
||
73 | this.cron = Config[TYPE_CRON][this.name]; |
||
74 | |||
75 | // Check has {plugin}.js |
||
76 | var script = SARAH.ConfigManager.PLUGIN+'/'+this.name+'/'+this.name+'.js'; |
||
77 | if (fs.existsSync(script)){ |
||
78 | this.script = script; |
||
79 | } |
||
80 | |||
81 | // Check has custom portlet.ejs |
||
82 | var template = SARAH.ConfigManager.PLUGIN+'/'+this.name+'/portlet.ejs'; |
||
83 | if (fs.existsSync(template)){ |
||
84 | this.template = template; |
||
85 | } else { |
||
86 | this.template = 'portlet.html'; |
||
87 | } |
||
88 | |||
89 | // Check has index.ejs |
||
90 | var index = SARAH.ConfigManager.PLUGIN+'/'+this.name+'/index.ejs'; |
||
91 | if (fs.existsSync(index)){ |
||
92 | this.index = index; |
||
93 | } |
||
94 | } |
||
95 | |||
96 | Plugin.prototype.isDisabled = function(){ |
||
97 | if (!this.script) return true; |
||
0 ignored issues
–
show
|
|||
98 | return this.config.disabled; |
||
99 | } |
||
100 | |||
101 | |||
102 | Plugin.prototype.getLocale = function(locale){ |
||
103 | var path = SARAH.ConfigManager.PLUGIN+'/'+this.name+'/locales/'+locale+'.js'; |
||
104 | if (!fs.existsSync(path)){ info('No locals',path); return; } |
||
105 | try { |
||
106 | var json = fs.readFileSync(path,'utf-8'); |
||
107 | info('Loading locales %s', path); |
||
108 | if (json) return JSON.parse(json); |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
109 | } |
||
110 | catch(ex){ warn("Can't parse %s locales in %s", this.name, locale); } |
||
111 | |||
112 | return false; |
||
113 | } |
||
114 | |||
115 | Plugin.prototype.getInstance = function(uncache){ |
||
116 | try { |
||
117 | // Dispose |
||
118 | if (Config.debug || uncache){ |
||
119 | if (this._script && this._script.dispose){ this._script.dispose(SARAH); } |
||
120 | require.uncache(this.script); |
||
121 | } |
||
122 | |||
123 | // Require |
||
124 | this._script = require(this.script); |
||
125 | |||
126 | // Initialise |
||
127 | if (!this._script.initialized){ |
||
128 | this._script.initialized = true; |
||
129 | if (this._script.init){ this._script.init(SARAH); } |
||
130 | } |
||
131 | |||
132 | // Last Modified |
||
133 | var modified = fs.statSync(this.script).mtime.getTime(); |
||
134 | if (!this._script.lastModified){ |
||
135 | this._script.lastModified = modified; |
||
136 | } |
||
137 | |||
138 | // Reload if new version |
||
139 | if (this._script.lastModified < modified){ |
||
140 | info('Reloading: ', this.name); |
||
141 | return this.getInstance(true); |
||
142 | } |
||
143 | |||
144 | return this._script; |
||
145 | } |
||
146 | catch (ex) { |
||
147 | warn('Error while loading plugin: %s', this.name, ex.message, ex.stack); |
||
148 | } |
||
149 | } |
||
150 | |||
151 | // ------------------------------------------ |
||
152 | // CACHE PLUGINS |
||
153 | // ------------------------------------------ |
||
154 | |||
155 | var cache = {}; |
||
156 | var getCache = function(){ return cache; } |
||
157 | |||
158 | var refresh = function(){ |
||
159 | |||
160 | cache = {}; |
||
161 | |||
162 | // Find config |
||
163 | var keys = Object.keys(Config[TYPE_MODULES]); |
||
164 | |||
165 | // Build a list of plugins |
||
166 | for(var i = 0 ; i < keys.length ; i++){ |
||
167 | var key = keys[i]; |
||
168 | cache[key] = new Plugin ({'name' : key }); |
||
169 | cache[key].getInstance(); |
||
170 | } |
||
171 | |||
172 | keys = Object.keys(Config[TYPE_PHANTOMS]); |
||
173 | for(var i = 0 ; i < keys.length ; i++){ |
||
174 | var key = keys[i]; |
||
175 | if (cache[key]) continue; |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
176 | cache[key] = new Plugin ({'name' : key }); |
||
177 | cache[key].getInstance(); |
||
178 | } |
||
179 | |||
180 | keys = Object.keys(Config[TYPE_CRON]); |
||
181 | for(var i = 0 ; i < keys.length ; i++){ |
||
182 | var key = keys[i]; |
||
183 | if (cache[key]) continue; |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
184 | cache[key] = new Plugin ({'name' : key }); |
||
185 | cache[key].getInstance(); |
||
186 | } |
||
187 | } |
||
188 | |||
189 | // ------------------------------------------ |
||
190 | // PLUGIN LOCALES |
||
191 | // ------------------------------------------ |
||
192 | |||
193 | var getLocales = function(locale){ |
||
194 | var prop = {} |
||
195 | var keys = Object.keys(cache); |
||
196 | for(var i = 0 ; i < keys.length ; i++){ |
||
197 | var key = keys[i]; |
||
198 | var json = cache[key].getLocale(locale); |
||
199 | if (json){ extend(true, prop, json); } |
||
200 | } |
||
201 | return prop; |
||
202 | } |
||
203 | |||
204 | // ------------------------------------------ |
||
205 | // PLUGIN LIST |
||
206 | // ------------------------------------------ |
||
207 | |||
208 | var sort = function(ids, xPos, yPos){ |
||
209 | for(var i = 0 ; i < ids.length ; i++){ |
||
210 | var tmp = cache[ids[i]]; |
||
211 | if (tmp){ |
||
212 | var cfg = tmp.config; |
||
213 | cfg.x = parseInt(xPos[i])+1; |
||
214 | cfg.y = parseInt(yPos[i])+1; |
||
215 | } |
||
216 | } |
||
217 | getList(true); |
||
218 | } |
||
219 | |||
220 | var getList = function(clean){ |
||
221 | |||
222 | if (clean){ refresh(); } |
||
223 | |||
224 | var keys = Object.keys(cache); |
||
225 | keys = keys.sort(function(k1, k2){ |
||
226 | var conf1 = cache[k1].config; |
||
227 | var conf2 = cache[k2].config; |
||
228 | |||
229 | if (!conf1.y) return 1; |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
230 | if (!conf2.y) return -1; |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
231 | |||
232 | if (conf1.y < conf2.y) return -1; |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
233 | if (conf1.y > conf2.y) return 1; |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
234 | return conf1.x < conf2.x ? -1 : 1; |
||
235 | }); |
||
236 | |||
237 | var list = []; |
||
238 | for(var i = 0 ; i < keys.length ; i++){ |
||
239 | var key = keys[i]; |
||
240 | var plugin = cache[key]; |
||
241 | |||
242 | // Skip disabled plugin |
||
243 | if (plugin.isDisabled()){ continue; } |
||
244 | |||
245 | list.push(plugin); |
||
246 | } |
||
247 | return list; |
||
248 | } |
||
249 | |||
250 | |||
251 | // ------------------------------------------ |
||
252 | // FIND / SEEK |
||
253 | // ------------------------------------------ |
||
254 | |||
255 | var find = function(name){ |
||
256 | return cache[name]; |
||
257 | } |
||
258 | |||
259 | var exists = function(name){ |
||
260 | var plugin = find(name); |
||
261 | return plugin ? true : false; |
||
262 | } |
||
263 | |||
264 | var remove = function(name, callback){ |
||
265 | var plugin = find(name); |
||
266 | if (!plugin){ return callback(); } |
||
267 | |||
268 | // Remove from filesystem |
||
269 | var path = SARAH.ConfigManager.PLUGIN+'/'+name; |
||
270 | info('Removing %s plugin...', path); |
||
271 | if (fs.existsSync(path)){ fs.removeSync(path); } |
||
272 | |||
273 | // Remove in memory |
||
274 | refresh(); |
||
275 | |||
276 | callback(); |
||
277 | } |
||
278 | |||
279 | // ------------------------------------------ |
||
280 | // EVENT |
||
281 | // ------------------------------------------ |
||
282 | |||
283 | var events = require('events'); |
||
284 | var ee = new events.EventEmitter(); |
||
285 | |||
286 | var listen = function(event, callback){ |
||
287 | ee.on(event, callback); |
||
288 | } |
||
289 | |||
290 | var trigger = function(event, data){ |
||
291 | ee.emit(event, data); |
||
292 | } |
||
293 | |||
294 | var socket = function(io){ |
||
295 | io.on('connection', function(socket){ |
||
296 | socket.on('disconnect', function(){ }); |
||
297 | |||
298 | for(var name in cache){ |
||
299 | plugin = cache[name].getInstance(); |
||
300 | if (plugin.socket) plugin.socket(io, socket); |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
301 | } |
||
302 | }); |
||
303 | } |
||
304 | |||
305 | // ------------------------------------------ |
||
306 | // ROUTER |
||
307 | // ------------------------------------------ |
||
308 | |||
309 | var Router = express.Router(); |
||
310 | |||
311 | Router.get('/plugin/help/:name', function(req, res, next) { |
||
312 | var name = req.params.name; |
||
313 | var plugin = find(name); |
||
314 | |||
315 | if (plugin && plugin.index) { |
||
316 | return res.render(plugin.index, {'title' : i18n('modal.plugin.help', name)}); |
||
317 | } |
||
318 | next(); |
||
319 | }); |
||
320 | |||
321 | Router.get('/plugin/github/:name', function(req, res, next) { |
||
322 | var name = req.params.name; |
||
323 | SARAH.Marketplace.getCommits(name, function(commits){ |
||
324 | res.render('plugin/github.ejs', { |
||
325 | 'title' : i18n('modal.plugin.github', name), |
||
326 | 'commits' : commits |
||
327 | }); |
||
328 | }); |
||
329 | }); |
||
330 | |||
331 | Router.get('/plugin/config/:name', function(req, res, next) { |
||
332 | var name = req.params.name; |
||
333 | return res.render('plugin/config.ejs', {'title' : i18n('modal.plugin.config', name) }); |
||
334 | }); |
||
335 | |||
336 | Router.post('/plugin/config/:name', function(req, res, next) { |
||
337 | var name = req.params.name; |
||
338 | var plugin = find(name); |
||
339 | var keys = Object.keys(req.body); |
||
340 | for(var i = 0 ; i < keys.length ; i++){ |
||
341 | var key = keys[i]; |
||
342 | var value = Helper.parse(req.body[key]); |
||
343 | var pfx = key.substring(0, key.indexOf('.')); |
||
344 | var prop = key.substring(key.indexOf('.')+1); |
||
345 | |||
346 | // skip internal parameter like ajax |
||
347 | if (!pfx) continue; |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
348 | |||
349 | info('[%s] %s.%s.%s = %s',key, pfx, name, prop, value); |
||
350 | Config[pfx][name][prop] = value; |
||
351 | } |
||
352 | SARAH.ConfigManager.save(); |
||
353 | |||
354 | var referer = req.headers.referer; |
||
355 | if (referer && referer.indexOf('/portal') < 0){ |
||
356 | return res.redirect(referer); |
||
357 | } else { |
||
358 | return res.render('plugin/config.ejs', {'title' : i18n('modal.plugin.config', name), 'message' : true }); |
||
359 | } |
||
360 | }); |
||
361 | |||
362 | Router.get('/plugin/edit/:name', function(req, res, next) { |
||
363 | var name = req.params.name; |
||
364 | var plugin = find(name); |
||
365 | return res.render('plugin/edit.ejs', {'title' : i18n('modal.plugin.edit', name)}); |
||
366 | }); |
||
367 | |||
368 | Router.all('/plugin/sort', function(req, res, next) { |
||
369 | sort(req.query.ids, req.query.xPos, req.query.yPos); |
||
370 | SARAH.ConfigManager.save(); |
||
371 | res.end(); |
||
372 | }); |
||
373 | |||
374 | Router.all('/plugin/:name/*', function(req, res, next) { |
||
375 | var name = req.params.name; |
||
376 | var plugin = find(name); |
||
377 | if (!plugin) return res.end(); |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
378 | |||
379 | var path = req.params[0]; |
||
380 | if (!path) return res.end(); |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
381 | |||
382 | try { |
||
383 | var fullpath = SARAH.ConfigManager.PLUGIN+'/'+name+'/'+path; |
||
384 | if (fs.existsSync(fullpath)){ |
||
385 | res.render(fullpath, { "plugin" : plugin, "title" : (req.query.title || req.body.title || "") }); |
||
386 | } else { |
||
387 | warn('path not found:', fullpath); |
||
388 | } |
||
389 | } |
||
390 | catch (ex){ |
||
391 | warn('Error' , fullpath, ex.stack); |
||
392 | } |
||
393 | }); |
||
394 | |||
395 | Router.all('/plugin/:name*', function(req, res, next) { |
||
396 | var plugin = find(req.params.name); |
||
397 | if (!plugin) return res.end(); |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
398 | |||
399 | var render = function(){ |
||
400 | res.render('portal/portlet.ejs', { "plugin" : plugin}); |
||
401 | } |
||
402 | |||
403 | var instance = plugin.getInstance(); |
||
404 | if (instance.ajax){ instance.ajax(req, res, render); } else { render(); } |
||
405 | }); |
||
406 | |||
407 | Router.all('/static/:name/*', function(req, res, next) { |
||
408 | var plugin = find(req.params.name); |
||
409 | if (!plugin) return res.end(); |
||
0 ignored issues
–
show
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later. Consider: if (a > 0)
b = 42;
If you or someone else later decides to put another statement in, only the first statement will be executed. if (a > 0)
console.log("a > 0");
b = 42;
In this case the statement if (a > 0) {
console.log("a > 0");
b = 42;
}
ensures that the proper code will be executed conditionally no matter how many statements are added or removed. ![]() |
|||
410 | |||
411 | var instance = plugin.getInstance(); |
||
412 | if (instance.route) { |
||
413 | instance.route(req, res, next); |
||
414 | } else { next(); } |
||
415 | }); |
||
416 | |||
417 | |||
418 | // ------------------------------------------ |
||
419 | // PUBLIC |
||
420 | // ------------------------------------------ |
||
421 | |||
422 | var PluginManager = { |
||
423 | 'init' : init, |
||
424 | 'getCache' : getCache, |
||
425 | 'getList' : getList, |
||
426 | 'getLocales' : getLocales, |
||
427 | |||
428 | 'find' : find, |
||
429 | 'exists' : exists, |
||
430 | 'remove' : remove, |
||
431 | |||
432 | 'socket' : socket, |
||
433 | 'trigger' : trigger, |
||
434 | 'listen' : listen, |
||
435 | |||
436 | 'Router' : Router |
||
437 | } |
||
438 | |||
439 | // Exports Manager |
||
440 | exports.init = PluginManager.init; |
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.
Consider:
If you or someone else later decides to put another statement in, only the first statement will be executed.
In this case the statement
b = 42
will always be executed, while the logging statement will be executed conditionally.ensures that the proper code will be executed conditionally no matter how many statements are added or removed.