Completed
Push — master ( eca1e1...16b172 )
by Nicolas
13:25
created

ModuleGenerator   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 475
Duplicated Lines 1.26 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 90.7%

Importance

Changes 0
Metric Value
dl 6
loc 475
ccs 117
cts 129
cp 0.907
rs 8.2769
c 0
b 0
f 0
wmc 41
lcom 1
cbo 5

30 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A setPlain() 0 6 1
A getName() 0 4 1
A getConfig() 0 4 1
A setConfig() 0 6 1
A getFilesystem() 0 4 1
A setFilesystem() 0 6 1
A getConsole() 0 4 1
A setConsole() 0 6 1
A getModule() 0 4 1
A setModule() 0 6 1
A getFolders() 0 4 1
A getFiles() 0 4 1
A setForce() 0 6 1
A generateFolders() 0 10 2
A generateGitKeep() 0 4 1
A generateFiles() 3 14 3
A generateResources() 0 19 1
B generate() 0 29 5
A getStubContents() 0 8 1
A getReplacements() 0 4 1
B getReplacement() 0 22 4
A generateModuleJsonFile() 3 12 2
A cleanModuleJsonFile() 0 14 1
A getLowerNameReplacement() 0 4 1
A getStudlyNameReplacement() 0 4 1
A getVendorReplacement() 0 4 1
A getModuleNamespaceReplacement() 0 4 1
A getAuthorNameReplacement() 0 4 1
A getAuthorEmailReplacement() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ModuleGenerator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ModuleGenerator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Nwidart\Modules\Generators;
4
5
use Illuminate\Config\Repository as Config;
6
use Illuminate\Console\Command as Console;
7
use Illuminate\Filesystem\Filesystem;
8
use Illuminate\Support\Str;
9
use Nwidart\Modules\Repository;
10
use Nwidart\Modules\Support\Stub;
11
12
class ModuleGenerator extends Generator
13
{
14
    /**
15
     * The module name will created.
16
     *
17
     * @var string
18
     */
19
    protected $name;
20
21
    /**
22
     * The laravel config instance.
23
     *
24
     * @var Config
25
     */
26
    protected $config;
27
28
    /**
29
     * The laravel filesystem instance.
30
     *
31
     * @var Filesystem
32
     */
33
    protected $filesystem;
34
35
    /**
36
     * The laravel console instance.
37
     *
38
     * @var Console
39
     */
40
    protected $console;
41
42
    /**
43
     * The pingpong module instance.
44
     *
45
     * @var Module
46
     */
47
    protected $module;
48
49
    /**
50
     * Force status.
51
     *
52
     * @var bool
53
     */
54
    protected $force = false;
55
56
    /**
57
     * Generate a plain module.
58
     *
59
     * @var bool
60
     */
61
    protected $plain = false;
62
63
    /**
64
     * The constructor.
65
     *
66
     * @param $name
67
     * @param Repository $module
68
     * @param Config     $config
69
     * @param Filesystem $filesystem
70
     * @param Console    $console
71
     */
72 56
    public function __construct(
73
        $name,
74
        Repository $module = null,
75
        Config $config = null,
76
        Filesystem $filesystem = null,
77
        Console $console = null
78
    ) {
79 56
        $this->name = $name;
80 56
        $this->config = $config;
81 56
        $this->filesystem = $filesystem;
82 56
        $this->console = $console;
83 56
        $this->module = $module;
0 ignored issues
show
Documentation Bug introduced by
It seems like $module can also be of type object<Nwidart\Modules\Repository>. However, the property $module is declared as type object<Nwidart\Modules\Generators\Module>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
84 56
    }
85
86
    /**
87
     * Set plain flag.
88
     *
89
     * @param bool $plain
90
     *
91
     * @return $this
92
     */
93 56
    public function setPlain($plain)
94
    {
95 56
        $this->plain = $plain;
96
97 56
        return $this;
98
    }
99
100
    /**
101
     * Get the name of module will created. By default in studly case.
102
     *
103
     * @return string
104
     */
105 56
    public function getName()
106
    {
107 56
        return Str::studly($this->name);
108
    }
109
110
    /**
111
     * Get the laravel config instance.
112
     *
113
     * @return Config
114
     */
115
    public function getConfig()
116
    {
117
        return $this->config;
118
    }
119
120
    /**
121
     * Set the laravel config instance.
122
     *
123
     * @param Config $config
124
     *
125
     * @return $this
126
     */
127 56
    public function setConfig($config)
128
    {
129 56
        $this->config = $config;
130
131 56
        return $this;
132
    }
133
134
    /**
135
     * Get the laravel filesystem instance.
136
     *
137
     * @return Filesystem
138
     */
139
    public function getFilesystem()
140
    {
141
        return $this->filesystem;
142
    }
143
144
    /**
145
     * Set the laravel filesystem instance.
146
     *
147
     * @param Filesystem $filesystem
148
     *
149
     * @return $this
150
     */
151 56
    public function setFilesystem($filesystem)
152
    {
153 56
        $this->filesystem = $filesystem;
154
155 56
        return $this;
156
    }
157
158
    /**
159
     * Get the laravel console instance.
160
     *
161
     * @return Console
162
     */
163
    public function getConsole()
164
    {
165
        return $this->console;
166
    }
167
168
    /**
169
     * Set the laravel console instance.
170
     *
171
     * @param Console $console
172
     *
173
     * @return $this
174
     */
175 56
    public function setConsole($console)
176
    {
177 56
        $this->console = $console;
178
179 56
        return $this;
180
    }
181
182
    /**
183
     * Get the pingpong module instance.
184
     *
185
     * @return Module
186
     */
187
    public function getModule()
188
    {
189
        return $this->module;
190
    }
191
192
    /**
193
     * Set the pingpong module instance.
194
     *
195
     * @param mixed $module
196
     *
197
     * @return $this
198
     */
199 56
    public function setModule($module)
200
    {
201 56
        $this->module = $module;
202
203 56
        return $this;
204
    }
205
206
    /**
207
     * Get the list of folders will created.
208
     *
209
     * @return array
210
     */
211 56
    public function getFolders()
212
    {
213 56
        return array_values($this->module->config('paths.generator'));
214
    }
215
216
    /**
217
     * Get the list of files will created.
218
     *
219
     * @return array
220
     */
221 50
    public function getFiles()
222
    {
223 50
        return $this->module->config('stubs.files');
224
    }
225
226
    /**
227
     * Set force status.
228
     *
229
     * @param bool|int $force
230
     *
231
     * @return $this
232
     */
233 56
    public function setForce($force)
234
    {
235 56
        $this->force = $force;
0 ignored issues
show
Documentation Bug introduced by
It seems like $force can also be of type integer. However, the property $force is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
236
237 56
        return $this;
238
    }
239
240
    /**
241
     * Generate the module.
242
     */
243 56
    public function generate()
244
    {
245 56
        $name = $this->getName();
246
247 56
        if ($this->module->has($name)) {
248 2
            if ($this->force) {
249 1
                $this->module->delete($name);
250
            } else {
251 1
                $this->console->error("Module [{$name}] already exist!");
252
253 1
                return;
254
            }
255
        }
256
257 56
        $this->generateFolders();
258
259 56
        $this->generateModuleJsonFile();
260
261 56
        if ($this->plain !== true) {
262 50
            $this->generateFiles();
263 50
            $this->generateResources();
264
        }
265
266 56
        if ($this->plain === true) {
267 6
            $this->cleanModuleJsonFile();
268
        }
269
270 56
        $this->console->info("Module [{$name}] created successfully.");
271 56
    }
272
273
    /**
274
     * Generate the folders.
275
     */
276 56
    public function generateFolders()
277
    {
278 56
        foreach ($this->getFolders() as $folder) {
279 56
            $path = $this->module->getModulePath($this->getName()) . '/' . $folder;
280
281 56
            $this->filesystem->makeDirectory($path, 0755, true);
282
283 56
            $this->generateGitKeep($path);
284
        }
285 56
    }
286
287
    /**
288
     * Generate git keep to the specified path.
289
     *
290
     * @param string $path
291
     */
292 56
    public function generateGitKeep($path)
293
    {
294 56
        $this->filesystem->put($path . '/.gitkeep', '');
295 56
    }
296
297
    /**
298
     * Generate the files.
299
     */
300 50
    public function generateFiles()
301
    {
302 50
        foreach ($this->getFiles() as $stub => $file) {
303 50
            $path = $this->module->getModulePath($this->getName()) . $file;
304
305 50 View Code Duplication
            if (!$this->filesystem->isDirectory($dir = dirname($path))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306 50
                $this->filesystem->makeDirectory($dir, 0775, true);
307
            }
308
309 50
            $this->filesystem->put($path, $this->getStubContents($stub));
310
311 50
            $this->console->info("Created : {$path}");
312
        }
313 50
    }
314
315
    /**
316
     * Generate some resources.
317
     */
318 50
    public function generateResources()
319
    {
320 50
        $this->console->call('module:make-seed', [
321 50
            'name' => $this->getName(),
322 50
            'module' => $this->getName(),
323
            '--master' => true,
324
        ]);
325
326 50
        $this->console->call('module:make-provider', [
327 50
            'name' => $this->getName() . 'ServiceProvider',
328 50
            'module' => $this->getName(),
329
            '--master' => true,
330
        ]);
331
332 50
        $this->console->call('module:make-controller', [
333 50
            'controller' => $this->getName() . 'Controller',
334 50
            'module' => $this->getName(),
335
        ]);
336 50
    }
337
338
    /**
339
     * Get the contents of the specified stub file by given stub name.
340
     *
341
     * @param $stub
342
     *
343
     * @return string
344
     */
345 56
    protected function getStubContents($stub)
346
    {
347 56
        return (new Stub(
348 56
            '/' . $stub . '.stub',
349 56
            $this->getReplacement($stub)
350
        )
351 56
        )->render();
352
    }
353
354
    /**
355
     * get the list for the replacements.
356
     */
357
    public function getReplacements()
358
    {
359
        return $this->module->config('stubs.replacements');
360
    }
361
362
    /**
363
     * Get array replacement for the specified stub.
364
     *
365
     * @param $stub
366
     *
367
     * @return array
368
     */
369 56
    protected function getReplacement($stub)
370
    {
371 56
        $replacements = $this->module->config('stubs.replacements');
372
373 56
        if (!isset($replacements[$stub])) {
374
            return [];
375
        }
376
377 56
        $keys = $replacements[$stub];
378
379 56
        $replaces = [];
380
381 56
        foreach ($keys as $key) {
382 56
            if (method_exists($this, $method = 'get' . ucfirst(studly_case(strtolower($key))) . 'Replacement')) {
383 56
                $replaces[$key] = call_user_func([$this, $method]);
384
            } else {
385 56
                $replaces[$key] = null;
386
            }
387
        }
388
389 56
        return $replaces;
390
    }
391
392
    /**
393
     * Generate the module.json file
394
     */
395 56
    private function generateModuleJsonFile()
396
    {
397 56
        $path = $this->module->getModulePath($this->getName()) . 'module.json';
398
399 56 View Code Duplication
        if (!$this->filesystem->isDirectory($dir = dirname($path))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400
            $this->filesystem->makeDirectory($dir, 0775, true);
401
        }
402
403 56
        $this->filesystem->put($path, $this->getStubContents('json'));
404
405 56
        $this->console->info("Created : {$path}");
406 56
    }
407
408
    /**
409
     * Remove the default service provider that was added in the module.json file
410
     * This is needed when a --plain module was created
411
     */
412 6
    private function cleanModuleJsonFile()
413
    {
414 6
        $path = $this->module->getModulePath($this->getName()) . 'module.json';
415
416 6
        $content = $this->filesystem->get($path);
417 6
        $namespace = $this->getModuleNamespaceReplacement();
418 6
        $studlyName = $this->getStudlyNameReplacement();
419
420 6
        $provider = '"' . $namespace . '\\\\' . $studlyName . '\\\\Providers\\\\' . $studlyName . 'ServiceProvider"';
421
422 6
        $content = str_replace($provider, '', $content);
423
424 6
        $this->filesystem->put($path, $content);
425 6
    }
426
427
    /**
428
     * Get the module name in lower case.
429
     *
430
     * @return string
431
     */
432 56
    protected function getLowerNameReplacement()
433
    {
434 56
        return strtolower($this->getName());
435
    }
436
437
    /**
438
     * Get the module name in studly case.
439
     *
440
     * @return string
441
     */
442 56
    protected function getStudlyNameReplacement()
443
    {
444 56
        return $this->getName();
445
    }
446
447
    /**
448
     * Get replacement for $VENDOR$.
449
     *
450
     * @return string
451
     */
452 50
    protected function getVendorReplacement()
453
    {
454 50
        return $this->module->config('composer.vendor');
455
    }
456
457
    /**
458
     * Get replacement for $MODULE_NAMESPACE$.
459
     *
460
     * @return string
461
     */
462 56
    protected function getModuleNamespaceReplacement()
463
    {
464 56
        return str_replace('\\', '\\\\', $this->module->config('namespace'));
465
    }
466
467
    /**
468
     * Get replacement for $AUTHOR_NAME$.
469
     *
470
     * @return string
471
     */
472 50
    protected function getAuthorNameReplacement()
473
    {
474 50
        return $this->module->config('composer.author.name');
475
    }
476
477
    /**
478
     * Get replacement for $AUTHOR_EMAIL$.
479
     *
480
     * @return string
481
     */
482 50
    protected function getAuthorEmailReplacement()
483
    {
484 50
        return $this->module->config('composer.author.email');
485
    }
486
}
487