aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails
diff options
context:
space:
mode:
authorMatt Jones <al2o3cr@gmail.com>2008-10-11 13:54:21 -0400
committerMichael Koziarski <michael@koziarski.com>2008-10-13 17:27:25 +0200
commit9f15870946720ef9a0dbaa3ac336fff8bd149752 (patch)
treeb40988cff37cc44838f003e0920a4959ca502e5b /railties/lib/rails
parent0d4dbb3dfaec8355e072d8f758de40a277f2f009 (diff)
downloadrails-9f15870946720ef9a0dbaa3ac336fff8bd149752.tar.gz
rails-9f15870946720ef9a0dbaa3ac336fff8bd149752.tar.bz2
rails-9f15870946720ef9a0dbaa3ac336fff8bd149752.zip
Make VendorGemSourceIndex handle broken/missing specs generated by previous versions.
Signed-off-by: Michael Koziarski <michael@koziarski.com>
Diffstat (limited to 'railties/lib/rails')
-rw-r--r--railties/lib/rails/gem_dependency.rb37
-rw-r--r--railties/lib/rails/vendor_gem_source_index.rb83
2 files changed, 97 insertions, 23 deletions
diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb
index ffdc403700..80481859e1 100644
--- a/railties/lib/rails/gem_dependency.rb
+++ b/railties/lib/rails/gem_dependency.rb
@@ -82,6 +82,10 @@ module Rails
File.join(base_directory, specification.full_name)
end
+ def spec_filename(base_directory)
+ File.join(gem_dir(base_directory), '.specification')
+ end
+
def load
return if @loaded || @load_paths_added == false
require(@lib || name) unless @lib == false
@@ -146,17 +150,42 @@ module Rails
Gem::GemRunner.new.run(unpack_command)
end
+ # Gem.activate changes the spec - get the original
+ real_spec = Gem::Specification.load(spec.loaded_from)
+ write_spec(directory, real_spec)
+
+ end
+
+ def write_spec(directory, spec)
# copy the gem's specification into GEMDIR/.specification so that
# we can access information about the gem on deployment systems
# without having the gem installed
- spec_filename = File.join(gem_dir(directory), '.specification')
- # Gem.activate changes the spec - get the original
- spec = Gem::Specification.load(specification.loaded_from)
- File.open(spec_filename, 'w') do |file|
+ File.open(spec_filename(directory), 'w') do |file|
file.puts spec.to_yaml
end
end
+ def refresh_spec(directory)
+ real_gems = Gem.source_index.installed_source_index
+ exact_dep = Gem::Dependency.new(name, "= #{specification.version}")
+ matches = real_gems.search(exact_dep)
+ installed_spec = matches.first
+ if installed_spec
+ # we have a real copy
+ # get a fresh spec - matches should only have one element
+ # note that there is no reliable method to check that the loaded
+ # spec is the same as the copy from real_gems - Gem.activate changes
+ # some of the fields
+ real_spec = Gem::Specification.load(matches.first.loaded_from)
+ write_spec(directory, real_spec)
+ puts "Reloaded specification for #{name} from installed gems."
+ else
+ # the gem isn't installed locally - write out our current specs
+ write_spec(directory, specification)
+ puts "Gem #{name} not loaded locally - writing out current spec."
+ end
+ end
+
def ==(other)
self.name == other.name && self.requirement == other.requirement
end
diff --git a/railties/lib/rails/vendor_gem_source_index.rb b/railties/lib/rails/vendor_gem_source_index.rb
index e188f07895..0dac1d5394 100644
--- a/railties/lib/rails/vendor_gem_source_index.rb
+++ b/railties/lib/rails/vendor_gem_source_index.rb
@@ -13,6 +13,14 @@ module Rails
attr_reader :installed_source_index
attr_reader :vendor_source_index
+ def self.silence_spec_warnings
+ @@silence_spec_warnings
+ end
+
+ def self.silence_spec_warnings=(v)
+ @@silence_spec_warnings = v
+ end
+
def initialize(installed_index, vendor_dir=Rails::GemDependency.unpacked_path)
@installed_source_index = installed_index
@vendor_dir = vendor_dir
@@ -33,31 +41,68 @@ module Rails
# load specifications from vendor/gems
Dir[File.join(Rails::GemDependency.unpacked_path, '*')].each do |d|
+ dir_name = File.basename(d)
+ dir_version = version_for_dir(dir_name)
spec = load_specification(d)
- next unless spec
- # NOTE: this is a bit of a hack - the gem system expects a different structure
- # than we have.
- # It's looking for:
- # repository
- # -> specifications
- # - gem_name.spec <= loaded_from points to this
- # -> gems
- # - gem_name <= gem files here
- # and therefore goes up one directory from loaded_from, then adds gems/gem_name
- # to the path.
- # But we have:
- # vendor
- # -> gems
- # -> gem_name <= gem files here
- # - .specification
- # so we set loaded_from to vendor/gems/.specification (not a real file) to
- # get the correct behavior.
- spec.loaded_from = File.join(Rails::GemDependency.unpacked_path, '.specification')
+ if spec
+ if spec.full_name != dir_name
+ # mismatched directory name and gem spec - produced by 2.1.0-era unpack code
+ if dir_version
+ # fix the spec version - this is not optimal (spec.files may be wrong)
+ # but it's better than breaking apps. Complain to remind users to get correct specs.
+ # use ActiveSupport::Deprecation.warn, as the logger is not set yet
+ $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has a mismatched specification file."+
+ " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings
+ spec.version = dir_version
+ else
+ $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems is not in a versioned directory"+
+ "(should be #{spec.full_name}).") unless @@silence_spec_warnings
+ # continue, assume everything is OK
+ end
+ end
+ else
+ # no spec - produced by early-2008 unpack code
+ # emulate old behavior, and complain.
+ $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems has no specification file."+
+ " Run 'rake gems:refresh_specs' to fix this.") unless @@silence_spec_warnings
+ if dir_version
+ spec = Gem::Specification.new
+ spec.version = dir_version
+ spec.require_paths = ['lib']
+ ext_path = File.join(d, 'ext')
+ spec.require_paths << 'ext' if File.exist?(ext_path)
+ spec.name = /^(.*)-[^-]+$/.match(dir_name)[1]
+ files = ['lib']
+ # set files to everything in lib/
+ files += Dir[File.join(d, 'lib', '*')].map { |v| v.gsub(/^#{d}\//, '') }
+ files += Dir[File.join(d, 'ext', '*')].map { |v| v.gsub(/^#{d}\//, '') } if ext_path
+ spec.files = files
+ else
+ $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems not in a versioned directory."+
+ " Giving up.") unless @silence_spec_warnings
+ next
+ end
+ end
+ spec.loaded_from = File.join(d, '.specification')
+ # finally, swap out full_gem_path
+ # it would be better to use a Gem::Specification subclass, but the YAML loads an explicit class
+ class << spec
+ def full_gem_path
+ path = File.join installation_path, full_name
+ return path if File.directory? path
+ File.join installation_path, original_name
+ end
+ end
vendor_gems[File.basename(d)] = spec
end
@vendor_source_index = Gem::SourceIndex.new(vendor_gems)
end
+ def version_for_dir(d)
+ matches = /-([^-]+)$/.match(d)
+ Gem::Version.new(matches[1]) if matches
+ end
+
def load_specification(gem_dir)
spec_file = File.join(gem_dir, '.specification')
YAML.load_file(spec_file) if File.exist?(spec_file)