1<?php 2 3/* 4 * This file is part of the Assetic package, an OpenSky project. 5 * 6 * (c) 2010-2014 OpenSky Project Inc 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Assetic\Filter; 13 14use Assetic\Asset\AssetInterface; 15use Assetic\Exception\FilterException; 16use Assetic\Factory\AssetFactory; 17use Assetic\Util\FilesystemUtils; 18 19/** 20 * Runs assets through Sprockets. 21 * 22 * Requires Sprockets 1.0.x. 23 * 24 * @link http://getsprockets.org/ 25 * @link http://github.com/sstephenson/sprockets/tree/1.0.x 26 * 27 * @author Kris Wallsmith <kris.wallsmith@gmail.com> 28 */ 29class SprocketsFilter extends BaseProcessFilter implements DependencyExtractorInterface 30{ 31 private $sprocketsLib; 32 private $rubyBin; 33 private $includeDirs; 34 private $assetRoot; 35 36 /** 37 * Constructor. 38 * 39 * @param string $sprocketsLib Path to the Sprockets lib/ directory 40 * @param string $rubyBin Path to the ruby binary 41 */ 42 public function __construct($sprocketsLib = null, $rubyBin = '/usr/bin/ruby') 43 { 44 $this->sprocketsLib = $sprocketsLib; 45 $this->rubyBin = $rubyBin; 46 $this->includeDirs = array(); 47 } 48 49 public function addIncludeDir($directory) 50 { 51 $this->includeDirs[] = $directory; 52 } 53 54 public function setAssetRoot($assetRoot) 55 { 56 $this->assetRoot = $assetRoot; 57 } 58 59 /** 60 * Hack around a bit, get the job done. 61 */ 62 public function filterLoad(AssetInterface $asset) 63 { 64 static $format = <<<'EOF' 65#!/usr/bin/env ruby 66 67require %s 68%s 69options = { :load_path => [], 70 :source_files => [%s], 71 :expand_paths => false } 72 73%ssecretary = Sprockets::Secretary.new(options) 74secretary.install_assets if options[:asset_root] 75print secretary.concatenation 76 77EOF; 78 79 $more = ''; 80 81 foreach ($this->includeDirs as $directory) { 82 $more .= 'options[:load_path] << '.var_export($directory, true)."\n"; 83 } 84 85 if (null !== $this->assetRoot) { 86 $more .= 'options[:asset_root] = '.var_export($this->assetRoot, true)."\n"; 87 } 88 89 if ($more) { 90 $more .= "\n"; 91 } 92 93 $tmpAsset = FilesystemUtils::createTemporaryFile('sprockets_asset'); 94 file_put_contents($tmpAsset, $asset->getContent()); 95 96 $input = FilesystemUtils::createTemporaryFile('sprockets_in'); 97 file_put_contents($input, sprintf($format, 98 $this->sprocketsLib 99 ? sprintf('File.join(%s, \'sprockets\')', var_export($this->sprocketsLib, true)) 100 : '\'sprockets\'', 101 $this->getHack($asset), 102 var_export($tmpAsset, true), 103 $more 104 )); 105 106 $pb = $this->createProcessBuilder(array( 107 $this->rubyBin, 108 $input, 109 )); 110 111 $proc = $pb->getProcess(); 112 $code = $proc->run(); 113 unlink($tmpAsset); 114 unlink($input); 115 116 if (0 !== $code) { 117 throw FilterException::fromProcess($proc)->setInput($asset->getContent()); 118 } 119 120 $asset->setContent($proc->getOutput()); 121 } 122 123 public function filterDump(AssetInterface $asset) 124 { 125 } 126 127 public function getChildren(AssetFactory $factory, $content, $loadPath = null) 128 { 129 // todo 130 return array(); 131 } 132 133 private function getHack(AssetInterface $asset) 134 { 135 static $format = <<<'EOF' 136 137module Sprockets 138 class Preprocessor 139 protected 140 def pathname_for_relative_require_from(source_line) 141 Sprockets::Pathname.new(@environment, File.join(%s, location_from(source_line))) 142 end 143 end 144end 145 146EOF; 147 148 if ($dir = $asset->getSourceDirectory()) { 149 return sprintf($format, var_export($dir, true)); 150 } 151 } 152} 153