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
|
# The Formulary is responsible for creating instances of Formula.
class Formulary
def self.unload_formula formula_name
Object.send(:remove_const, Formula.class_s(formula_name))
end
def self.formula_class_defined? formula_name
Object.const_defined?(Formula.class_s(formula_name))
end
def self.get_formula_class formula_name
Object.const_get(Formula.class_s(formula_name))
end
def self.restore_formula formula_name, value
old_verbose, $VERBOSE = $VERBOSE, nil
Object.const_set(Formula.class_s(formula_name), value)
ensure
$VERBOSE = old_verbose
end
# A FormulaLoader returns instances of formulae.
# Subclasses implement loaders for particular sources of formulae.
class FormulaLoader
# The formula's name
attr_reader :name
# The formula's ruby file's path or filename
attr_reader :path
# Gets the formula instance.
# Subclasses must define this.
def get_formula; end
# Return the Class for this formula, `require`-ing it if
# it has not been parsed before.
def klass
begin
have_klass = Formulary.formula_class_defined? name
rescue NameError
raise FormulaUnavailableError.new(name)
end
unless have_klass
puts "#{$0}: loading #{path}" if ARGV.debug?
begin
require path.to_s
rescue NoMethodError
# This is a programming error in an existing formula, and should not
# have a "no such formula" message.
raise
rescue LoadError, NameError
raise if ARGV.debug? # let's see the REAL error
raise FormulaUnavailableError.new(name)
end
end
klass = Formulary.get_formula_class(name)
if (klass == Formula) || !klass.ancestors.include?(Formula)
raise FormulaUnavailableError.new(name)
end
klass
end
end
# Loads formulae from bottles.
class BottleLoader < FormulaLoader
def initialize bottle_name
@bottle_filename = Pathname(bottle_name).realpath
name_without_version = bottle_filename_formula_name @bottle_filename
if name_without_version.empty?
if ARGV.homebrew_developer?
opoo "Add a new regex to bottle_version.rb to parse this filename."
end
@name = bottle_name
else
@name = name_without_version
end
@path = Formula.path(@name)
end
def get_formula
formula = klass.new(name)
formula.local_bottle_path = @bottle_filename
return formula
end
end
# Loads formulae from Homebrew's provided Library
class StandardLoader < FormulaLoader
def initialize name
@name = name
@path = Formula.path(name)
end
def get_formula
return klass.new(name)
end
end
# Loads formulae from disk using a path
class FromPathLoader < FormulaLoader
def initialize path
# require allows filenames to drop the .rb extension, but everything else
# in our codebase will require an exact and fullpath.
path = "#{path}.rb" unless path =~ /\.rb$/
@path = Pathname.new(path)
@name = @path.stem
end
def get_formula
klass.new(name, path.to_s)
end
end
# Loads formulae from URLs
class FromUrlLoader < FormulaLoader
attr_reader :url
def initialize url
@url = url
@path = HOMEBREW_CACHE_FORMULA/File.basename(url)
@name = File.basename(url, '.rb')
end
# Downloads the formula's .rb file
def fetch
unless Formulary.formula_class_defined? name
HOMEBREW_CACHE_FORMULA.mkpath
FileUtils.rm path.to_s, :force => true
curl url, '-o', path.to_s
end
end
def get_formula
return klass.new(name, path.to_s)
end
end
# Loads tapped formulae.
class TapLoader < FormulaLoader
def initialize tapped_name
@name = tapped_name
@path = Pathname.new(tapped_name)
end
def get_formula
klass.new(tapped_name, path.to_s)
end
end
# Return a Formula instance for the given reference.
# `ref` is string containing:
# * a formula name
# * a formula pathname
# * a formula URL
# * a local bottle reference
def self.factory ref
# If a URL is passed, download to the cache and install
if ref =~ %r[(https?|ftp)://]
f = FromUrlLoader.new(ref)
f.fetch
elsif ref =~ Pathname::BOTTLE_EXTNAME_RX
f = BottleLoader.new(ref)
else
name_or_path = Formula.canonical_name(ref)
if name_or_path =~ %r{^(\w+)/(\w+)/([^/])+$}
# name appears to be a tapped formula, so we don't munge it
# in order to provide a useful error message when require fails.
f = TapLoader.new(name_or_path)
elsif name_or_path.include? "/"
# If name was a path or mapped to a cached formula
f = FromPathLoader.new(name_or_path)
elsif name_or_path =~ /\.rb$/
f = FromPathLoader.new("./#{name_or_path}")
else
f = StandardLoader.new(name_or_path)
end
end
f.get_formula
end
end
|