module Buildr class FilterTask < Rake::Task # The target directory. attr_reader :target # Filter to use. attr_accessor :filter def initialize(*args) super enhance do |task| fail "No target directory specified" if !target || (File.exist?(target) && !File.directory?(target)) unless copy_map.empty? verbose(Rake.application.options.trace || false) do copy_map do |dest, src| mkpath File.dirname(dest) rescue nil case filter when Proc, Method filtered = filter.call(File.read(src)) File.open(dest, "w") { |file| file.write filtered } when Hash filtered = File.read(src).gsub(/\$\{.*\}/) { |str| filter[str[2..-2]] || str } File.open(dest, "w") { |file| file.write filtered } when nil cp src, dest else fail "Filter can be a hash (key=>value), or a proc/method; I don't understand #{filter}" end end touch target if File.exist?(target) end end end end def include(*files) prerequisites.include *files self end alias :add :include def exclude(*files) prerequisites.exclude *files self end def into(dir) dir = File.expand_path(dir) unless @target == dir @target = dir file(dir).enhance [self] end self end def using(filter, &block) self.filter = filter || block self end def needed? return false if target.nil? || copy_map.empty? return true unless File.exist?(target) return true if copy_map.any? { |dest, src| !File.exist?(dest) || File.mtime(src) > File.mtime(dest) } false end protected # Return a copy map of all the files that need copying: the key is # the file to copy to, the value is the source file. If called with # a block, yields with each dest/source pair. def copy_map(&block) # Create a map between the source file and the similarly named # file in the target directory, including all files nested inside # directories. @copy_map ||= prerequisites.map(&:to_s).inject({}) do |map, path| if File.directory?(path) Dir[File.join(path, "**", "*")].each do |file| map[file.sub(File.dirname(path), target)] = file unless File.directory?(file) || prerequisites.exclude?(file) end elsif File.exist?(path) map[File.join(target, File.basename(path))] = path end map end.reject do |dest, src| # ... while ignoring that which does not need updating. File.exist?(dest) && File.stat(dest).mtime > File.stat(src).mtime end if block_given? @copy_map.each(&block) else @copy_map end end end def filter(*files) task = nil namespace { task = FilterTask.define_task("filter").include *files } task end end