class Sass::Importers::Filesystem

The default importer, used for any strings found in the load path. Simply loads Sass files from the filesystem using the default logic.

Constants

REDUNDANT_DIRECTORY

Attributes

root[RW]

Public Class Methods

new(root) click to toggle source

Creates a new filesystem importer that imports files relative to a given path.

@param root [String] The root path.

This importer will import files relative to this path.
# File lib/sass/importers/filesystem.rb, line 14
def initialize(root)
  @root = File.expand_path(root)
  @real_root = Sass::Util.realpath(@root).to_s
  @same_name_warnings = Set.new
end

Public Instance Methods

directories_to_watch() click to toggle source

@see Sass::Importers::Base#directories_to_watch

# File lib/sass/importers/filesystem.rb, line 58
def directories_to_watch
  [root]
end
eql?(other) click to toggle source
# File lib/sass/importers/filesystem.rb, line 53
def eql?(other)
  !other.nil? && other.respond_to?(:root) && root.eql?(other.root)
end
find(name, options) click to toggle source

@see Sass::Importers::Base#find

# File lib/sass/importers/filesystem.rb, line 26
def find(name, options)
  _find(@root, name, options)
end
find_relative(name, base, options) click to toggle source

@see Sass::Importers::Base#find_relative

# File lib/sass/importers/filesystem.rb, line 21
def find_relative(name, base, options)
  _find(File.dirname(base), name, options)
end
hash() click to toggle source
# File lib/sass/importers/filesystem.rb, line 49
def hash
  @root.hash
end
key(name, options) click to toggle source

@see Sass::Importers::Base#key

# File lib/sass/importers/filesystem.rb, line 39
def key(name, options)
  [self.class.name + ":" + File.dirname(File.expand_path(name)),
   File.basename(name)]
end
mtime(name, options) click to toggle source

@see Sass::Importers::Base#mtime

# File lib/sass/importers/filesystem.rb, line 31
def mtime(name, options)
  file, _ = Sass::Util.destructure(find_real_file(@root, name, options))
  File.mtime(file) if file
rescue Errno::ENOENT
  nil
end
public_url(name, sourcemap_directory) click to toggle source
# File lib/sass/importers/filesystem.rb, line 69
def public_url(name, sourcemap_directory)
  file_pathname = Sass::Util.cleanpath(Sass::Util.absolute_path(name, @root))
  return Sass::Util.file_uri_from_path(file_pathname) if sourcemap_directory.nil?

  sourcemap_pathname = Sass::Util.cleanpath(sourcemap_directory)
  begin
    Sass::Util.file_uri_from_path(
      Sass::Util.relative_path_from(file_pathname, sourcemap_pathname))
  rescue ArgumentError # when a relative path cannot be constructed
    Sass::Util.file_uri_from_path(file_pathname)
  end
end
to_s() click to toggle source

@see Sass::Importers::Base#to_s

# File lib/sass/importers/filesystem.rb, line 45
def to_s
  @root
end
watched_file?(filename) click to toggle source

@see Sass::Importers::Base#watched_file?

# File lib/sass/importers/filesystem.rb, line 63
def watched_file?(filename)
  # Check against the root with symlinks resolved, since Listen
  # returns fully-resolved paths.
  filename =~ /\.s[ac]ss$/ && filename.start_with?(@real_root + File::SEPARATOR)
end

Protected Instance Methods

escape_glob_characters(name) click to toggle source
# File lib/sass/importers/filesystem.rb, line 128
def escape_glob_characters(name)
  name.gsub(/[\*\[\]\{\}\?]/) do |char|
    "\\#{char}"
  end
end
extensions() click to toggle source

A hash from file extensions to the syntaxes for those extensions. The syntaxes must be `:sass` or `:scss`.

This can be overridden by subclasses that want normal filesystem importing with unusual extensions.

@return [{String => Symbol}]

# File lib/sass/importers/filesystem.rb, line 101
def extensions
  {'sass' => :sass, 'scss' => :scss}
end
find_real_file(dir, name, options) click to toggle source

Given a base directory and an `@import`ed name, finds an existant file that matches the name.

@param dir [String] The directory relative to which to search. @param name [String] The filename to search for. @return [(String, Symbol)] A filename-syntax pair.

