Class: Slinky::ManifestFile

Inherits:
Object
  • Object
show all
Defined in:
lib/slinky/manifest.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, build_path, manifest, parent = nil, options = {:devel => false}) ⇒ ManifestFile

Returns a new instance of ManifestFile.



500
501
502
503
504
505
506
507
508
509
510
511
# File 'lib/slinky/manifest.rb', line 500

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

Instance Attribute Details

#build_pathObject

Returns the value of attribute build_path.



497
498
499
# File 'lib/slinky/manifest.rb', line 497

def build_path
  @build_path
end

#directivesObject (readonly)

Returns the value of attribute directives.



498
499
500
# File 'lib/slinky/manifest.rb', line 498

def directives
  @directives
end

#last_builtObject (readonly)

Returns the value of attribute last_built.



498
499
500
# File 'lib/slinky/manifest.rb', line 498

def last_built
  @last_built
end

#manifestObject (readonly)

Returns the value of attribute manifest.



498
499
500
# File 'lib/slinky/manifest.rb', line 498

def manifest
  @manifest
end

#parentObject (readonly)

Returns the value of attribute parent.



498
499
500
# File 'lib/slinky/manifest.rb', line 498

def parent
  @parent
end

#sourceObject

Returns the value of attribute source.



497
498
499
# File 'lib/slinky/manifest.rb', line 497

def source
  @source
end

#updatedObject (readonly)

Returns the value of attribute updated.



498
499
500
# File 'lib/slinky/manifest.rb', line 498

def updated
  @updated
end

Instance Method Details

#buildObject

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



753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
# File 'lib/slinky/manifest.rb', line 753

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_toObject

Path to which the file will be built



747
748
749
# File 'lib/slinky/manifest.rb', line 747

def build_to
  Pathname.new(@build_path) + output_path.basename
end

#compile(path, to = nil) ⇒ Object

Takes a path and compiles the file if necessary.

Returns:

  • Pathname the path of the compiled file, or the original path if compiling is not necessary



666
667
668
669
670
671
672
673
674
675
676
677
# File 'lib/slinky/manifest.rb', line 666

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

#dependenciesObject

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.



524
525
526
527
528
529
530
531
532
533
534
535
536
# File 'lib/slinky/manifest.rb', line 524

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_dependenciesObject

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



686
687
688
689
690
# File 'lib/slinky/manifest.rb', line 686

def external_dependencies
  (@directives[:slinky_depends_external] || []).map{|ed|
    Dir.glob(File.join(@manifest.dir, ed))
  }.flatten
end

#external_dependencies_updated?Boolean

Returns:

  • (Boolean)


692
693
694
695
696
# File 'lib/slinky/manifest.rb', line 692

def external_dependencies_updated?
  return false if external_dependencies.empty?

  external_dependencies.map{|x| File.mtime(x)}.max > (@updated || Time.at(0))
end

#find_directivesSymbol => [String]

Looks through the file for directives

Returns:

  • (Symbol => [String])

    the directives in the file



615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/slinky/manifest.rb', line 615

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) ⇒ Object

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.

Returns:

  • String the path of the de-directivefied file



642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
# File 'lib/slinky/manifest.rb', line 642

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) ⇒ Boolean

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

Returns:

  • (Boolean)


582
583
584
585
586
587
# File 'lib/slinky/manifest.rb', line 582

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

#inspectObject



773
774
775
# File 'lib/slinky/manifest.rb', line 773

def inspect
  to_s
end

#invalidateObject



513
514
515
516
# File 'lib/slinky/manifest.rb', line 513

def invalidate
  @last_built = Time.at(0)
  @last_md5 = nil
end

#matches?(s, match_glob = false) ⇒ Boolean

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.

Parameters:

  • String

    a filename

  • Bool

    match_glob if true, matches according to glob rules

Returns:

  • (Boolean)

    Bool True if the filename matches, false otherwise



547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
# File 'lib/slinky/manifest.rb', line 547

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) ⇒ Boolean

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

Returns:

  • (Boolean)


573
574
575
576
577
578
# File 'lib/slinky/manifest.rb', line 573

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

#md5Object

Gets the md5 hash of the source file



680
681
682
# File 'lib/slinky/manifest.rb', line 680

def md5
  Digest::MD5.hexdigest(File.read(@source)) rescue nil
end

#output_pathObject

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

Returns:

  • Pathname the output path



594
595
596
597
598
599
600
601
# File 'lib/slinky/manifest.rb', line 594

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) ⇒ Object

Gets manifest file ready for serving or building by handling the directives and compiling the file if neccesary.

Parameters:

  • String

    path to which the file should be compiled

Returns:

  • String the path of the processed file, ready for serving



703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
# File 'lib/slinky/manifest.rb', line 703

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_pathObject

Returns the output path relative to the manifest directory



609
610
611
# File 'lib/slinky/manifest.rb', line 609

def relative_output_path
  output_path.relative_path_from(Pathname.new(@manifest.dir))
end

#relative_source_pathObject

returns the source path relative to the manifest directory



604
605
606
# File 'lib/slinky/manifest.rb', line 604

def relative_source_path
  Pathname.new(@source).relative_path_from(Pathname.new(@manifest.dir))
end

#should_buildObject



769
770
771
# File 'lib/slinky/manifest.rb', line 769

def should_build
  @manifest.files_for_all_products.include?(self) || ![".js", ".css"].include?(output_path.extname)
end

#to_sObject



777
778
779
# File 'lib/slinky/manifest.rb', line 777

def to_s
  "<Slinky::ManifestFile '#{@source}'>"
end