Class: Arrow::Path

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Constants, Loggable, Enumerable
Defined in:
lib/arrow/path.rb

Overview

The Arrow::Path class, which represents a collection of paths to search for various resources. Instances of this class are used to search for templates, applets, and other resources loaded by the server from a configured list of directories.

Synopsis

require 'arrow/path'

# Constructed from a String with PATH_SEPARATOR characters:
template_path = Arrow::Path.new( ".:/www/templates:/usr/local/www/templates" )

# ...or from an Array of Strings
template_path = Arrow::Path.new([ '.', '/www/templates', '/usr/local/www/templates' ])

# Return only those paths that exist, are directories, are readable
# by the current user, and are not world-writable. This will use a
# cached value if it has been built within
# Arrow::Path::DEFAULT_CACHE_LIFESPAN seconds of the last fetch.
paths = template_path.valid_dirs

# Fetch without caching
template_path.find_valid_dirs

# ...or turn caching off and fetch
template_path.cache_lifespan = 0
paths = template_path.valid_dirs

Authors

Please see the file LICENSE in the top-level directory for licensing details.

Constant Summary collapse

SEPARATOR =

The character to split path Strings on, and join on when converting back to a String.

File::PATH_SEPARATOR
DEFAULT_CACHE_LIFESPAN =

How many seconds to cache directory stat information, in seconds.

1.5

Constants included from Constants

Constants::HTML_MIMETYPE, Constants::RUBY_MARSHALLED_MIMETYPE, Constants::RUBY_OBJECT_MIMETYPE, Constants::XHTML_MIMETYPE, Constants::YAML_DOMAIN

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path = [], cache_lifespan = DEFAULT_CACHE_LIFESPAN) ⇒ Path

Create a new Arrow::Path object for the specified path, which can be either a String containing directory names separated by File::PATH_SEPARATOR, an Array of directory names, or an object which returns such an Array when #to_a is called on it. If cache_lifespan is non-zero, the Array of valid directories will be cached for cache_lifespan seconds to save calls to stat().



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/arrow/path.rb', line 83

def initialize( path=[], cache_lifespan=DEFAULT_CACHE_LIFESPAN )
	@dirs = case path
			when Array
				path.flatten
			when String
				path.split(SEPARATOR)
			else
				path.to_a.flatten
			end

	@dirs.collect! {|dir| dir.untaint.to_s }

	@valid_dirs = []
	@cache_lifespan = cache_lifespan
	@last_stat = Time.at(0)
end

Instance Attribute Details

#cache_lifespanObject

How long (in seconds) to cache the list of good directories. Setting this to 0 turns off caching.



111
112
113
# File 'lib/arrow/path.rb', line 111

def cache_lifespan
  @cache_lifespan
end

#dirsObject

The raw list of directories contained in the path, including invalid (non-existent or unreadable) ones.



107
108
109
# File 'lib/arrow/path.rb', line 107

def dirs
  @dirs
end

Class Method Details

.to_yaml_typeObject

Return the YAML type for this class



68
69
70
# File 'lib/arrow/path.rb', line 68

def self::to_yaml_type
	"!%s/arrowPath" % [ Arrow::Constants::YAML_DOMAIN ]
end

Instance Method Details

#each(&block) ⇒ Object

Enumerable interface method. Iterate over the list of valid dirs in this path, calling the specified block for each.



173
174
175
# File 'lib/arrow/path.rb', line 173

def each( &block )
	self.valid_dirs.each( &block )
end

#find_valid_dirsObject

Fetch the list of paths in the search path, vetted to only contain those that are not tainted, exist, are directories, are readable by the current user, and are not world-writable.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/arrow/path.rb', line 136

def find_valid_dirs
	return @dirs.find_all do |dir|
		if dir.tainted?
			self.log.info "Discarding tainted directory entry %p" % [ dir ]
			next
		end

		path = Pathname.new( dir )

		if ! path.exist?
			self.log.debug "Discarding non-existant path: %s" % [ path ]
			next false
		elsif ! path.directory?
			self.log.debug "Discarding non-directory: %s" % [ path ]
			next false
		elsif ! path.readable?
			self.log.debug "Discarding unreadable directory: %s" % [ path ]
			next false
		elsif( (path.stat.mode & 0002).nonzero? )
			self.log.debug "Discarding world-writable directory: %s" % [ path ]
			next false
		end
		
		true
	end.map {|pn| pn.to_s }
end

#to_sObject

Return the path as a SEPARATOR-separated String.



179
180
181
# File 'lib/arrow/path.rb', line 179

def to_s
	return self.valid_dirs.join( SEPARATOR )
end

#to_yaml(opts = {}) ⇒ Object

Return the path as YAML text



185
186
187
188
189
190
191
192
# File 'lib/arrow/path.rb', line 185

def to_yaml( opts={} )
	require 'yaml'
	YAML.quick_emit( self.object_id, opts ) {|out|
		out.seq( self.class.to_yaml_type ){|seq|
			seq.add( self.dirs )
		}
	}
end

#valid_dirsObject

Fetch the list of valid directories, using a cached value if the path has caching enabled (which is the default). Otherwise, it fetches the valid list via #find_valid_dirs and caches the result for #cache_lifespan seconds. If caching is disabled, this is equivalent to just calling #find_valid_dirs.



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/arrow/path.rb', line 119

def valid_dirs
	if ( @cache_lifespan.nonzero? &&
		 ((Time.now - @last_stat) < @cache_lifespan) )
		self.log.debug "Returning cached dirs."
		return @valid_dirs
	end

	@valid_dirs = self.find_valid_dirs
	@last_stat = Time.now

	return @valid_dirs
end