SASS Ruby Extension to Check if File Exists

SASS Custom Functions
CSS is executed client-side and so it cannot check for the existence of an image, font or other asset file being referenced. However, since Sass is written in Ruby, it allows for server-side calls by extending Sass via custom functions. Here is a custom function to check for the existence of a file:

If this code is placed in a file named functions.rb, the Sass watch command would be:

So, why would you ever need to check for the existence of a file at Sass compile time? One place I found it useful (I’m sure there are other uses) was when eliminating duplication of internationalized CTA (call-to-action) images. Canadian (or British) English is similar to U.S. English in many ways, but there are some words that are a different between the two (favorite vs favourite for example). The following Sass mixin selects a CTA image from a folder based on the lang attribute set on the page. In the case of Canadian English, it will first check to see if the image exists in the en-ca folder. If not, it will fall back to using the image from the en-us folder. This avoids duplication of the English images that are the same in both Canadian and U.S. English. The benefit of this is:

  1. Fewer total assets, so they are easier to maintain
  2. The total asset payload is smaller (especially important if used in a mobile app)

6 thoughts on “SASS Ruby Extension to Check if File Exists

  1. Hey there Steve —

    This is exactly what I was looking for. I’m having a problem getting it to work and I wondered if you would mind providing insight.

    Instead of requiring a separate functions.rb file from a command line sass command, I included it in my config.rb (I’m using Compass.) The problem is that the file path that is passed to the function is a relative one, and the File.exists? method that’s being called seems to be needing an absolute path. I tried prepending Dir.getwd to the path to no avail. Any suggestions?

    For example, here’s how I altered the function:
    https://gist.github.com/5006999

    Any insight? Thanks in advance for your time.

    • Fitz, glad this is something that could be useful. To hopefully answer your question, File.exists can take a relative path. So, for instance, here is a scenario that I have working in production:

      My file structure is as follows
      assets/
        |-css
        |-scss/
          |-extensions/
            |-functions.rb

      from the assets folder, I run the command:
      sass –watch scss:css –style compressed –require ./scss/extensions/functions.rb

      My functions.rb file on line 4 actually has:
      path = image_file.sub(/\.\.\/\.\.\//,’../’)
      to create the relative path to pass to File.exists.

      Hope that helps!

    • Hello Fitz, Do you have to do anything extra to get this running ? I have a function returning a boolean in config.rb but it isn’t getting picked by in SASS. Any idea why ?

  2. Hello Steve, this worked a treat for me but I did have to modify it a bit as I am running it as part of Compass/SASS invoked via Grunt using: grunt-contrib-watch and grunt-contrib-compass.
    I also added a debug print option so I could see what it was doing.

    So this is what worked for me:

    module Sass::Script::Functions
    def file_exists(image_file, print_debug = false)
    baseDir = File.expand_path(File.dirname(File.dirname(__FILE__)))
    path = baseDir + ‘/html/’ + image_file.value
    result = Sass::Script::Bool.new(File.file?(path))

    if print_debug
    if result.to_s == ‘true’
    puts ‘++ :’ + path
    else
    puts ‘– :’ + path
    end
    end

    return result
    end
    end

    Currently my structure is:
    site_root\
    ..compass\
    ….config.rb (where I put the module)
    ..html\
    ….images\
    ..Gruntfile.js

  3. Awesome helper! Was looking for something like that for few months!
    had to play a little bit with the path as I’m using CodeKit (and also complete ignorant about Ruby).
    Kevin Hunt’s script’s came handy!

    in my setup:
    baseDir = File.expand_path(File.dirname(File.dirname(__FILE__)))
    path = baseDir + “[ project specific bit ]” + image_file.value

Leave a Reply

Your email address will not be published. Required fields are marked *