class Slinky::ManifestFile

Attributes

build_path[RW]
directives[R]
last_built[R]
manifest[R]
parent[R]
source[RW]
updated[R]

Public Class Methods

new(source, build_path, manifest, parent = nil, options = {:devel => false}) click to toggle source
# File lib/slinky/manifest.rb, line 502
def initialize source, build_path, manifest, parent = nil, options = {:devel => false}
  @parent = parent
  @source = Pathname.new(source).cleanpath.to_s
  @last_built = Time.at(0)

  @cfile = Compilers.cfile_for_file(@source)

  @directives = find_directives
  @build_path = build_path
  @manifest = manifest
  @devel = true if options[:devel]
end

Public Instance Methods

build() click to toggle source

Builds the file by handling and compiling it and then copying it to the build path

# File lib/slinky/manifest.rb, line 755
def build
  return nil unless should_build

  if !File.exists? @build_path
    FileUtils.mkdir_p(@build_path)
  end
  to = build_to
  path = process to

  if path != to
    FileUtils.cp(path.to_s, to.to_s)
    @last_built = Time.now
  end
  to
end
build_to() click to toggle source

Path to which the file will be built

# File lib/slinky/manifest.rb, line 749
def build_to
  Pathname.new(@build_path) + output_path.basename
end
compile(path, to = nil) click to toggle source

Takes a path and compiles the file if necessary. @return Pathname the path of the compiled file, or the original

path if compiling is not necessary
# File lib/slinky/manifest.rb, line 668
def compile path, to = nil
  if @cfile
    cfile = @cfile.clone
    cfile.source = path
    cfile.print_name = @source
    cfile.output_path = to if to
    cfile.file do |cpath, _, _, _|
      path = cpath
    end
  end
  path ? Pathname.new(path) : nil
end
dependencies() click to toggle source

Gets the list of manifest files that this one depends on according to its directive list and the dependencies config option.

Throws a FileNotFoundError if a dependency doesn't exist in the tree.

# File lib/slinky/manifest.rb, line 526
def dependencies
  SlinkyError.batch_errors do
    (@directives[:slinky_require].to_a +
     @manifest.config.dependencies["/" + relative_source_path.to_s].to_a).map{|rf|
      required = parent.find_by_path(rf, true).flatten
      if required.empty?
        error = "Could not find file #{rf} required by /#{relative_source_path}"
        SlinkyError.raise FileNotFoundError, error
      end
      required
    }.flatten
  end
end
external_dependencies() click to toggle source

The list of paths to files external to the manifest that this file depends on

# File lib/slinky/manifest.rb, line 688
def external_dependencies
  (@directives[:slinky_depends_external] || []).map{|ed|
    Dir.glob(File.join(@manifest.dir, ed))
  }.flatten
end
external_dependencies_updated?() click to toggle source
# File lib/slinky/manifest.rb, line 694
def external_dependencies_updated?
  return false if external_dependencies.empty?

  external_dependencies.map{|x| File.mtime(x)}.max > (@updated || Time.at(0))
end
find_directives() click to toggle source

Looks through the file for directives @return {Symbol => [String]} the directives in the file

# File lib/slinky/manifest.rb, line 617
def find_directives
  _, _, ext = @source.match(EXTENSION_REGEX).to_a
  directives = {}
  # check if this file might include directives
  if @cfile || DIRECTIVE_FILES.include?(ext)
    # make sure the file isn't too big to scan
    stat = File::Stat.new(@source)
    if stat.size < 1024*1024
      File.open(@source) {|f|
        matches = f.read.scan(BUILD_DIRECTIVES).to_a
        matches.each{|slice|
          key, value = slice.compact
          directives[key.to_sym] ||= []
          directives[key.to_sym] << value[1..-2] if value
        }
      } rescue nil
    end
  end

  @directives = directives
end
handle_directives(path, to = nil) click to toggle source

If there are any build directives for this file, the file is read and the directives are handled appropriately and a new file is written to a temp location.

@return String the path of the de-directivefied file

# File lib/slinky/manifest.rb, line 644
def handle_directives path, to = nil
  if path && @directives.size > 0
    out = File.read(path)
    out.gsub!(DEPENDS_DIRECTIVE, "")
    out.gsub!(EXTERNAL_DEPENDS_DIRECTIVE, "")
    out.gsub!(REQUIRE_DIRECTIVE, "")
    out.gsub!(SCRIPTS_DIRECTIVE){ @manifest.scripts_string }
    out.gsub!(STYLES_DIRECTIVE){ @manifest.styles_string }
    out.gsub!(PRODUCT_DIRECTIVE){
      @manifest.product_string($2[1..-2])
    }
    to = to || Tempfile.new("slinky").path + ".cache"
    File.open(to, "w+"){|f|
      f.write(out)
    }
    to
  else
    path
  end
end
in_tree?(path) click to toggle source

