summaryrefslogtreecommitdiffstats
path: root/lib/puppet/file_serving/fileset.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet/file_serving/fileset.rb')
-rw-r--r--lib/puppet/file_serving/fileset.rb138
1 files changed, 138 insertions, 0 deletions
diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb
new file mode 100644
index 000000000..7f7b9fc2d
--- /dev/null
+++ b/lib/puppet/file_serving/fileset.rb
@@ -0,0 +1,138 @@
+#
+# Created by Luke Kanies on 2007-10-22.
+# Copyright (c) 2007. All rights reserved.
+
+require 'find'
+require 'puppet/file_serving'
+require 'puppet/file_serving/metadata'
+
+# Operate recursively on a path, returning a set of file paths.
+class Puppet::FileServing::Fileset
+ attr_reader :path, :ignore, :links
+ attr_accessor :recurse
+
+ # Find our collection of files. This is different from the
+ # normal definition of find in that we support specific levels
+ # of recursion, which means we need to know when we're going another
+ # level deep, which Find doesn't do.
+ def find
+ files = perform_recursion
+
+ # Now strip off the leading path, so each file becomes relative, and remove
+ # any slashes that might end up at the beginning of the path.
+ result = files.collect { |file| file.sub(%r{^#{@path}/*}, '') }
+
+ # And add the path itself.
+ result.unshift(".")
+
+ result
+ end
+
+ # Should we ignore this path?
+ def ignore?(path)
+ # 'detect' normally returns the found result, whereas we just want true/false.
+ ! @ignore.detect { |pattern| File.fnmatch?(pattern, path) }.nil?
+ end
+
+ def ignore=(values)
+ values = [values] unless values.is_a?(Array)
+ @ignore = values
+ end
+
+ def initialize(path, options = {})
+ raise ArgumentError.new("Fileset paths must be fully qualified") unless path =~ /^#{::File::SEPARATOR}/
+
+ @path = path
+
+ # Set our defaults.
+ @ignore = []
+ @links = :manage
+ @recurse = false
+
+ options.each do |option, value|
+ method = option.to_s + "="
+ begin
+ send(method, value)
+ rescue NoMethodError
+ raise ArgumentError, "Invalid option '%s'" % option
+ end
+ end
+
+ raise ArgumentError.new("Fileset paths must exist") unless stat = stat(path)
+ end
+
+ def links=(links)
+ links = links.intern if links.is_a?(String)
+ raise(ArgumentError, "Invalid :links value '%s'" % links) unless [:manage, :follow].include?(links)
+ @links = links
+ @stat_method = links == :manage ? :lstat : :stat
+ end
+
+ # Should we recurse further? This is basically a single
+ # place for all of the logic around recursion.
+ def recurse?(depth)
+ # If recurse is true, just return true
+ return true if self.recurse == true
+
+ # Return false if the value is false or zero.
+ return false if [false, 0].include?(self.recurse)
+
+ # Return true if our current depth is less than the allowed recursion depth.
+ return true if self.recurse.is_a?(Fixnum) and depth <= self.recurse
+
+ # Else, return false.
+ return false
+ end
+
+ private
+
+ # Pull the recursion logic into one place. It's moderately hairy, and this
+ # allows us to keep the hairiness apart from what we do with the files.
+ def perform_recursion
+ # Start out with just our base directory.
+ current_dirs = [@path]
+
+ next_dirs = []
+
+ depth = 1
+
+ result = []
+ return result unless recurse?(depth)
+
+ while dir_path = current_dirs.shift or ((depth += 1) and recurse?(depth) and current_dirs = next_dirs and next_dirs = [] and dir_path = current_dirs.shift)
+ next unless stat = stat(dir_path)
+ next unless stat.directory?
+
+ Dir.entries(dir_path).each do |file_path|
+ next if [".", ".."].include?(file_path)
+
+ # Note that this also causes matching directories not
+ # to be recursed into.
+ next if ignore?(file_path)
+
+ # Add it to our list of files to return
+ result << File.join(dir_path, file_path)
+
+ # And to our list of files/directories to iterate over.
+ next_dirs << File.join(dir_path, file_path)
+ end
+ end
+
+ return result
+ end
+
+ # Stat a given file, using the links-appropriate method.
+ def stat(path)
+ unless defined?(@stat_method)
+ @stat_method = self.links == :manage ? :lstat : :stat
+ end
+
+ begin
+ return File.send(@stat_method, path)
+ rescue
+ # If this happens, it is almost surely because we're
+ # trying to manage a link to a file that does not exist.
+ return nil
+ end
+ end
+end