Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 3.1 Asset Pipeline not precompiling files from a different directory

I currently have a Rails 3.1 app that hosts multiple sites for several different customers. Each site is represented by a model that knows the domain name and path to where the assets are stored, along with some other info.

I've been using app/sites/domain-name as a location for storing assets, views and locales that are specific to a given site, and are running a custom middleware and controller actions for modifying the load path for Sprockets, setting up the view paths and so on.

middleware (loaded using config.middleware.use "Configurator" in application.rb):

class Configurator
  def initialize(app)
    @app = app
  end

  def call(env)
    @env = env
    clear_existing_paths
    prepend_local_assets
    @app.call(env)
  end

  def current_site
    # find site using @env
  end

  def current_site_path
    Rails.root.join('app', 'sites', current_site.path)
  end

  def clear_existing_paths
    paths = Rails.application.assets.paths
    Rails.application.assets.clear_paths
    paths.each do |path|
      next if path.include?("app/sites/")
      Rails.application.assets.append_path path
    end
  end

  def prepend_local_assets
    path = current_site_path.join('assets')
    return unless Dir.exists?(path)

    ['images', 'javascripts', 'misc', 'stylesheets'].each do |subdir|
      asset_dir = path.join(subdir).to_s
      next unless Dir.exists?(asset_dir)
      next if     Rails.application.assets.paths.include? asset_dir

      Rails.application.assets.prepend_path asset_dir
    end
  end
end

Asset bits from application.rb:

module MyApp
  class Application < Rails::Application
    ...
    config.middleware.use "Configurator"

    config.assets.enabled = true 
    config.assets.version = '1.0'
  end
end

And environments/production.rb:

MyApp::Application.configure do
  config.serve_static_assets = false
  config.assets.compress = true
  config.assets.compile = true
  config.assets.digest = true
  # Defaults to Rails.root.join("public/assets")
  # config.assets.manifest = YOUR_PATH
end

The problem is that while this setup works, it prevents me from precompiling the assets that is not shared with the entire app, making them be generated over and over again, from the looks of it.

Is there any way I could tell the precompiler to find the assets located here, and create a version of those as well? Each site has a site.css.scss and a site.js.coffee file that might require other assets inside the site-dir. Would be nice if I could get it precompiled to public/assets/domain-name/site.(js|css), so I could easily set up a separate subdomain for assets down the line when I need to optimize further

Final solution (2012-02-22)

After implementing what was suggested by Brian, I have ended up with

Main stylesheet/javascript stored in app/sites/<sitename>/assets/<shortname>/site.css|js, where sitename is the domain for this site, and shortname is the main part of the domain, with no subdomain or com|org|net|ccTLD.

Modified all views and stylesheets to prepend shortname to my asset paths.

In config/application.rb:

{ "sitename" => "shortname", ... }.each_pair do |domain, short|
  %w{stylesheets javascripts}.each do |dir|
    config.assets.paths << Rails.root.join("app/sites/#{domain}/assets/#{dir}").to_s
  end # Had to specify each dir to make it work
  config.assets.precompile += ["#{short}/site.css", "#{short}/site.js"]
end

When running rake assets:precompile this creates public/assets/shortname filled with all the assets for that site, and public/assets have all the shared assets as well. Works great for my needs.

And since everything ended up in public/assets, I was able to drop the Configurator-middleware, since the default configuration was able to find all the assets

like image 508
PerfectlyNormal Avatar asked Feb 13 '12 13:02

PerfectlyNormal


People also ask

How do you Precompile rails assets?

To compile your assets locally, run the assets:precompile task locally on your app. Make sure to use the production environment so that the production version of your assets are generated. A public/assets directory will be created. Inside this directory you'll find a manifest.

What does rake assets Clean do?

Two cleanup tasks: rake assets:clean is now a safe cleanup that only removes older assets that are no longer used, while rake assets:clobber nukes the entire public/assets directory. The clean task allows for rolling deploys that may still be linking to an old asset while the new assets are being built.

How does Rails asset pipeline work?

The asset pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages such as CoffeeScript, Sass and ERB. Prior to Rails 3.1 these features were added through third-party Ruby libraries such as Jammit and Sprockets.


2 Answers

I think the problem is, by adding each site as a path, sprockets only finds the first site.scss

I've tried this using compass, but it should be the same for plain sprockets. I haven't tried your configurator approach, but it looks straightforward to adapt my arrangement to it.

Can you change your app/sites/* to the more standard file arrangement?

./app/assets/javascripts/application.js
./app/assets/stylesheets/screen.css.scss
./app/assets/stylesheets/othersite/screen.css.scss

Change your config/application.rb, and add each of your sites. This will pregenerate all styles, on each of your hosts:

config.assets.precompile += ['screen.css', 'othersite/screen.css']

In your view/layouts/application, you'll need to configure the path to sitename:

= stylesheet_link_tag '[your sitename here]/screen'

After I rake assets:clean and precompile, I see this in public:

./assets/othersite/screen.css
./assets/othersite/screen.css.gz
./assets/screen.css
./assets/screen.css.gz
like image 64
Brian Maltzan Avatar answered Oct 05 '22 18:10

Brian Maltzan


Looks like Rails manages to find the assets at least when I changed config/environments/production.rb to include

config.assets.precompile += %w( site.js site.css )

and config/application.rb to include

config.assets.paths << Rails.root.join("app/sites/sitename/assets/stylesheets").to_s

This gives me a precompiled version, but only for the first site to have a site.css. I guess renaming them to sitename.css, or add a extra subdirectory sitename/site.css might work as well.

like image 33
PerfectlyNormal Avatar answered Oct 05 '22 17:10

PerfectlyNormal