Completed
Push — master ( 70a113...5db400 )
by Nicolas
02:54
created

ModuleGenerator   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 486
Duplicated Lines 1.23 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 91.04%

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 6
loc 486
ccs 122
cts 134
cp 0.9104
rs 8.3157
wmc 43
lcom 1
cbo 7

31 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
B generate() 0 29 5
A generateFolders() 0 16 3
A generateGitKeep() 0 4 1
A generateFiles() 3 14 3
A generateResources() 0 19 1
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
A getRoutesLocationReplacement() 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\Config\GenerateConfigReader;
11
use Nwidart\Modules\Support\Stub;
12
13
class ModuleGenerator extends Generator
14
{
15
    /**
16
     * The module name will created.
17
     *
18
     * @var string
19
     */
20
    protected $name;
21
22
    /**
23
     * The laravel config instance.
24
     *
25
     * @var Config
26
     */
27
    protected $config;
28
29
    /**
30
     * The laravel filesystem instance.
31
     *
32
     * @var Filesystem
33
     */
34
    protected $filesystem;
35
36
    /**
37
     * The laravel console instance.
38
     *
39
     * @var Console
40
     */
41
    protected $console;
42
43
    /**
44
     * The pingpong module instance.
45
     *
46
     * @var Module
47
     */
48
    protected $module;
49
50
    /**
51
     * Force status.
52
     *
53
     * @var bool
54
     */
55
    protected $force = false;
56
57
    /**
58
     * Generate a plain module.
59
     *
60
     * @var bool
61
     */
62
    protected $plain = false;
63
64
    /**
65
     * The constructor.
66
     *
67
     * @param $name
68
     * @param Repository $module
69
     * @param Config     $config
70
     * @param Filesystem $filesystem
71
     * @param Console    $console
72
     */
73 75
    public function __construct(
74
        $name,
75
        Repository $module = null,
76
        Config $config = null,
77
        Filesystem $filesystem = null,
78
        Console $console = null
79
    ) {
80 75
        $this->name = $name;
81 75
        $this->config = $config;
82 75
        $this->filesystem = $filesystem;
83 75
        $this->console = $console;
84 75
        $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...
85 75
    }
86
87
    /**
88
     * Set plain flag.
89
     *
90
     * @param bool $plain
91
     *
92
     * @return $this
93
     */
94 75
    public function setPlain($plain)
95
    {
96 75
        $this->plain = $plain;
97
98 75
        return $this;
99
    }
100
101
    /**
102
     * Get the name of module will created. By default in studly case.
103
     *
104
     * @return string
105
     */
106 75
    public function getName()
107
    {
108 75
        return Str::studly($this->name);
109
    }
110
111
    /**
112
     * Get the laravel config instance.
113
     *
114
     * @return Config
115
     */
116
    public function getConfig()
117
    {
118
        return $this->config;
119
    }
120
121
    /**
122
     * Set the laravel config instance.
123
     *
124
     * @param Config $config
125
     *
126
     * @return $this
127
     */
128 75
    public function setConfig($config)
129
    {
130 75
        $this->config = $config;
131
132 75
        return $this;
133
    }
134
135
    /**
136
     * Get the laravel filesystem instance.
137
     *
138
     * @return Filesystem
139
     */
140
    public function getFilesystem()
141
    {
142
        return $this->filesystem;
143
    }
144
145
    /**
146
     * Set the laravel filesystem instance.
147
     *
148
     * @param Filesystem $filesystem
149
     *
150
     * @return $this
151
     */
152 75
    public function setFilesystem($filesystem)
153
    {
154 75
        $this->filesystem = $filesystem;
155
156 75
        return $this;
157
    }
158
159
    /**
160
     * Get the laravel console instance.
161
     *
162
     * @return Console
163
     */
164
    public function getConsole()
165
    {
166
        return $this->console;
167
    }
168
169
    /**
170
     * Set the laravel console instance.
171
     *
172
     * @param Console $console
173
     *
174
     * @return $this
175
     */
176 75
    public function setConsole($console)
177
    {
178 75
        $this->console = $console;
179
180 75
        return $this;
181
    }
182
183
    /**
184
     * Get the pingpong module instance.
185
     *
186
     * @return Module
187
     */
188
    public function getModule()
189
    {
190
        return $this->module;
191
    }
192
193
    /**
194
     * Set the pingpong module instance.
195
     *
196
     * @param mixed $module
197
     *
198
     * @return $this
199
     */
200 75
    public function setModule($module)
201
    {
202 75
        $this->module = $module;
203
204 75
        return $this;
205
    }
206
207
    /**
208
     * Get the list of folders will created.
209
     *
210
     * @return array
211
     */
212 75
    public function getFolders()
213
    {
214 75
        return $this->module->config('paths.generator');
215
    }
216
217
    /**
218
     * Get the list of files will created.
219
     *
220
     * @return array
221
     */
222 68
    public function getFiles()
223
    {
224 68
        return $this->module->config('stubs.files');
225
    }
226
227
    /**
228
     * Set force status.
229
     *
230
     * @param bool|int $force
231
     *
232
     * @return $this
233
     */
234 75
    public function setForce($force)
235
    {
236 75
        $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...
237
238 75
        return $this;
239
    }
240
241
    /**
242
     * Generate the module.
243
     */
244 75
    public function generate()
245
    {
246 75
        $name = $this->getName();
247
248 75
        if ($this->module->has($name)) {
249 2
            if ($this->force) {
250 1
                $this->module->delete($name);
251
            } else {
252 1
                $this->console->error("Module [{$name}] already exist!");
253
254 1
                return;
255
            }
256
        }
257
258 75
        $this->generateFolders();
259
260 75
        $this->generateModuleJsonFile();
261
262 75
        if ($this->plain !== true) {
263 68
            $this->generateFiles();
264 68
            $this->generateResources();
265
        }
266
267 75
        if ($this->plain === true) {
268 7
            $this->cleanModuleJsonFile();
269
        }
270
271 75
        $this->console->info("Module [{$name}] created successfully.");
272 75
    }
273
274
    /**
275
     * Generate the folders.
276
     */
277 75
    public function generateFolders()
278
    {
279 75
        foreach ($this->getFolders() as $key => $folder) {
280 75
            $folder = GenerateConfigReader::read($key);
281
282 75
            if ($folder->generate() === false) {
283 3
                continue;
284
            }
285
286 75
            $path = $this->module->getModulePath($this->getName()) . '/' . $folder->getPath();
287
288 75
            $this->filesystem->makeDirectory($path, 0755, true);
289
290 75
            $this->generateGitKeep($path);
291
        }
292 75
    }
293
294
    /**
295
     * Generate git keep to the specified path.
296
     *
297
     * @param string $path
298
     */
299 75
    public function generateGitKeep($path)
300
    {
301 75
        $this->filesystem->put($path . '/.gitkeep', '');
302 75
    }
303
304
    /**
305
     * Generate the files.
306
     */
307 68
    public function generateFiles()
308
    {
309 68
        foreach ($this->getFiles() as $stub => $file) {
310 68
            $path = $this->module->getModulePath($this->getName()) . $file;
311
312 68 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...
313 68
                $this->filesystem->makeDirectory($dir, 0775, true);
314
            }
315
316 68
            $this->filesystem->put($path, $this->getStubContents($stub));
317
318 68
            $this->console->info("Created : {$path}");
319
        }
320 68
    }
