Search code examples
ruby-on-railsgruntjsgulpsidekiq

Rails minify/optimize files on the fly using Grunt or Gulp


My Rails 4 application allow it's users to import some Adobe Edge animation files (advertisement). While it works, Adobe Edge is loading a bunch of files that could be minified and optimized (images) using a tool like Grunt or Gulp.

I'm using CarrierWave to upload the files and then I extract them if they are inside an archive :

class FileUploader < CarrierWave::Uploader::Base
  after :store, :uncompress_and_update_reference

  def uncompress_and_update_reference(_file)
    return unless archive_is_supported?(model.ext)
    extractor = Uploader::Archive.const_get("Extractor#{model.ext.upcase}").new
    Uploader::Archive::PubArchive.new(extractor, model, full_path).extract_and_save
  end
end

I was thinking that I could use Grunt or Gulp in a background job (using Sidekiq for example) to work on those extracted files as I need. The problem is how can I do it?

From what I've read, in order to run grunt or gulp, they both need to have a Gruntfile.js and Gulpfile.js respectively, which makes things tricky as I need to run it dynamically inside the extracted folder.

Anyone?


Solution

  • Ok, I finally created a Node CLI to handle that case. Since I needed to be able to optimize files dynamically inside a folder, having a command line utility is somehow the best scenario :

    cli.js

    #!/usr/bin/env node
    
    'use strict';
    
    var meow      = require('meow');
    var fs        = require('fs');
    var optimizer = require('./');
    
    var cli = meow({
      help: [
        'Usage : pub-optimizer <path_to_optimize> <path_to_optimize>',
        ''
      ].join('\n')
    });
    
    var paths = cli.input
    
    paths.forEach(function (path) {
      if (fs.existsSync(path) {
        optimizer.minify(path);
      } else {
        console.log(path + ' is not a valid directory.')
      }
    });
    

    index.js

    'use strict';
    
    var gulp     = require('gulp');
    var imagemin = require('gulp-imagemin');
    var pngquant = require('imagemin-pngquant');
    
    exports.minify = function (folderPath) {
    
      // Remove trailing slash
      folderPath = folderPath.replace(/\/+$/, "");
    
      // Optimize images
      gulp.task('default', function () {
        return gulp
          .src(folderPath + '/**/*.{png,jpg,jpeg,gif,webp,svg}')
          .pipe(imagemin({
            progressive: true,
            use: [pngquant()]
          }))
          .pipe(gulp.dest(folderPath));
      });
    
      gulp.start();
    };
    

    Then all I need to do was to invoke that CLI from my Rails app :

    uploaders/file_uploader.rb

    class FileUploader < CarrierWave::Uploader::Base
      after :store, :uncompress_and_update_reference
    
      def uncompress_and_update_reference(_file)
        return unless archive_is_supported?(model.ext)
        extractor = Uploader::Archive.const_get("Extractor#{model.ext.upcase}").new
        Uploader::Archive::PubArchive.new(extractor, model, full_path).extract_and_save
    
        PubOptimizerWorker.perform_async(fullpath)
      end
    end
    

    workers/pub_optimizer_worker.rb

    class PubApprovalRequestWorker
      include Sidekiq::Worker
      sidekiq_options queue: :pub
    
      def perform(path)
        system("pub-optimizer #{path}")
      end
    end
    

    Hope this will maybe help someone.