require 'pathname'
module IMW
# Implements methods designed to work with an object's
# @paths attributes, adding and deleting symbolic
# references to paths and expanding calls to +path_to+ from that
# attribute or (when a miss) from IMW::PATHS.
#
# An including class should therefore define an array attribute
# @paths.
module Paths
# Expands a shorthand workflow path specification to an actual
# file path. Strings are interpreted literally but symbols are
# first resolved to the paths they represent.
#
# add_path :foo, '~/whoa'
# path_to :foo, 'my_thing'
# => '~/whoa/my_thing'
#
# @param [String, Symbol] pathsegs the path segments to join
# @return [String] the resulting expanded path
def path_to *pathsegs
path = Pathname.new path_to_helper(*pathsegs)
path.absolute? ? File.expand_path(path) : path.to_s
end
# Return the presently defined paths for this object.
#
# @return [Hash]
def paths
@paths ||= {}
end
# Adds a symbolic path for expansion by +path_to+.
#
# add_path :foo, '~/whoa'
# add_path :bar, :foo, 'baz'
# path_to :bar
# => '~/whoa/baz'
#
# @param [Symbol] sym the name of the path to store
# @param [Symbol, String] pathsegs the path segments to use to define the path to the name
# @return [String] the resulting path
def add_path sym, *pathsegs
paths[sym] = pathsegs.flatten
path_to(sym)
end
# Removes a symbolic path for expansion by +path_to+.
#
# @param [Symbol] sym the stored path symbol to remove
def remove_path sym
paths.delete sym if paths.include? sym
end
private
def path_to_helper *pathsegs # :nodoc:
# +path_to_helper+ handles the recursive calls for +path_to+.
expanded = pathsegs.flatten.compact.map do |pathseg|
case
when pathseg.is_a?(Symbol) && paths.include?(pathseg) then path_to(paths[pathseg])
when pathseg.is_a?(Symbol) && IMW::PATHS.include?(pathseg) then path_to(IMW::PATHS[pathseg])
when pathseg.is_a?(Symbol) then raise IMW::PathError.new("No path expansion set for #{pathseg.inspect}")
else pathseg
end
end
File.join(*expanded)
end
end
# Default paths for the IMW. Chosen to make sense on most *NIX
# distributions.
DEFAULT_PATHS = {
:home => ENV['HOME'],
:data_root => "/var/lib/imw",
:log_root => "/var/log/imw",
:scripts_root => "/usr/share/imw",
:tmp_root => "/tmp/imw",
# the imw library
:imw_root => File.expand_path(File.dirname(__FILE__) + "/../../.."),
:imw_bin => [:imw_root, 'bin'],
:imw_etc => [:imw_root, 'etc'],
:imw_lib => [:imw_root, 'lib'],
# workflow
:ripd_root => [:data_root, 'ripd'],
:rawd_root => [:data_root, 'rawd'],
:fixd_root => [:data_root, 'fixd'],
:pkgd_root => [:data_root, 'pkgd']
}
defined?(PATHS) ? PATHS.reverse_merge!(DEFAULT_PATHS) : PATHS = DEFAULT_PATHS
# Expands a shorthand workflow path specification to an actual
# file path. Strings are interpreted literally but symbols are
# first resolved to the paths they represent.
#
# IMW.add_path :foo, '~/whoa'
# IMW.path_to :foo, 'my_thing'
# => '~/whoa/my_thing'
#
# @param [String, Symbol] pathsegs the path segments to join
# @return [String] the resulting expanded path
def self.path_to *pathsegs
path = Pathname.new IMW.path_to_helper(*pathsegs)
path.absolute? ? File.expand_path(path) : path.to_s
end
# Adds a symbolic path for expansion by +path_to+.
#
# IMW.add_path :foo, '~/whoa'
# IMW.add_path :bar, :foo, 'baz'
# IMW.path_to :bar
# => '~/whoa/baz'
#
# @param [Symbol] sym the name of the path to store
# @param [Symbol, String] pathsegs the path segments to use to define the path to the name
# @return [String] the resulting path
def self.add_path sym, *pathsegs
IMW::PATHS[sym] = pathsegs.flatten
path_to[sym]
end
# Removes a symbolic path for expansion by +path_to+.
#
# @param [Symbol] sym the stored path symbol to remove
def self.remove_path sym
IMW::PATHS.delete sym if IMW::PATHS.include? sym
end
protected
def self.path_to_helper *pathsegs # :nodoc:
# +path_to_helper+ handles the recursive calls for +path_to+.
expanded = pathsegs.flatten.compact.map do |pathseg|
case
when pathseg.is_a?(Symbol) && IMW::PATHS.include?(pathseg) then path_to(IMW::PATHS[pathseg])
when pathseg.is_a?(Symbol) then raise IMW::PathError.new("No path expansion set for #{pathseg.inspect}")
else pathseg
end
end
File.join(*expanded)
end
end