# File lib/sass/importers/filesystem.rb, line 141
      def find_real_file(dir, name, options)
        # On windows 'dir' or 'name' can be in native File::ALT_SEPARATOR form.
        dir = dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
        name = name.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?

        found = possible_files(remove_root(name)).map do |f, s|
          path = (dir == "." || Sass::Util.pathname(f).absolute?) ? f :
            "#{escape_glob_characters(dir)}/#{f}"
          Dir[path].map do |full_path|
            full_path.gsub!(REDUNDANT_DIRECTORY, File::SEPARATOR)
            [Sass::Util.cleanpath(full_path).to_s, s]
          end
        end.flatten(1)
        return if found.empty?

        if found.size > 1 && !@same_name_warnings.include?(found.first.first)
          found.each {|(f, _)| @same_name_warnings << f}
          relative_to = Sass::Util.pathname(dir)
          if options[:_from_import_node]
            # If _line exists, we're here due to an actual import in an
            # import_node and we want to print a warning for a user writing an
            # ambiguous import.
            candidates = found.map do |(f, _)|
              "  " + Sass::Util.pathname(f).relative_path_from(relative_to).to_s
            end.join("\n")
            raise Sass::SyntaxError.new("It's not clear which file to import for '@import "#{name}"'.
Candidates:
#{candidates}
Please delete or rename all but one of these files.
")
          else
            # Otherwise, we're here via StalenessChecker, and we want to print a
            # warning for a user running `sass --watch` with two ambiguous files.
            candidates = found.map {|(f, _)| "    " + File.basename(f)}.join("\n")
            Sass::Util.sass_warn <<WARNING
WARNING: In #{File.dirname(name)}:
  There are multiple files that match the name "#{File.basename(name)}":
#{candidates}
WARNING
          end
        end
        found.first
      end
possible_files(name) click to toggle source

Given an `@import`ed path, returns an array of possible on-disk filenames and their corresponding syntaxes for that path.

@param name [String] The filename. @return [Array(String, Symbol)] An array of pairs.

The first element of each pair is a filename to look for;
the second element is the syntax that file would be in (`:sass` or `:scss`).
# File lib/sass/importers/filesystem.rb, line 112
def possible_files(name)
  name = escape_glob_characters(name)
  dirname, basename, extname = split(name)
  sorted_exts = extensions.sort
  syntax = extensions[extname]

  if syntax
    ret = [["#{dirname}/{_,}#{basename}.#{extensions.invert[syntax]}", syntax]]
  else
    ret = sorted_exts.map {|ext, syn| ["#{dirname}/{_,}#{basename}.#{ext}", syn]}
  end

  # JRuby chokes when trying to import files from JARs when the path starts with './'.
  ret.map {|f, s| [f.sub(/^\.\//, ''), s]}
end
remove_root(name) click to toggle source

If a full uri is passed, this removes the root from it otherwise returns the name unchanged

# File lib/sass/importers/filesystem.rb, line 86
def remove_root(name)
  if name.index(@root + "/") == 0
    name[(@root.length + 1)..-1]
  else
    name
  end
end
split(name) click to toggle source

Splits a filename into three parts, a directory part, a basename, and an extension Only the known extensions returned from the extensions method will be recognized as such.

# File lib/sass/importers/filesystem.rb, line 188
def split(name)
  extension = nil
  dirname, basename = File.dirname(name), File.basename(name)
  if basename =~ /^(.*)\.(#{extensions.keys.map {|e| Regexp.escape(e)}.join('|')})$/
    basename = $1
    extension = $2
  end
  [dirname, basename, extension]
end

Private Instance Methods

_find(dir, name, options) click to toggle source
# File lib/sass/importers/filesystem.rb, line 200
def _find(dir, name, options)
  full_filename, syntax = Sass::Util.destructure(find_real_file(dir, name, options))
  return unless full_filename && File.readable?(full_filename)

  # TODO: this preserves historical behavior, but it's possible
  # :filename should be either normalized to the native format
  # or consistently URI-format.
  full_filename = full_filename.tr("\\", "/") if Sass::Util.windows?

  options[:syntax] = syntax
  options[:filename] = full_filename
  options[:importer] = self
  Sass::Engine.new(File.read(full_filename), options)
end