1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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
|
######################################################################
# This file is imported from the rubygems project.
# DO NOT make modifications in this repo. They _will_ be reverted!
# File a patch instead and assign it to Ryan Davis or Eric Hodel.
######################################################################
##
# GemPathSearcher has the capability to find loadable files inside
# gems. It generates data up front to speed up searches later.
class Gem::GemPathSearcher
##
# Initialise the data we need to make searches later.
def initialize
# We want a record of all the installed gemspecs, in the order we wish to
# examine them.
# TODO: remove this stupid method
@gemspecs = init_gemspecs
# Map gem spec to glob of full require_path directories. Preparing this
# information may speed up searches later.
@lib_dirs = {}
@gemspecs.each do |spec|
@lib_dirs[spec.object_id] = lib_dirs_for spec
end
end
##
# Look in all the installed gems until a matching +glob+ is found.
# Return the _gemspec_ of the gem where it was found. If no match
# is found, return nil.
#
# The gems are searched in alphabetical order, and in reverse
# version order.
#
# For example:
#
# find('log4r') # -> (log4r-1.1 spec)
# find('log4r.rb') # -> (log4r-1.1 spec)
# find('rake/rdoctask') # -> (rake-0.4.12 spec)
# find('foobarbaz') # -> nil
#
# Matching paths can have various suffixes ('.rb', '.so', and
# others), which may or may not already be attached to _file_.
# This method doesn't care about the full filename that matches;
# only that there is a match.
def find(glob)
# HACK violation of encapsulation
@gemspecs.find do |spec|
# TODO: inverted responsibility
matching_file? spec, glob
end
end
def find_active(glob)
# HACK violation of encapsulation
@gemspecs.find do |spec|
# TODO: inverted responsibility
spec.loaded? and matching_file? spec, glob
end
end
##
# Works like #find, but finds all gemspecs matching +glob+.
def find_all(glob)
# HACK violation of encapsulation
@gemspecs.select do |spec|
# TODO: inverted responsibility
matching_file? spec, glob
end || []
end
def find_in_unresolved(glob)
# HACK violation
specs = Gem.unresolved_deps.values.map { |dep|
Gem.source_index.search dep, true
}.flatten
specs.select do |spec|
# TODO: inverted responsibility
matching_file? spec, glob
end || []
end
def find_in_unresolved_tree glob
# HACK violation
# TODO: inverted responsibility
specs = Gem.unresolved_deps.values.map { |dep|
Gem.source_index.search dep, true
}.flatten
specs.reverse_each do |spec|
trails = matching_paths(spec, glob)
next if trails.empty?
return trails.map(&:reverse).sort.first.reverse
end
[]
end
##
# Attempts to find a matching path using the require_paths of the given
# +spec+.
def matching_file?(spec, path)
not matching_files(spec, path).empty?
end
def matching_paths(spec, path)
trails = []
spec.traverse do |from_spec, dep, to_spec, trail|
next unless to_spec.conflicts.empty?
trails << trail unless matching_files(to_spec, path).empty?
end
trails
end
##
# Returns files matching +path+ in +spec+.
#--
# Some of the intermediate results are cached in @lib_dirs for speed.
def matching_files(spec, path)
return [] unless @lib_dirs[spec.object_id] # case no paths
glob = File.join @lib_dirs[spec.object_id], "#{path}#{Gem.suffix_pattern}"
Dir[glob].select { |f| File.file? f.untaint }
end
##
# Return a list of all installed gemspecs, sorted by alphabetical order and
# in reverse version order. (bar-2, bar-1, foo-2)
def init_gemspecs
specs = Gem.source_index.map { |_, spec| spec }
specs.sort { |a, b|
names = a.name <=> b.name
next names if names.nonzero?
b.version <=> a.version
}
end
##
# Returns library directories glob for a gemspec. For example,
# '/usr/local/lib/ruby/gems/1.8/gems/foobar-1.0/{lib,ext}'
def lib_dirs_for(spec)
"#{spec.full_gem_path}/{#{spec.require_paths.join(',')}}" if
spec.require_paths
end
end
|