class Fig::WorkingDirectoryMaintainer
Copies files from the project directories in FIG_HOME to the user's working directory. It keeps track of which files have already been copied, and which package/versions they came from, and deletes files as necessary to ensure that we never have files from two different versions of the same package in the user's working directory.
Constants
- SYMLOOP_MAX
Public Class Methods
new(base_dir)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 20 def initialize(base_dir) @base_dir = base_dir @package_metadata_by_name = {} @local_fig_data_directory = File.join(@base_dir, '.fig') if ( File.exist?(@local_fig_data_directory) \ && ! File.directory?(@local_fig_data_directory) ) raise Fig::UserInputError.new( %Q<"#{@local_fig_data_directory}" exists and it isn't a directory. Are you running inside a repository?> ) end @metadata_file = File.join(@local_fig_data_directory, 'retrieve') if File.exist?(@metadata_file) load_metadata() end end
Public Instance Methods
find_package_version_for_file(file)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 85 def find_package_version_for_file(file) @package_metadata_by_name.each do |name, package_meta| package_meta.each_file do |target| if File.identical? file, target return formatted_meta(package_meta) end end end return nil end
prepare_for_shutdown(purged_unused_packages)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 97 def prepare_for_shutdown(purged_unused_packages) if purged_unused_packages clean_up_unused_packages() end save_metadata() return end
retrieve(source, relpath)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 56 def retrieve(source, relpath) resolved_source = source # When recursing through a retrieve, if we encounter a symlink that doesn't # point to a directory, we copy it as a symlink. However, if the retrieve # path itself is a symlink, we copy the target of the symlink, not the # symlink itself. if File.exist? resolved_source # Ruby v1.8 does not have File.realdirpath(). traversal_count = 0 while File.symlink? resolved_source resolved_source = File.join( File.dirname(resolved_source), File.readlink(resolved_source) ) traversal_count += 1 if traversal_count > SYMLOOP_MAX raise Fig::RepositoryError.new( %Q<Could not resolve symlink "#{source}"; symlink chain exceeded #{SYMLOOP_MAX}.> ) end end end copy(resolved_source, relpath) return end
switch_to_package_version(name, version)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 41 def switch_to_package_version(name, version) @package_meta = @package_metadata_by_name[name] if @package_meta && @package_meta.current_version != version clean_up_package_files() @package_meta = nil end if not @package_meta @package_meta = reset_package_metadata_with_version(name, version) end return end
Private Instance Methods
clean_up_package_files(package_meta = @package_meta)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 257 def clean_up_package_files(package_meta = @package_meta) package_meta.each_file do |relpath| Fig::Logging.info( Fig::Logging::Colorizable.new( "- [#{formatted_meta(package_meta)}] #{relpath}", :magenta, nil ) ) FileUtils.rm_f(File.join(@base_dir, relpath)) end return end
clean_up_unused_packages()
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 272 def clean_up_unused_packages() @package_metadata_by_name.each_value do |metadata| if not metadata.retrieved? clean_up_package_files(metadata) metadata.reset_with_version(nil) end end return end
copy(source, relpath)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 158 def copy(source, relpath) target = File.join(@base_dir, relpath) if source_and_target_are_same?(source, target) # Actually happened: Retrieve and "set" both set to ".". Victim's current # directory included a ".git" directory. Update was done and then later, # an update with different dependencies. Fig proceeded to delete all # files that had previously existed in the current directory, including # out of the git repo. Whoops. Fig::Logging.warn %Q<Skipping copying "#{source}" to itself.> return end if File.directory?(source) copy_directory(source, relpath, target) else copy_file(source, relpath, target) end return end
copy_directory(source, relpath, target)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 189 def copy_directory(source, relpath, target) FileUtils.mkdir_p(target) Fig::Logging.debug "Copying directory #{source} to #{target}." Dir.foreach(source) do |child| if child != '.' and child != '..' source_file = File.join(source, child) target_file = File.join(relpath, child) copy(source_file, target_file) end end return end
copy_file(source, relpath, target)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 204 def copy_file(source, relpath, target) if should_copy_file?(source, target) if Fig::Logging.debug? Fig::Logging.debug \ "Copying file from #{source} to #{target}." else Fig::Logging.info( Fig::Logging::Colorizable.new( "+ [#{formatted_meta()}] #{relpath}", :green, nil ) ) end FileUtils.mkdir_p(File.dirname(target)) # If the source is a dangling symlink, then there's no time, etc. to # preserve. preserve = File.exist?(source) && ! File.symlink?(source) if File.exist?(target) Fig::Logging.info("Overwriting #{target}.") end FileUtils.copy_entry( source, target, preserve, false, :remove_destination ) end if @package_meta @package_meta.add_file(relpath) @package_meta.mark_as_retrieved() end return end
formatted_meta(package_meta = @package_meta)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 298 def formatted_meta(package_meta = @package_meta) return Fig::PackageDescriptor.format( package_meta.package_name, package_meta.current_version, nil ) end
load_metadata()
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 109 def load_metadata() file = nil begin file = File.open(@metadata_file) file.each_line do |line| line.strip!() if line =~ /^(.+)=(.+)\/(.+)$/ target = $1 package_name = $2 package_version = $3 package_meta = @package_metadata_by_name[package_name] if package_meta if package_meta.current_version != package_version raise "Version mismatch for #{package_meta.package_name} in #{@metadata_file}." end else package_meta = reset_package_metadata_with_version(package_name, package_version) end package_meta.add_file(target) else raise "parse error in #{@metadata_file}:#{file.lineno}: #{line}" end end ensure if not file.nil? file.close end end return end
reset_package_metadata_with_version(name, version)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 146 def reset_package_metadata_with_version(name, version) metadata = @package_metadata_by_name[name] if not metadata metadata = Fig::WorkingDirectoryMetadata.new(name, version) @package_metadata_by_name[name] = metadata else metadata.reset_with_version(version) end return metadata end
save_metadata()
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 285 def save_metadata() FileUtils.mkdir_p(@local_fig_data_directory) File.open(@metadata_file, 'w') do |file| @package_metadata_by_name.each do |name, package_meta| package_meta.each_file do |target| file << target << '=' << formatted_meta(package_meta) << "\n" end end end return end
should_copy_file?(source, target)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 241 def should_copy_file?(source, target) if File.symlink?(target) if File.symlink?(source) && File.readlink(source) == File.readlink(target) return false end Fig::Logging.info("Removing symbolic link #{target}.") FileUtils.rm(target) return true end return true if ! File.exist?(target) return File.mtime(source) > File.mtime(target) end
source_and_target_are_same?(source, target)
click to toggle source
# File lib/fig/working_directory_maintainer.rb, line 180 def source_and_target_are_same?(source, target) # Ruby 1.8 doesn't have File.absolute_path(), so we have to fall back to # .expand_path(). source_absolute = File.expand_path(source) target_absolute = File.expand_path(target) return source_absolute == target_absolute end