Posted by Andy on Monday, July 26, 2010.

Sharing views across Rails 3 apps

Here at Brighter Planet we run several production Rails 3 apps, including the emission estimates service, the climate data service, and our keyserver. As the person reponsible for much of our recent front-end work, I wasn’t really looking forward to maintaining a half-dozen different versions of what is mostly the same layout. I wanted to DRY the situation up. What I really wanted was to put all the shared stuff into a gem that I could require from all of our apps that would just sort of insinuate itself into all the right places.

Luckily Rails 3 makes this possible, after a fashion. The key trick is giving your plugin a Railtie, which isn’t documented very well yet–this gist from Jose Valim is the best I could find.

To get started just add a file in lib/gemname/ called railtie.rb and require it from the main gemname.rb file:

module BrighterPlanetLayout
  class Railtie < Rails::Railtie
  end
end

Because it inherits from Rails::Railtie, you don’t have to “declare” the Railtie–Rails automatically keeps track of it and calls the right parts when they’re needed.

The easy part is telling Rails to add your gem’s view path to the app’s view path:

module BrighterPlanetLayout
  class Railtie < Rails::Railtie
    initializer 'brighter_planet_layout.add_paths' do |app|
      app.paths.app.views.push BrighterPlanetLayout.view_path
    end
  end
end

Telling ApplicationController to use the layout in your gem’s view path as the default is a little tricker–you have to use a to_prepare block:

module BrighterPlanetLayout
  class Railtie < Rails::Railtie
    # ...
    config.to_prepare do
      ApplicationController.layout 'brighter_planet'
    end
  end
end

It turns out the hardest part is hooking Rails up to your gem’s static asset files–stylesheets, images, fonts, etc. For that we add another instance of ActionDispatch::Static to the Rack middleware stack:

module BrighterPlanetLayout
  class Railtie < Rails::Railtie
    config.app_middleware.use '::ActionDispatch::Static', BrighterPlanetLayout.public_path
    # ...
  end
end

This is a fine solution in the development environment, but it’s too slow for production–your webserver has to fire up Rails just to push an image binary, for example. So in production we just copy static files to the app’s public dir:

module BrighterPlanetLayout
  class Railtie < Rails::Railtie
    if BrighterPlanetLayout.serve_static_files_using_rack? # "if in development environment"
      config.app_middleware.use '::ActionDispatch::Static', BrighterPlanetLayout.public_path
    end
    initializer 'brighter_planet_layout.copy_static_files_to_web_server_document_root' do
      if BrighterPlanetLayout.copy_static_files? # "if in production"
        BrighterPlanetLayout.copy_static_files_to_web_server_document_root
      end
    end
    # ...
  end
end

And that’s it. Check out the gem source for details on the folder structure and for additional tricks like loading shared helpers. Next time: how to use this shared layout with Jekyll.

What blog is this?

Safety in Numbers is Brighter Planet's blog about climate science, Ruby, Rails, data, transparency, and, well, us.

Who's behind this?

We're Brighter Planet, the world's leading computational sustainability platform.

Who's blogging here?

  1. Patti Prairie CEO