321
322
    /**
323
     * Generate some resources.
324
     */
325 68
    public function generateResources()
326
    {
327 68
        $this->console->call('module:make-seed', [
328 68
            'name' => $this->getName(),
329 68
            'module' => $this->getName(),
330
            '--master' => true,
331
        ]);
332
333 68
        $this->console->call('module:make-provider', [
334 68
            'name' => $this->getName() . 'ServiceProvider',
335 68
            'module' => $this->getName(),
336
            '--master' => true,
337
        ]);
338
339 68
        $this->console->call('module:make-controller', [
340 68
            'controller' => $this->getName() . 'Controller',
341 68
            'module' => $this->getName(),
342
        ]);
343 68
    }
344
345
    /**
346
     * Get the contents of the specified stub file by given stub name.
347
     *
348
     * @param $stub
349
     *
350
     * @return string
351
     */
352 75
    protected function getStubContents($stub)
353
    {
354 75
        return (new Stub(
355 75
            '/' . $stub . '.stub',
356 75
            $this->getReplacement($stub)
357
        )
358 75
        )->render();
359
    }
360
361
    /**
362
     * get the list for the replacements.
363
     */
364
    public function getReplacements()
365
    {
366
        return $this->module->config('stubs.replacements');
367
    }
368
369
    /**
370
     * Get array replacement for the specified stub.
371
     *
372
     * @param $stub
373
     *
374
     * @return array
375
     */