Predicate which determines whether the file is the supplied path or lies on supplied tree

# File lib/slinky/manifest.rb, line 584
def in_tree? path
  full_path = @manifest.dir + "/" + path
  abs_path = Pathname.new(full_path).expand_path.to_s
  dir = Pathname.new(@source).dirname.expand_path.to_s
  dir.start_with?(abs_path) || abs_path == @source
end
inspect() click to toggle source
# File lib/slinky/manifest.rb, line 775
def inspect
  to_s
end
invalidate() click to toggle source
# File lib/slinky/manifest.rb, line 515
def invalidate
  @last_built = Time.at(0)
  @last_md5 = nil
end
matches?(s, match_glob = false) click to toggle source

Predicate which determines whether the supplied name is the same as the file's name, taking into account compiled file extensions. For example, if mf refers to “/tmp/test/hello.sass”, `mf.matches? “hello.sass”` and `mf.matches? “hello.css”` should both return true.

@param String a filename @param Bool match_glob if true, matches according to glob rules @return Bool True if the filename matches, false otherwise

# File lib/slinky/manifest.rb, line 549
def matches? s, match_glob = false
  name = Pathname.new(@source).basename.to_s
  output = output_path.basename.to_s
  # check for stars that are not escaped
  a = [""]
  last = ""
  s.each_char {|c|
    if c != "*" || last == "\\"
      a[-1] << c
    else
      a << ""
    end
    last = c
  }

  if match_glob && a.size > 1
    r2 = a.reduce{|a, x| /#{a}.*#{x}/}
    name.match(r2) || output.match(r2)
  else
    name == s || output == s
  end
end
matches_path?(s, match_glob = false) click to toggle source

Predicate which determines whether the file matches (see `ManifestFile#matches?`) the full path relative to the manifest root.

# File lib/slinky/manifest.rb, line 575
def matches_path? s, match_glob = false
  p = Pathname.new(s)
  dir = Pathname.new("/" + relative_source_path.to_s).dirname
  matches?(p.basename.to_s, match_glob) &&
     dir == p.dirname
end
md5() click to toggle source

Gets the md5 hash of the source file

# File lib/slinky/manifest.rb, line 682
def md5
  Digest::MD5.hexdigest(File.read(@source)) rescue nil
end
output_path() click to toggle source

Returns the path to which this file should be output. This is equal to the source path unless the file needs to be compiled, in which case the extension returned is the output extension

@return Pathname the output path

# File lib/slinky/manifest.rb, line 596
def output_path
  if @cfile
    ext = /\.[^.]*$/
    Pathname.new(@source.gsub(ext, ".#{@cfile.output_ext}"))
  else
    Pathname.new(@source)
  end
end
process(to = nil, should_compile = true) click to toggle source

Gets manifest file ready for serving or building by handling the directives and compiling the file if neccesary. @param String path to which the file should be compiled

@return String the path of the processed file, ready for serving

# File lib/slinky/manifest.rb, line 705
def process to = nil, should_compile = true
  return if @processing # prevent infinite recursion
  start_time = Time.now
  hash = md5
  if hash != @last_md5
    find_directives
  end

  SlinkyError.batch_errors do
    depends = @directives[:slinky_depends].map{|f|
      ps = if f.start_with?("/")
             @manifest.find_by_pattern(f)
           else
             parent.find_by_path(f, true)
           end
      unless ps.size > 0
        SlinkyError.raise DependencyError,
                          "File #{f} depended on by #{@source} not found"
      end
      ps
    }.flatten.compact if @directives[:slinky_depends]
    depends ||= []
    @processing = true
    # process each file on which we're dependent, watching out for
    # infinite loops
    depends.each{|f| f.process }
    @processing = false

    # get hash of source file
    if @last_path && hash == @last_md5 &&
       depends.all?{|f| f.updated < start_time} &&
       !external_dependencies_updated?
      @last_path
    else
      @last_md5 = hash
      @updated = Time.now
      # mangle file appropriately
      f = should_compile ? (compile @source) : @source
      @last_path = handle_directives(f, to)
    end
  end
end
relative_output_path() click to toggle source

Returns the output path relative to the manifest directory

# File lib/slinky/manifest.rb, line 611
def relative_output_path
  output_path.relative_path_from(Pathname.new(@manifest.dir))
end
relative_source_path() click to toggle source

returns the source path relative to the manifest directory

# File lib/slinky/manifest.rb, line 606
def relative_source_path
  Pathname.new(@source).relative_path_from(Pathname.new(@manifest.dir))
end
should_build() click to toggle source
# File lib/slinky/manifest.rb, line 771
def should_build
  @manifest.files_for_all_products.include?(self) || ![".js", ".css"].include?(output_path.extname)
end
to_s() click to toggle source
# File lib/slinky/manifest.rb, line 779
def to_s
  "<Slinky::ManifestFile '#{@source}'>"
end