1 require 'rails/vendor_gem_source_index'
4 def self.source_index
=(index
)
10 class GemDependency
< Gem
::Dependency
11 attr_accessor
:lib, :source, :dep
13 def self.unpacked_path
14 @unpacked_path ||= File
.join(RAILS_ROOT
, 'vendor', 'gems')
19 def self.add_frozen_gem_path
20 @
@paths_loaded ||= begin
21 source_index
= Rails
::VendorGemSourceIndex.new(Gem
.source_index
)
23 Gem
.source_index
= source_index
24 # loaded before us - we can't change them, so mark them
25 Gem
.loaded_specs
.each
do |name
, spec
|
26 @
@framework_gems[name
] = spec
32 def initialize(name
, options
= {})
33 require 'rubygems' unless Object
.const_defined
?(:Gem)
35 if options
[:requirement]
36 req
= options
[:requirement]
37 elsif options
[:version]
38 req
= Gem
::Requirement.create(options
[:version])
40 req
= Gem
::Requirement.default
44 @source = options
[:source]
45 @loaded = @frozen = @load_paths_added = false
51 self.class.add_frozen_gem_path
52 return if @loaded || @load_paths_added
54 @load_paths_added = @loaded = @frozen = true
58 @spec = Gem
.loaded_specs
[name
]
59 @frozen = @spec.loaded_from
.include?(self.class.unpacked_path
) if @spec
60 @load_paths_added = true
65 return [] if framework_gem
?
66 return [] unless installed
?
67 specification
.dependencies
.reject
do |dependency
|
68 dependency
.type
== :development
69 end.map
do |dependency
|
70 GemDependency
.new(dependency
.name
, :requirement => dependency
.version_requirements
)
75 # code repeated from Gem.activate. Find a matching spec, or the currently loaded version.
76 # error out if loaded version and requested version are incompatible.
78 matches
= Gem
.source_index
.search(self)
79 matches
<< @
@framework_gems[name
] if framework_gem
?
80 if Gem
.loaded_specs
[name
] then
81 # This gem is already loaded. If the currently loaded gem is not in the
82 # list of candidate gems, then we have a version conflict.
83 existing_spec
= Gem
.loaded_specs
[name
]
84 unless matches
.any
? { |spec
| spec
.version == existing_spec
.version } then
86 "can't activate #{@dep}, already activated #{existing_spec.full_name}"
88 # we're stuck with it, so change to match
89 version_requirements
= Gem
::Requirement.create("=#{existing_spec.version}")
99 r
= version_requirements
100 (r
== Gem
::Requirement.default
) ? nil : r
104 # TODO: If Rubygems ever gives us a way to detect this, we should use it
109 @
@framework_gems.has_key
?(name
)
113 @frozen ||= vendor_rails
? || vendor_gem
?
117 Gem
.loaded_specs
.keys
.include?(name
)
120 def load_paths_added
?
121 # always try to add load paths - even if a gem is loaded, it may not
122 # be a compatible version (ie random_gem 0.4 is loaded and a later spec
123 # needs >= 0.5 - gem 'random_gem' will catch this and error out)
131 elsif specification
.nil?
134 # check if the gem is loaded by inspecting $"
135 # specification.files lists all the files contained in the gem
136 gem_files
= specification
.files
137 # select only the files contained in require_paths - typically in bin and lib
138 require_paths_regexp
= Regexp
.new("^(#{specification.require_paths*'|'})/")
139 gem_lib_files
= gem_files
.select
{ |f
| require_paths_regexp
.match(f
) }
140 # chop the leading directory off - a typical file might be in
141 # lib/gem_name/file_name.rb, but it will be 'require'd as gem_name/file_name.rb
142 gem_lib_files
.map
! { |f
| f
.split('/', 2)[1] }
143 # if any of the files from the above list appear in $", the gem is assumed to
145 !(gem_lib_files
& $
").empty?
151 Gem.loaded_specs.has_key?(name) && Gem.loaded_specs[name].loaded_from.empty?
155 specification && File.exists?(unpacked_gem_directory)
159 require 'rails/gem_builder'
161 return unless File.exists?(unpacked_specification_filename)
162 spec = YAML::load_file(unpacked_specification_filename)
163 Rails::GemBuilder.new(spec, unpacked_gem_directory).build_extensions
164 puts "Built gem
: '#{unpacked_gem_directory}'"
166 dependencies.each { |dep| dep.build }
171 cmd = "#{gem_command} #{install_command.join(' ')}"
178 return if @loaded || @load_paths_added == false
179 require(@lib || name) unless @lib == false
183 $!.backtrace.each { |b| puts b }
187 Rails::VendorGemSourceIndex.silence_spec_warnings = true
188 real_gems = Gem.source_index.installed_source_index
189 exact_dep = Gem::Dependency.new(name, "= #{specification.version}")
190 matches = real_gems.search(exact_dep)
191 installed_spec = matches.first
194 # we have a real copy
195 # get a fresh spec - matches should only have one element
196 # note that there is no reliable method to check that the loaded
197 # spec is the same as the copy from real_gems - Gem.activate changes
199 real_spec = Gem::Specification.load(matches.first.loaded_from)
200 write_specification(real_spec)
201 puts "Reloaded specification for #{name} from installed gems."
203 # the gem isn't installed locally - write out our current specs
204 write_specification(specification)
205 puts "Gem #{name} not loaded locally - writing out current spec."
209 puts "Gem directory for #{name} not found - check if it's loading before rails."
211 puts "Something bad is going on - gem directory not found for #{name}."
216 def unpack(options={})
217 unless frozen? || framework_gem?
218 FileUtils.mkdir_p unpack_base
219 Dir.chdir unpack_base do
220 Gem::GemRunner.new.run(unpack_command)
222 # Gem.activate changes the spec - get the original
223 real_spec = Gem::Specification.load(specification.loaded_from)
224 write_specification(real_spec)
226 dependencies.each { |dep| dep.unpack } if options[:recursive]
229 def write_specification(spec)
230 # copy the gem's specification into GEMDIR/.specification so that
231 # we can access information about the gem on deployment systems
232 # without having the gem installed
233 File.open(unpacked_specification_filename, 'w') do |file|
234 file.puts spec.to_yaml
239 self.name == other.name && self.requirement == other.requirement
241 alias_method :"eql?", :"=="
257 cmd = %w(install) << name
258 cmd << "--version" << %("#{requirement.to_s}") if requirement
259 cmd << "--source" << @source if @source
264 cmd = %w(unpack) << name
265 cmd << "--version" << "= "+specification.version.to_s if requirement
270 Rails::GemDependency.unpacked_path
273 def unpacked_gem_directory
274 File.join(unpack_base, specification.full_name)
277 def unpacked_specification_filename
278 File.join(unpacked_gem_directory, '.specification')