376 75
    protected function getReplacement($stub)
377
    {
378 75
        $replacements = $this->module->config('stubs.replacements');
379
380 75
        if (!isset($replacements[$stub])) {
381
            return [];
382
        }
383
384 75
        $keys = $replacements[$stub];
385
386 75
        $replaces = [];
387
388 75
        foreach ($keys as $key) {
389 75
            if (method_exists($this, $method = 'get' . ucfirst(studly_case(strtolower($key))) . 'Replacement')) {
390 75
                $replaces[$key] = $this->$method();
391
            } else {
392 75
                $replaces[$key] = null;
393
            }
394
        }
395
396 75
        return $replaces;
397
    }
398
399
    /**
400
     * Generate the module.json file
401
     */
402 75
    private function generateModuleJsonFile()
403
    {
404 75
        $path = $this->module->getModulePath($this->getName()) . 'module.json';
405
406 75 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...
407
            $this->filesystem->makeDirectory($dir, 0775, true);
408
        }
409
410 75
        $this->filesystem->put($path, $this->getStubContents('json'));
411
412 75
        $this->console->info("Created : {$path}");
413 75
    }
414
415
    /**
416
     * Remove the default service provider that was added in the module.json file
417
     * This is needed when a --plain module was created
418
     */
419 7
    private function cleanModuleJsonFile()
420
    {
421 7
        $path = $this->module->getModulePath($this->getName()) . 'module.json';
422
423 7
        $content = $this->filesystem->get($path);
424 7
        $namespace = $this->getModuleNamespaceReplacement();
425 7
        $studlyName = $this->getStudlyNameReplacement();
426
427 7
        $provider = '"' . $namespace . '\\\\' . $studlyName . '\\\\Providers\\\\' . $studlyName . 'ServiceProvider"';
428
429 7
        $content = str_replace($provider, '', $content);
430
431 7
        $this->filesystem->put($path, $content);
432 7
    }
433
434
    /**
435
     * Get the module name in lower case.
436
     *
437
     * @return string
438
     */
439 75
    protected function getLowerNameReplacement()
440
    {
441 75
        return strtolower($this->getName());
442
    }
443
444
    /**
445
     * Get the module name in studly case.
446
     *
447
     * @return string
448
     */
449 75
    protected function getStudlyNameReplacement()
450
    {
451 75
        return $this->getName();
452
    }
453
454
    /**
455
     * Get replacement for $VENDOR$.
456
     *
457
     * @return string
458
     */
459 68
    protected function getVendorReplacement()
460
    {
461 68
        return $this->module->config('composer.vendor');
462
    }
463
464
    /**
465
     * Get replacement for $MODULE_NAMESPACE$.
466
     *
467
     * @return string
468
     */
469 75
    protected function getModuleNamespaceReplacement()
470
    {
471 75
        return str_replace('\\', '\\\\', $this->module->config('namespace'));
472
    }
473
474
    /**
475
     * Get replacement for $AUTHOR_NAME$.
476
     *
477
     * @return string
478
     */
479 68
    protected function getAuthorNameReplacement()
480
    {
481 68
        return $this->module->config('composer.author.name');
482
    }
483
484
    /**
485
     * Get replacement for $AUTHOR_EMAIL$.
486
     *
487
     * @return string
488
     */
489 68
    protected function getAuthorEmailReplacement()
490
    {
491 68
        return $this->module->config('composer.author.email');
492
    }
493
494 68
    protected function getRoutesLocationReplacement()
495
    {
496 68
        return '/' . $this->module->config('stubs.files.routes');
497
    }
498
}
499