21

I'm using the PDFkit in my controller to build out a series of PDFs, zip them up, and then send them to the user.

In order to control the output styles, I tell PDFKit which stylesheets to use during content generation. I need to pass along the file reference of the CSS file. Since Rails is now compiling and renaming my stylesheets, I'm not sure how to reference the compiled CSS asset inside my controller.

Here's what I used to do:

InvoicesController < ApplicationController
  def download
    kit = PDFKit.new(render_to_string(:show, :layout => false))
    kit.stylesheets << "#{Sass::Plugin.options[:css_location]}/application.css"
    kit.to_file("#{file_date_string}.pdf")
    # snip
  end
end

Sass::Plugin.options[:css_location] now returns the incorrect location, not to mention the fact that application.css is no longer the valid name of the file. I should mention that I have an app/assets/application.css file that serves as a manifest for my SCSS files, and it is working correctly in my views via the stylesheet_link_tag() method.

Basically what I'm looking for is a controller equivalent of asset_path() in order to do something like this:

kit = PDFKit.new(render_to_string(:show, :layout => false))
kit.stylesheets << asset_path('application.css')
kit.to_file("#{file_date_string}.pdf")

Can anyone help?

cdreier
  • 211
  • 2
  • 5
  • Actually there is another issue here: You need to use an absolute filesystem path to reference the stylesheets with pdfkit. asset_path returns a relative "web path" though. I solved the issue for now by creating a pdf.css which I put in public/stylesheets. Not ideal as it avoids the asset pipeline, but works and forces me to separate pdf relevant stlyes out of my app.css. – Nico Sep 14 '11 at 10:26
  • 2
    Please indicate an answer as correct. This helps out the community a lot. – Bert Goethals Jan 04 '12 at 10:41

6 Answers6

31

Rails.application.assets is poorly documented but it provides access to Rails' hook into Sprockets, as a Sprockets::Environment object. Rails uses Sprockets to basically run the whole asset pipeline, and this is where you should hook in for things like this:

kit.stylesheets << Rails.application.assets['application.css'].pathname

https://github.com/sstephenson/sprockets says of it:

Accessing Assets Programmatically

You can use the find_asset method (aliased as []) to retrieve an asset from a Sprockets environment. Pass it a logical path and you'll get a Sprockets::BundledAsset instance back:

  environment['application.js']
  # => #<Sprockets::BundledAsset ...>

Call to_s on the resulting asset to access its contents, length to get its length in bytes, mtime to query its last-modified time, and pathname to get its full path on the filesystem.

Dave Burt
  • 1,838
  • 19
  • 20
  • 2
    It doesn't seem to work with assets requiring precompilation. It just loads the raw file (`.scss` in my case). – mbillard Apr 10 '12 at 14:11
  • I'm getting processed CSS output both of these ways: irb(main):008:0* Rails.version => "3.2.2" irb(main):009:0> Rails.application.assets["main.css"].to_s == Rails.application.assets["main.css.scss"].to_s => true – Dave Burt Apr 27 '12 at 05:55
  • While the file contents match up, for some reason I'm finding they don't come through correctly in the PDF. Generating it via the rack middleware works, but not through explicitly instantiating a PDFKit object. I've found the only way I can get it working is by putting a compiled stylesheet in /public/ and then coding that stylesheet into the relevant layout. eg `" media="all" rel="stylesheet" type="text/css" />` – benz001 Mar 19 '13 at 10:05
13

view_context.asset_path 'application.css' should do the trick.

Manuel Meurer
  • 3,238
  • 6
  • 35
  • 50
  • 6
    If you are trying to check in Rails console, `ActionController::Base.new.view_context.asset_path "application.css"` – Jason Kim Nov 20 '12 at 18:30
10

Rails.application.assets['application.css'].pathname always returns the original path of the raw asset, not the precompiled file, so the top answer did not work for me.

However, calling to_s on the bundled asset instead of pathname does seem to correctly return the body of the precompiled asset, so you can just use an inline style instead of using kit.stylesheets <<:

<style> <%= Rails.application.assets["application.css"].to_s %> </style>

Louis Simoneau
  • 1,781
  • 14
  • 18
4

One solution is to pull the CSS inline in your view.

In HAML, this could look like:

%style
  = Sass.compile(File.read(File.join(Rails.root, 'app', 'assets', 'stylesheets', 'sass', "application.scss")))

Or in ERB:

<style>
  <%= Sass.compile(File.read(File.join(Rails.root, 'app', 'assets', 'stylesheets', 'sass', "application.scss"))) %>
</style>
sbleon
  • 1,685
  • 2
  • 13
  • 20
0

The best way to get the compiled name is from the manifest that is generate when you compile.

You can make a controller method that serves the raw name in development, and then accesses the manifest in production to map the correct name.

The location of the manifest by default is:

File.join(Rails.public_path, config.assets.prefix, 'manifest.yml')

But it looks like you can access this as a hash at config.assets.digests

config.assets.digests[css_file_name_as_string]

Richard Hulse
  • 10,383
  • 2
  • 33
  • 37
-2

I think stylesheet_path("application") is what you're looking for

RyanWilcox
  • 13,890
  • 1
  • 36
  • 60
  • Sadly, no. `stylesheet_path()` is a view helper, and I need this to work inside of a controller action. I tried including the module into the controller, but it assumes the existence of an `assets_path` method which does not exist in the controller scope. – cdreier Sep 14 '11 at 07:51