Completed
Pull Request — master (#29)
by Paulo Rodrigues
06:00
created

SetupCommand::interact()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 50
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 50
c 0
b 0
f 0
rs 9.3333
ccs 28
cts 28
cp 1
cc 1
eloc 32
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Rj\FrontendBundle\Command;
4
5
use Rj\FrontendBundle\Command\Options\SimpleOptionHelper;
6
use Rj\FrontendBundle\Command\Options\ChoiceOptionHelper;
7
use Symfony\Component\Console\Command\Command;
8
use Symfony\Component\Console\Input\ArrayInput;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Symfony\Component\Templating\PhpEngine;
13
use Symfony\Component\Templating\TemplateNameParser;
14
use Symfony\Component\Templating\Loader\FilesystemLoader;
15
16
class SetupCommand extends Command
17
{
18
    private $templating;
19
    private $rootDir = null;
20
21 15
    public function __construct($name = null)
22
    {
23 15
        parent::__construct($name);
24
25 15
        $this->templating = new PhpEngine(
26 15
            new TemplateNameParser(),
27 15
            new FilesystemLoader(array(__DIR__.'/../Resources/blueprints/%name%'))
28
        );
29 15
    }
30
31 15
    public function setRootDir($path)
32
    {
33 15
        $this->rootDir = $path;
34 15
    }
35
36 15
    protected function configure()
37
    {
38
        $this
39 15
            ->setName('rj_frontend:setup')
40 15
            ->setDescription('Generate the configuration for the asset pipeline')
41 15
            ->addOption(
42 15
                'dry-run',
43 15
                null,
44 15
                InputOption::VALUE_NONE,
45 15
                'Output which commands would have been run instead of running them'
46
            )
47 15
            ->addOption(
48 15
                'force',
49 15
                null,
50 15
                InputOption::VALUE_NONE,
51 15
                'Force execution'
52
            )
53 15
            ->addOption(
54 15
                'src-dir',
55 15
                null,
56 15
                InputOption::VALUE_REQUIRED,
57 15
                'Path to the directory containing the source assets [e.g. '.$this->getDefaultOption('src-dir').']'
58
            )
59 15
            ->addOption(
60 15
                'dest-dir',
61 15
                null,
62 15
                InputOption::VALUE_REQUIRED,
63 15
                'Path to the directory containing the compiled assets [e.g. '.$this->getDefaultOption('dest-dir').']'
64
            )
65 15
            ->addOption(
66 15
                'pipeline',
67 15
                null,
68 15
                InputOption::VALUE_REQUIRED,
69 15
                'Asset pipeline to use [only gulp is available at the moment]'
70
            )
71 15
            ->addOption(
72 15
                'csspre',
73 15
                null,
74 15
                InputOption::VALUE_REQUIRED,
75 15
                'CSS preprocessor to use [sass, less or none]'
76
            )
77 15
            ->addOption(
78 15
                'coffee',
79 15
                null,
80 15
                InputOption::VALUE_REQUIRED,
81 15
                'Use the CoffeeScript compiler [true or false]'
82
            )
83
        ;
84 15
    }
85
86 2
    protected function interact(InputInterface $input, OutputInterface $output)
87
    {
88 2
        $simpleOptionHelper = new SimpleOptionHelper($this, $input, $output);
89 2
        $choiceOptionHelper = new ChoiceOptionHelper($this, $input, $output);
90
91
        $simpleOptionHelper
92 2
            ->setDefaultValue($this->getDefaultOption('src-dir'))
93 2
            ->setOption(
94 2
                'src-dir',
95 2
                'Path to the directory containing the source assets [default is '.$this->getDefaultOption('src-dir').']'
96
            )
97
        ;
98
99
        $simpleOptionHelper
100 2
            ->setDefaultValue($this->getDefaultOption('dest-dir'))
101 2
            ->setOption(
102 2
                'dest-dir',
103 2
                'Path to the directory containing the compiled assets [default is '.$this->getDefaultOption('dest-dir').']'
104
            )
105
        ;
106
107
        $choiceOptionHelper
108 2
            ->setAllowedValues(array('gulp'))
109 2
            ->setErrorMessage('%s is not a supported asset pipeline')
110 2
            ->setOption(
111 2
                'pipeline',
112 2
                'Asset pipeline to use [only gulp is available at the moment]'
113
            )
114
        ;
115
116
        $choiceOptionHelper
117 2
            ->setAllowedValues(array('sass', 'less', 'none'))
118 2
            ->setErrorMessage('%s is not a supported CSS preprocessor')
119 2
            ->setOption(
120 2
                'csspre',
121 2
                'CSS preprocessor to use [default is '.$this->getDefaultOption('csspre').']'
122
            )
123
        ;
124
125
        $choiceOptionHelper
126 2
            ->setAllowedValues(array('false', 'true'))
127 2
            ->setErrorMessage('%s is not a supported value for --coffee. Use either true or false')
128 2
            ->setOption(
129 2
                'coffee',
130 2
                'Whether to use the CoffeeScript compiler [default is '.$this->getDefaultOption('coffee').']'
131
            )
132
        ;
133
134 2
        $output->writeln('');
135 2
    }
136
137 15
    protected function execute(InputInterface $input, OutputInterface $output)
138
    {
139 15
        $this->processOptions($input);
140
141 15
        $output->writeln('<info>Selected options are:</info>');
142 15
        $output->writeln('src-dir:  '.$input->getOption('src-dir'));
143 15
        $output->writeln('dest-dir: '.$input->getOption('dest-dir'));
144 15
        $output->writeln('pipeline: '.$input->getOption('pipeline'));
145 15
        $output->writeln('csspre:   '.$input->getOption('csspre'));
146 15
        $output->writeln('coffee:   '.($input->getOption('coffee') ? 'true' : 'false'));
147
148 15
        if (!preg_match('|web/.+|', $input->getOption('dest-dir'))) {
149 3
            throw new \InvalidArgumentException("'dest-dir' must be a directory under web/");
150
        }
151
152 12
        $output->writeln('');
153 12
        $this->createSourceTree($input, $output);
154 12
        $this->createBuildFile($input, $output);
155 12
        $this->createPackageJson($input, $output);
156 12
        $this->createBowerJson($input, $output);
157
158 12
        $output->writeln('');
159 12
        $this->runInstallCommand($input, $output);
160 12
    }
161
162 12
    private function runInstallCommand($input, $output)
163
    {
164 12
        if ($input->getOption('dry-run')) {
165 6
            return $output->writeln('<info>Would have installed npm and bower dependencies</info>');
166
        }
167
168 6
        $this->getApplication()->find('rj_frontend:install')
169 6
            ->run(new ArrayInput(array('command' => 'rj_frontend:install')), $output);
170 6
    }
171
172 12
    private function createSourceTree($input, $output)
173
    {
174 12
        $blueprints = __DIR__.'/../Resources/blueprints';
175 12
        $dryRun = $input->getOption('dry-run');
176 12
        $base = $input->getOption('src-dir');
177
178 12
        $output->writeln($dryRun
179 6
            ? '<info>Would have created directory tree for source assets:</info>'
180 12
            : '<info>Creating directory tree for source assets:</info>'
181
        );
182
183 12
        $blueprintDir = "$blueprints/images";
184 12
        $this->createDirFromBlueprint($input, $output, $blueprintDir, "$base/images");
185
186 12
        $blueprintDir = "$blueprints/stylesheets/".$input->getOption('csspre');
187 12
        $this->createDirFromBlueprint($input, $output, $blueprintDir, "$base/stylesheets");
188
189 12
        $blueprintDir = "$blueprints/scripts/";
190 12
        $blueprintDir .= $input->getOption('coffee') ? 'coffee' : 'js';
191 12
        $this->createDirFromBlueprint($input, $output, $blueprintDir, "$base/scripts");
192
193 12
        $output->writeln('');
194 12
    }
195
196 12 View Code Duplication
    private function createBuildFile($input, $output)
197
    {
198
        $files = array(
199 12
            'gulp' => 'gulp/gulpfile.js',
200
        );
201
202 12
        $this->createFileFromTemplate($input, $output, 'pipelines/'.$files[$input->getOption('pipeline')]);
203 12
    }
204
205 12 View Code Duplication
    private function createPackageJson($input, $output)
206
    {
207
        $files = array(
208 12
            'gulp' => 'gulp/package.json',
209
        );
210
211 12
        $this->createFileFromTemplate($input, $output, 'pipelines/'.$files[$input->getOption('pipeline')]);
212 12
    }
213
214 12
    private function createBowerJson($input, $output)
215
    {
216 12
        $this->createFileFromTemplate($input, $output, 'bower.json');
217 12
    }
218
219 12
    private function createDirFromBlueprint($input, $output, $blueprintDir, $targetDir)
220
    {
221 12
        $dryRun = $input->getOption('dry-run');
222
223 12
        if (!$dryRun && !file_exists($targetDir)) {
224 6
            mkdir($targetDir, 0777, true);
225
        }
226
227 12
        foreach (preg_grep('/^\.?\w+/', scandir($blueprintDir)) as $entry) {
228 12
            $target = $entry;
229
230 12
            $isPhpTemplate = substr($entry, strrpos($entry, '.')) === '.php';
231 12
            if ($isPhpTemplate) {
232 12
                $entry = str_replace('.php', '', $entry);
233 12
                $target = str_replace('.php', '', $target);
234
            }
235
236 12
            $entry = $blueprintDir.'/'.$entry;
237 12
            $target = $targetDir.'/'.$target;
238
239 12
            if (!$dryRun) {
240 6
                if ($isPhpTemplate) {
241 6
                    $this->renderTemplate($input, $output, $entry, $target);
242
                } else {
243 6
                    if (file_exists($target) && !$input->getOption('force')) {
244
                        $output->writeln(
245
                            "<error>$target already exists. Run this command with --force to overwrite</error>
246
                        ");
247
248
                        continue;
249
                    }
250
251 6
                    copy($entry, $target);
252
                }
253
            }
254
255 12
            $output->writeln($target);
256
        }
257 12
    }
258
259 12
    private function createFileFromTemplate($input, $output, $file)
260
    {
261 12
        $dryRun = $input->getOption('dry-run');
262
263 12
        $targetFile = basename($file);
264 12
        if (!empty($this->rootDir)) {
265 12
            $targetFile = $this->rootDir.'/'.$targetFile;
266
        }
267
268 12
        $output->writeln($dryRun
269 6
            ? "<info>Would have created file $targetFile</info>"
270 12
            : "<info>Creating file $targetFile</info>"
271
        );
272
273 12
        if ($dryRun) {
274 6
            return;
275
        }
276
277 6
        $this->renderTemplate($input, $output, $file, $targetFile);
278 6
    }
279
280 6
    private function renderTemplate($input, $output, $file, $target)
281
    {
282 6
        if (file_exists($target) && !$input->getOption('force')) {
283 1
            return $output->writeln(
284 1
                "<error>$target already exists. Run this command with --force to overwrite</error>
285
            ");
286
        }
287
288 6
        switch ($input->getOption('csspre')) {
289 6
            case 'sass':
290 5
                $stylesheetExtension = 'scss';
291 5
                break;
292 1
            case 'less':
293 1
                $stylesheetExtension = 'less';
294 1
                break;
295
            default:
296
                $stylesheetExtension = 'css';
297
                break;
298
        }
299
300 6
        file_put_contents($target, $this->templating->render("$file.php", array(
301 6
            'projectName'         => basename(getcwd()),
302 6
            'srcDir'              => $input->getOption('src-dir'),
303 6
            'destDir'             => $input->getOption('dest-dir'),
304 6
            'prefix'              => str_replace('web/', '', $input->getOption('dest-dir')),
305 6
            'coffee'              => $input->getOption('coffee'),
306 6
            'cssPre'              => $input->getOption('csspre'),
307 6
            'stylesheetExtension' => $stylesheetExtension,
308
        )));
309 6
    }
310
311 15
    private function processOptions($input)
312
    {
313 15
        foreach ($input->getOptions() as $name => $value) {
314 15
            if (!$input->isInteractive() && $value === null) {
315 13
                $value = $this->getDefaultOption($name);
316
            }
317
318 15
            if ($value === 'true') {
319 2
                $value = true;
320 15
            } elseif ($value === 'false') {
321 12
                $value = false;
322
            }
323
324 15
            $input->setOption($name, $value);
325
        }
326 15
    }
327
328 15
    private function getDefaultOption($name)
329
    {
330
        $defaults = array(
331 15
            'src-dir'  => empty($this->rootDir) ? 'app/Resources' : $this->rootDir.'/app/Resources',
332 15
            'dest-dir' => empty($this->rootDir) ? 'web/assets' : $this->rootDir.'/web/assets',
333 15
            'pipeline' => 'gulp',
334 15
            'csspre'   => 'sass',
335 15
            'coffee'   => 'false',
336
        );
337
338 15
        return $defaults[$name];
339
    }
340
}
341