-
Notifications
You must be signed in to change notification settings - Fork 5.4k
/
Copy pathdependency_command.rb
206 lines (158 loc) · 5.08 KB
/
dependency_command.rb
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# frozen_string_literal: true
require_relative "../command"
require_relative "../local_remote_options"
require_relative "../version_option"
class Gem::Commands::DependencyCommand < Gem::Command
include Gem::LocalRemoteOptions
include Gem::VersionOption
def initialize
super "dependency",
"Show the dependencies of an installed gem",
version: Gem::Requirement.default, domain: :local
add_version_option
add_platform_option
add_prerelease_option
add_option("-R", "--[no-]reverse-dependencies",
"Include reverse dependencies in the output") do |value, options|
options[:reverse_dependencies] = value
end
add_option("-p", "--pipe",
"Pipe Format (name --version ver)") do |value, options|
options[:pipe_format] = value
end
add_local_remote_options
end
def arguments # :nodoc:
"REGEXP show dependencies for gems whose names start with REGEXP"
end
def defaults_str # :nodoc:
"--local --version '#{Gem::Requirement.default}' --no-reverse-dependencies"
end
def description # :nodoc:
<<-EOF
The dependency commands lists which other gems a given gem depends on. For
local gems only the reverse dependencies can be shown (which gems depend on
the named gem).
The dependency list can be displayed in a format suitable for piping for
use with other commands.
EOF
end
def usage # :nodoc:
"#{program_name} REGEXP"
end
def fetch_remote_specs(name, requirement, prerelease) # :nodoc:
fetcher = Gem::SpecFetcher.fetcher
specs_type = prerelease ? :complete : :released
ss = if name.nil?
fetcher.detect(specs_type) { true }
else
fetcher.detect(specs_type) do |name_tuple|
name === name_tuple.name && requirement.satisfied_by?(name_tuple.version)
end
end
ss.map {|tuple, source| source.fetch_spec(tuple) }
end
def fetch_specs(name_pattern, requirement, prerelease) # :nodoc:
specs = []
if local?
specs.concat Gem::Specification.stubs.find_all {|spec|
name_matches = name_pattern ? name_pattern =~ spec.name : true
version_matches = requirement.satisfied_by?(spec.version)
name_matches && version_matches
}.map(&:to_spec)
end
specs.concat fetch_remote_specs name_pattern, requirement, prerelease if remote?
ensure_specs specs
specs.uniq.sort
end
def display_pipe(specs) # :nodoc:
specs.each do |spec|
next if spec.dependencies.empty?
spec.dependencies.sort_by(&:name).each do |dep|
say "#{dep.name} --version '#{dep.requirement}'"
end
end
end
def display_readable(specs, reverse) # :nodoc:
response = String.new
specs.each do |spec|
response << print_dependencies(spec)
unless reverse[spec.full_name].empty?
response << " Used by\n"
reverse[spec.full_name].each do |sp, dep|
response << " #{sp} (#{dep})\n"
end
end
response << "\n"
end
say response
end
def execute
ensure_local_only_reverse_dependencies
pattern = name_pattern options[:args]
requirement = Gem::Requirement.new options[:version]
specs = fetch_specs pattern, requirement, options[:prerelease]
reverse = reverse_dependencies specs
if options[:pipe_format]
display_pipe specs
else
display_readable specs, reverse
end
end
def ensure_local_only_reverse_dependencies # :nodoc:
if options[:reverse_dependencies] && remote? && !local?
alert_error "Only reverse dependencies for local gems are supported."
terminate_interaction 1
end
end
def ensure_specs(specs) # :nodoc:
return unless specs.empty?
patterns = options[:args].join ","
say "No gems found matching #{patterns} (#{options[:version]})" if
Gem.configuration.verbose
terminate_interaction 1
end
def print_dependencies(spec, level = 0) # :nodoc:
response = String.new
response << " " * level + "Gem #{spec.full_name}\n"
unless spec.dependencies.empty?
spec.dependencies.sort_by(&:name).each do |dep|
response << " " * level + " #{dep}\n"
end
end
response
end
def reverse_dependencies(specs) # :nodoc:
reverse = Hash.new {|h, k| h[k] = [] }
return reverse unless options[:reverse_dependencies]
specs.each do |spec|
reverse[spec.full_name] = find_reverse_dependencies spec
end
reverse
end
##
# Returns an Array of [specification, dep] that are satisfied by +spec+.
def find_reverse_dependencies(spec) # :nodoc:
result = []
Gem::Specification.each do |sp|
sp.dependencies.each do |dep|
dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep
if spec.name == dep.name &&
dep.requirement.satisfied_by?(spec.version)
result << [sp.full_name, dep]
end
end
end
result
end
private
def name_pattern(args)
return if args.empty?
if args.length == 1 && args.first =~ /\A(.*)(i)?\z/m
flags = $2 ? Regexp::IGNORECASE : nil
Regexp.new $1, flags
else
/\A#{Regexp.union(*args)}/
end
end
end