Putting Sencha Architect 2 into Rails 3.1's asset pipeline

Suraj N. Kurapati

  1. Problem
    1. Solution
      1. app/assets/javascripts/gui.rake


      After months of building ExtJS applications by hand, I decided to try a simpler, Visual Basic styled approach by using Sencha Architect 2, wherein I would design the Graphical User Interface (GUI) by drag-and-drop and then add interactivity through appropriately-placed JavaScript snippets.

      In this article, I’ll show you how I put my ExtJS application (generated by Sencha Architect 2) into my Rails 3.1 application’s asset pipeline. This procedure should work equally well for handwritten ExtJS applications too!


      First, I created a gui/ directory inside my Rails application’s root to house all of my Sencha Architect 2 project’s files like this:

      Second, I wrote a Rake script (see below) to compile the gui/ directory into a single app/assets/javascripts/gui.js asset and ran it like this:

      cd app/assets/javascripts/
      rake -f gui.rake               # builds the asset as necessary
      rake -f gui.rake clean default # forcefully rebuilds the asset

      Third, I placed the compiled app/assets/javascripts/gui.js asset into my Rails application’s asset pipeline by adding the following line to my app/assets/javascripts/application.js file:

      //= require gui

      That’s all folks!


      This Rake script operates by assembling a dependency graph from requires fields (which specify the names of other ExtJS classes that need to be loaded beforehand in order to avoid dependency errors) that are present in most ExtJS classes generated for the project by Sencha Architect 2.

      It then flattens the dependency graph into a linear sequence, using the Topological Sort algorithm, in such a way that simply loading each ExtJS class in the linear sequence, sequentially, will not trigger any dependency errors.

      require 'rake/clean'
      require 'tsort'
      require 'yaml'
      task default: 'gui.js'
      CLEAN.include 'gui.js'
      directory project = '../../../gui'
      file 'gui' => project do |t|
        ln_s t.prerequisites.first, t.name
      file 'gui.js' => FileList['gui', 'gui/app/**/*.js'] do |t|
        requires_by_file = RequiresHash.new
        t.prerequisites.each do |file|
          if file.end_with? '.js' and File.read(file) =~ /\brequires:\s+(.+?)\s+\}/m
            requires_by_file[file] = Array(YAML.load($1)).map do |require|
              "gui/app/#{require.split('.', 2).last.tr('.', '/')}.js"
        File.open(t.name, 'w') do |file|
            '//= require_self',
            requires_by_file.tsort.map {|r| "//= require ./#{r}" },
            '//= require_tree ./gui/app',
            '//= require_tree ./gui',
            # add a blank line to separate asset requires from inline logic
            # that directly corresponds to the require_self directive above
            # prevent GUI logic from enabling ExtJS' dynamic asset loader since
            # all necessary GUI assets are already present in this gui.js asset
            'Ext.Loader.setConfig = function(){};'
      class RequiresHash < Hash
        include TSort
        alias tsort_each_node each_key
        def tsort_each_child(node, &block)
          fetch(node, []).each(&block)