root 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History +3 -0
- data/MIT-LICENSE +22 -0
- data/README +14 -0
- data/lib/root.rb +268 -0
- data/lib/root/constant.rb +140 -0
- data/lib/root/constant_manifest.rb +126 -0
- data/lib/root/gems.rb +40 -0
- data/lib/root/intern.rb +36 -0
- data/lib/root/manifest.rb +168 -0
- data/lib/root/minimap.rb +88 -0
- data/lib/root/string_ext.rb +57 -0
- data/lib/root/utils.rb +391 -0
- data/lib/root/versions.rb +134 -0
- metadata +72 -0
data/History
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2009, Regents of the University of Colorado.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
= Root[http://tap.rubyforge.org/root]
|
2
|
+
|
3
|
+
== Installation
|
4
|
+
|
5
|
+
Root is available as a gem on RubyForge[http://rubyforge.org/projects/tap]. Use:
|
6
|
+
|
7
|
+
% gem install root
|
8
|
+
|
9
|
+
== Info
|
10
|
+
|
11
|
+
Copyright (c) 2009, Regents of the University of Colorado.
|
12
|
+
Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com], {Biomolecular Structure Program}[http://biomol.uchsc.edu/], {Hansen Lab}[http://hsc-proteomics.uchsc.edu/hansenlab/]
|
13
|
+
Support:: CU Denver School of Medicine Deans Academic Enrichment Fund
|
14
|
+
License:: {MIT-Style}[link:files/MIT-LICENSE.html]
|
data/lib/root.rb
ADDED
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'configurable'
|
2
|
+
require 'root/utils'
|
3
|
+
|
4
|
+
# Root allows you to define a root directory and alias relative paths, so
|
5
|
+
# that you can conceptualize what filepaths you need without predefining the
|
6
|
+
# full filepaths. Root also simplifies operations on filepaths.
|
7
|
+
#
|
8
|
+
# # define a root directory with aliased relative paths
|
9
|
+
# r = Root.new '/root_dir', :input => 'in', :output => 'out'
|
10
|
+
#
|
11
|
+
# # work with aliases
|
12
|
+
# r[:input] # => '/root_dir/in'
|
13
|
+
# r[:output] # => '/root_dir/out'
|
14
|
+
# r['implicit'] # => '/root_dir/implicit'
|
15
|
+
#
|
16
|
+
# # expanded paths are returned unchanged
|
17
|
+
# r[File.expand_path('expanded')] # => File.expand_path('expanded')
|
18
|
+
#
|
19
|
+
# # work with filepaths
|
20
|
+
# fp = r.filepath(:input, 'path/to/file.txt') # => '/root_dir/in/path/to/file.txt'
|
21
|
+
# r.relative_filepath(:input, fp) # => 'path/to/file.txt'
|
22
|
+
# r.translate(fp, :input, :output) # => '/root_dir/out/path/to/file.txt'
|
23
|
+
#
|
24
|
+
# # version filepaths
|
25
|
+
# r.version('path/to/config.yml', 1.0) # => 'path/to/config-1.0.yml'
|
26
|
+
# r.increment('path/to/config-1.0.yml', 0.1) # => 'path/to/config-1.1.yml'
|
27
|
+
# r.deversion('path/to/config-1.1.yml') # => ['path/to/config.yml', "1.1"]
|
28
|
+
#
|
29
|
+
# # absolute paths can also be aliased
|
30
|
+
# r[:abs, true] = "/absolute/path"
|
31
|
+
# r.filepath(:abs, "to", "file.txt") # => '/absolute/path/to/file.txt'
|
32
|
+
#
|
33
|
+
# By default, Roots are initialized to the present working directory
|
34
|
+
# (Dir.pwd).
|
35
|
+
#
|
36
|
+
#--
|
37
|
+
# === Implementation Notes
|
38
|
+
#
|
39
|
+
# Internally Root expands and stores all aliased paths in the 'paths' hash.
|
40
|
+
# Expanding paths ensures they remain constant even when the present working
|
41
|
+
# directory (Dir.pwd) changes.
|
42
|
+
#
|
43
|
+
# Root keeps a separate 'relative_paths' hash mapping aliases to their
|
44
|
+
# relative paths. This hash allow reassignment if and when the root directory
|
45
|
+
# changes. By contrast, there is no separate data structure storing the
|
46
|
+
# absolute paths. An absolute path thus has an alias in 'paths' but not
|
47
|
+
# 'relative_paths', whereas relative paths have aliases in both.
|
48
|
+
#
|
49
|
+
# These features may be important to note when subclassing Root:
|
50
|
+
# - root and all filepaths in 'paths' are expanded
|
51
|
+
# - relative paths are stored in 'relative_paths'
|
52
|
+
# - absolute paths are present in 'paths' but not in 'relative_paths'
|
53
|
+
#
|
54
|
+
class Root
|
55
|
+
include Configurable
|
56
|
+
include Utils
|
57
|
+
|
58
|
+
# The root directory.
|
59
|
+
config_attr(:root, '.', :writer => false)
|
60
|
+
|
61
|
+
# A hash of (alias, relative path) pairs for aliased paths relative
|
62
|
+
# to root.
|
63
|
+
config_attr(:relative_paths, {}, :writer => false)
|
64
|
+
|
65
|
+
# A hash of (alias, relative path) pairs for aliased absolute paths.
|
66
|
+
config_attr(:absolute_paths, {}, :reader => false, :writer => false)
|
67
|
+
|
68
|
+
# A hash of (alias, expanded path) pairs for expanded relative and
|
69
|
+
# absolute paths.
|
70
|
+
attr_reader :paths
|
71
|
+
|
72
|
+
# The filesystem root, inferred from self.root
|
73
|
+
# (ex '/' on *nix or something like 'C:/' on Windows).
|
74
|
+
attr_reader :path_root
|
75
|
+
|
76
|
+
# Creates a new Root with the given root directory, aliased relative paths
|
77
|
+
# and absolute paths. By default root is the present working directory
|
78
|
+
# and no aliased relative or absolute paths are specified.
|
79
|
+
def initialize(root=Dir.pwd, relative_paths={}, absolute_paths={})
|
80
|
+
assign_paths(root, relative_paths, absolute_paths)
|
81
|
+
@config = DelegateHash.new(self.class.configurations, {}, self)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Sets the root directory. All paths are reassigned accordingly.
|
85
|
+
def root=(path)
|
86
|
+
assign_paths(path, relative_paths, absolute_paths)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Sets the relative_paths to those provided. 'root' and :root are reserved
|
90
|
+
# aliases and cannot be set using this method (use root= instead).
|
91
|
+
#
|
92
|
+
# r = Root.new
|
93
|
+
# r['alt'] # => File.join(r.root, 'alt')
|
94
|
+
# r.relative_paths = {'alt' => 'dir'}
|
95
|
+
# r['alt'] # => File.join(r.root, 'dir')
|
96
|
+
#
|
97
|
+
def relative_paths=(paths)
|
98
|
+
paths = Validation::HASH[paths]
|
99
|
+
assign_paths(root, paths, absolute_paths)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Sets the absolute paths to those provided. 'root' and :root are reserved
|
103
|
+
# aliases and cannot be set using this method (use root= instead).
|
104
|
+
#
|
105
|
+
# r = Root.new
|
106
|
+
# r['abs'] # => File.join(r.root, 'abs')
|
107
|
+
# r.absolute_paths = {'abs' => '/path/to/dir'}
|
108
|
+
# r['abs'] # => '/path/to/dir'
|
109
|
+
#
|
110
|
+
def absolute_paths=(paths)
|
111
|
+
paths = Validation::HASH[paths]
|
112
|
+
assign_paths(root, relative_paths, paths)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns the absolute paths registered with self.
|
116
|
+
def absolute_paths
|
117
|
+
abs_paths = {}
|
118
|
+
paths.each do |als, path|
|
119
|
+
unless relative_paths.include?(als) || als.to_s == 'root'
|
120
|
+
abs_paths[als] = path
|
121
|
+
end
|
122
|
+
end
|
123
|
+
abs_paths
|
124
|
+
end
|
125
|
+
|
126
|
+
# Sets an alias for the path relative to the root directory. The aliases
|
127
|
+
# 'root' and :root cannot be set with this method (use root= instead).
|
128
|
+
# Absolute filepaths can be set using the second syntax.
|
129
|
+
#
|
130
|
+
# r = Root.new '/root_dir'
|
131
|
+
# r[:dir] = 'path/to/dir'
|
132
|
+
# r[:dir] # => '/root_dir/path/to/dir'
|
133
|
+
#
|
134
|
+
# r[:abs, true] = '/abs/path/to/dir'
|
135
|
+
# r[:abs] # => '/abs/path/to/dir'
|
136
|
+
#
|
137
|
+
#--
|
138
|
+
# Implementation Note:
|
139
|
+
#
|
140
|
+
# The syntax for setting an absolute filepath requires an odd use []=.
|
141
|
+
# In fact the method recieves the arguments (:dir, true, '/abs/path/to/dir')
|
142
|
+
# rather than (:dir, '/abs/path/to/dir', true), meaning that internally path
|
143
|
+
# and absolute are switched when setting an absolute filepath.
|
144
|
+
#
|
145
|
+
def []=(als, path, absolute=false)
|
146
|
+
raise ArgumentError, "the alias #{als.inspect} is reserved" if als.to_s == 'root'
|
147
|
+
|
148
|
+
# switch the paths if absolute was provided
|
149
|
+
unless absolute == false
|
150
|
+
switch = path
|
151
|
+
path = absolute
|
152
|
+
absolute = switch
|
153
|
+
end
|
154
|
+
|
155
|
+
case
|
156
|
+
when path.nil?
|
157
|
+
@relative_paths.delete(als)
|
158
|
+
@paths.delete(als)
|
159
|
+
when absolute
|
160
|
+
@relative_paths.delete(als)
|
161
|
+
@paths[als] = File.expand_path(path)
|
162
|
+
else
|
163
|
+
@relative_paths[als] = path
|
164
|
+
@paths[als] = File.expand_path(File.join(root, path))
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns the expanded path for the specified alias. If the alias has not
|
169
|
+
# been set, then the path is inferred to be 'root/als'. Expanded paths
|
170
|
+
# are returned directly.
|
171
|
+
#
|
172
|
+
# r = Root.new '/root_dir', :dir => 'path/to/dir'
|
173
|
+
# r[:dir] # => '/root_dir/path/to/dir'
|
174
|
+
#
|
175
|
+
# r.path_root # => '/'
|
176
|
+
# r['relative/path'] # => '/root_dir/relative/path'
|
177
|
+
# r['/expanded/path'] # => '/expanded/path'
|
178
|
+
#
|
179
|
+
def [](als)
|
180
|
+
path = self.paths[als]
|
181
|
+
return path unless path == nil
|
182
|
+
|
183
|
+
als = als.to_s
|
184
|
+
expanded?(als) ? als : File.expand_path(File.join(root, als))
|
185
|
+
end
|
186
|
+
|
187
|
+
# Resolves the specified alias, joins the paths together, and expands the
|
188
|
+
# resulting filepath.
|
189
|
+
def filepath(als, *paths)
|
190
|
+
File.expand_path(File.join(self[als], *paths))
|
191
|
+
end
|
192
|
+
|
193
|
+
# Retrieves the filepath relative to the path of the specified alias.
|
194
|
+
def relative_filepath(als, path)
|
195
|
+
super(self[als], path)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Same as filepath but raises an error if the result is not a subpath of
|
199
|
+
# the aliased directory.
|
200
|
+
def subpath(als, *paths)
|
201
|
+
dir = self[als]
|
202
|
+
path = filepath(als, *paths)
|
203
|
+
|
204
|
+
if path.rindex(dir, 0) != 0
|
205
|
+
raise "not a subpath: #{path} (#{dir})"
|
206
|
+
end
|
207
|
+
|
208
|
+
path
|
209
|
+
end
|
210
|
+
|
211
|
+
# Generates a filepath translated from the aliased source dir to the
|
212
|
+
# aliased target dir. Raises an error if the filepath is not relative
|
213
|
+
# to the source dir.
|
214
|
+
#
|
215
|
+
# r = Root.new '/root_dir'
|
216
|
+
# path = r.filepath(:in, 'path/to/file.txt') # => '/root_dir/in/path/to/file.txt'
|
217
|
+
# r.translate(path, :in, :out) # => '/root_dir/out/path/to/file.txt'
|
218
|
+
#
|
219
|
+
def translate(path, source_als, target_als)
|
220
|
+
super(path, self[source_als], self[target_als])
|
221
|
+
end
|
222
|
+
|
223
|
+
# Lists all files along the aliased path matching the input patterns.
|
224
|
+
# Patterns should join with the aliased path make valid globs for
|
225
|
+
# Dir.glob. If no patterns are specified, glob returns all paths
|
226
|
+
# matching 'als/**/*'.
|
227
|
+
def glob(als, *patterns)
|
228
|
+
patterns << "**/*" if patterns.empty?
|
229
|
+
patterns.collect! {|pattern| filepath(als, pattern)}
|
230
|
+
super(*patterns)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Lists all versions of path in the aliased dir matching the version
|
234
|
+
# patterns. If no patterns are specified, then all versions of path
|
235
|
+
# will be returned.
|
236
|
+
def vglob(als, path, *vpatterns)
|
237
|
+
super(filepath(als, path), *vpatterns)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Changes pwd to the specified directory using Root.chdir.
|
241
|
+
def chdir(als, mkdir=false, &block)
|
242
|
+
super(self[als], mkdir, &block)
|
243
|
+
end
|
244
|
+
|
245
|
+
# Constructs a path from the inputs (using filepath) and prepares it using
|
246
|
+
# Root.prepare. Returns the path.
|
247
|
+
def prepare(als, *paths, &block)
|
248
|
+
super(filepath(als, *paths), &block)
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
# reassigns all paths with the input root, relative_paths, and absolute_paths
|
254
|
+
def assign_paths(root, relative_paths, absolute_paths)
|
255
|
+
@root = File.expand_path(root)
|
256
|
+
@relative_paths = {}
|
257
|
+
@paths = {'root' => @root, :root => @root}
|
258
|
+
|
259
|
+
@path_root = File.dirname(@root)
|
260
|
+
while @path_root != (parent = File.dirname(@path_root))
|
261
|
+
@path_root = parent
|
262
|
+
end
|
263
|
+
|
264
|
+
relative_paths.each_pair {|dir, path| self[dir] = path }
|
265
|
+
absolute_paths.each_pair {|dir, path| self[dir, true] = path }
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'root/string_ext'
|
2
|
+
|
3
|
+
class Root
|
4
|
+
|
5
|
+
# A Constant serves as a placeholder for an actual constant, sort of like
|
6
|
+
# autoload. Use the constantize method to retrieve the actual constant; if
|
7
|
+
# it doesn't exist, constantize requires require_path and tries again.
|
8
|
+
#
|
9
|
+
# Object.const_defined?(:Net) # => false
|
10
|
+
# $".include?('net/http') # => false
|
11
|
+
#
|
12
|
+
# http = Constant.new('Net::HTTP', 'net/http')
|
13
|
+
# http.constantize # => Net::HTTP
|
14
|
+
# $".include?('net/http') # => true
|
15
|
+
#
|
16
|
+
class Constant
|
17
|
+
class << self
|
18
|
+
|
19
|
+
# Tries to find a declared constant under base with the specified
|
20
|
+
# const_name. When a constant is missing, constantize yields the
|
21
|
+
# current base and any non-existant constant names the block, if given,
|
22
|
+
# or raises a NameError. The block is expected to return the desired
|
23
|
+
# constant; in the example 'Non::Existant' is effectively mapping to
|
24
|
+
# ConstName.
|
25
|
+
#
|
26
|
+
# module ConstName; end
|
27
|
+
#
|
28
|
+
# Constant.constantize('ConstName') # => ConstName
|
29
|
+
# Constant.constantize('Non::Existant') { ConstName } # => ConstName
|
30
|
+
#
|
31
|
+
def constantize(const_name, base=Object) # :yields: base, missing_const_names
|
32
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ const_name
|
33
|
+
raise NameError, "#{const_name.inspect} is not a valid constant name!"
|
34
|
+
end
|
35
|
+
|
36
|
+
constants = $1.split(/::/)
|
37
|
+
while !constants.empty?
|
38
|
+
unless const_is_defined?(base, constants[0])
|
39
|
+
if block_given?
|
40
|
+
return yield(base, constants)
|
41
|
+
else
|
42
|
+
raise NameError.new("uninitialized constant #{const_name}", constants[0])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
base = base.const_get(constants.shift)
|
46
|
+
end
|
47
|
+
base
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# helper method. Determines if a constant named name is defined in
|
53
|
+
# const. The implementation (annoyingly) has to be different for ruby
|
54
|
+
# 1.9 due to changes in the API.
|
55
|
+
case RUBY_VERSION
|
56
|
+
when /^1.9/
|
57
|
+
def const_is_defined?(const, name) # :nodoc:
|
58
|
+
const.const_defined?(name, false)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
def const_is_defined?(const, name) # :nodoc:
|
62
|
+
const.const_defined?(name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# The constant name
|
68
|
+
attr_reader :name
|
69
|
+
|
70
|
+
# The path to load to initialize a missing constant
|
71
|
+
attr_reader :require_path
|
72
|
+
|
73
|
+
# Initializes a new Constant with the specified constant name and
|
74
|
+
# require_path. The name should be a valid constant name.
|
75
|
+
def initialize(name, require_path=nil)
|
76
|
+
@name = name
|
77
|
+
@require_path = require_path
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the underscored name.
|
81
|
+
def path
|
82
|
+
@path ||= name.underscore
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns the basename of path.
|
86
|
+
def basename
|
87
|
+
@basename ||= File.basename(path)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns the path, minus the basename of path.
|
91
|
+
def dirname
|
92
|
+
@dirname ||= (dirname = File.dirname(path)) == "." ? "" : dirname
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the name of the constant, minus nesting.
|
96
|
+
def const_name
|
97
|
+
@const_name ||= (name =~ /.*::(.*)$/ ? $1 : name)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the nesting constants of name.
|
101
|
+
def nesting
|
102
|
+
@nesting ||= (name =~ /(.*)::.*$/ ? $1 : '')
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns the number of constants in nesting.
|
106
|
+
def nesting_depth
|
107
|
+
@nesting_depth ||= nesting.split(/::/).length
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns the Lazydoc document for require_path.
|
111
|
+
def document
|
112
|
+
require_path ? Lazydoc[require_path] : nil
|
113
|
+
end
|
114
|
+
|
115
|
+
# True if another is a Constant with the same name
|
116
|
+
# and require_path as self.
|
117
|
+
def ==(another)
|
118
|
+
another.kind_of?(Constant) &&
|
119
|
+
another.name == self.name &&
|
120
|
+
another.require_path == self.require_path
|
121
|
+
end
|
122
|
+
|
123
|
+
# Looks up and returns the constant indicated by name. If the constant
|
124
|
+
# cannot be found, constantize requires require_path and tries again.
|
125
|
+
#
|
126
|
+
# Raises a NameError if the constant cannot be found.
|
127
|
+
def constantize
|
128
|
+
Constant.constantize(name) do
|
129
|
+
require require_path if require_path
|
130
|
+
Constant.constantize(name)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a string like:
|
135
|
+
# "#<Root::Constant:object_id Const::Name (require_path)>"
|
136
|
+
def inspect
|
137
|
+
"#<#{self.class}:#{object_id} #{name}#{@require_path == nil ? "" : " (#{@require_path})"}>"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|