Revised Batman.js all-views-cache hack

Batman.js works very well with Rails. However, when it comes to production environment, the way Batman request every view template via Ajax on demand will cause some inconvenience.

John Lynch and Ryan Funduk has an post about how to precompile all view templates into one json, and fetch it on App runs. This solves most issues, but has one drawback. When you're about to load the all-views json file, your App has actually became running already. It will look for the template of your root (or firtly-requested) view, and will be unable to find it in View Cache 'cause the Ajax is still running.

Eventually, Batman.js will try to download the view template almost at the same time your precompiled all-views json starts to be fetched. And this will generate one more HTTP request, and may occur some 404 errors if those template files are not served on the server.

Here's how I try to revise this hack and solve the above issue: prevent the Batman.App from running until the view caches are successfully loaded.

Firstly you tell your app to prevent run when it's ready to do so:

/app/assets/batman/my_app.js.coffee
#= require ./helpers/views_preloader
class MyApp extends Batman.App
  @on 'ready', =>
    @prevent 'run'
    @preloadHTMLs()

then add one line to the preloadHTMLs helper to tell your App to run.

/app/assets/batman/helpers/views_preloader.js.coffee.erb
MyApp.preloadViews = ->
  new Batman.Request({
    url: '<%= asset_path("all_views.json") %>'
    type: 'json'
    error: (response)  -> throw new Error("Could not load views")
    success: (all_views) ->
      for view of all_views
        Batman.View.store.set(view, all_views[view])
        
      MyApp.allowAndFire 'run' # Add this to run your App
  })

and of course, don't forget our all_htmls.json.erb old friend from the original hack:

/app/assetes/batman/all_htmls.json.erb
<%=
  template_path = File.expand_path('../html', __FILE__)
  paths = Dir.glob("#{template_path}/**/*").select{|f| File.file?(f) && (f =~ /\.(html|haml|erb)$/i) }
  paths.inject({}) do |all_views, f|
    viewname = f.sub( /^#{template_path}/, '' ).sub( /\..*$/i, '' )
    all_views[viewname] = environment["html/#{viewname}.html"].to_s
    all_views
  end.to_json
%>
/config/environments/production.rb
config.assets.precompile += %w(all_views.json)

You're now all done!

Ref: Batman's Secret Cache

comments powered by Disqus