Search code examples
ruby-on-railsasset-pipelinebundler

How to copy over /public folder of Rails Engine to a Rails application in production?


I know a simple solution would be just to manually copy over all the files in the Rails Engine's /public folder to the Rails application's /public folder. However, this means that each installation would require manually copying.

Furthermore because the Javascript files that my engine uses have hard-coded image paths, I cannot simply throw all my static files under app/assets or vendor/assets, as then Rails would copy them over to public/assets. I can't change the path where Sprockets outputs the files as I have other gems that expect their assets to be in the default public/assets folder.

I tried doing something like

    class Engine < ::Rails::Engine
        if Rails.application.config.serve_static_assets
            initializer "static assets" do |app|
              app.middleware.insert_before(::ActionDispatch::Static, ::ActionDispatch::Static, "#{root}/public")
            end
        end
    end

but this only works for development.


Solution

  • Personally, I believe that the best solution would be to update your javascript files to follow Rails' conventions by replacing the hard-coded images in the javascript with the asset path helper - http://guides.rubyonrails.org/asset_pipeline.html#coding-links-to-assets and then throwing everything into app/assets.

    With that said, I can think of situations where you may not want to do that, and that may be the case here.

    From your post, I'm guessing that you're precompiling assets for production (i.e. - running rake assets:precompile). In that case, you can just hook into the assets:precompile rake task and copy the files over. It would look something like this in your engine's lib/tasks/my_engine_tasks.rake:

    Rake::Task["assets:precompile"].enhance do
      Rake::Task["my_engine:copy_assets"].invoke
    end
    
    namespace :my_engine do
      task :copy_assets => :"assets:environment"  do
        assets = ["file1", "file2"]
        assets.each do |asset|
          source_file = File.join(MyEngine::Engine.root, 'public', asset)
          dest_file = File.join(Rails.root, 'public', asset)
          FileUtils.copy_file source_file, dest_file, true
        end
      end
    end
    

    The above is a simple copy operation but you can also manually run sprockets over those files as well. Take a look at the code for the assets:precompile task (lib/sprockets/rails/task.rb in the sprockets-rails gem) and the Sprockets Manifest class (lib/sprockets/manifest.rb in the sprockets gem).