Skip to content

Commit

Permalink
Port the Psych/Syck YAML fix from Rubygems 1.8.24
Browse files Browse the repository at this point in the history
The existing Psych/Syck fix wasn't working in certain circumstances
(gem fetch XXXXX, for example), so the broader solution from
Rubygems 1.8.24 was used instead. This resolved the problem without
breaking existing tests.
  • Loading branch information
mckern committed May 4, 2012
1 parent 39c38dc commit e7aa0c2
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 22 deletions.
60 changes: 45 additions & 15 deletions lib/rubygems.rb
Expand Up @@ -106,7 +106,7 @@
module Gem
NAME = 'SlimGems'
GEM_NAME = 'slimgems'
VERSION = '1.3.9.4'
VERSION = '1.3.9.5'
SlimGemsVersion = RubyGemsVersion = VERSION

##
Expand Down Expand Up @@ -633,24 +633,54 @@ def self.remove_prelude_paths
##
# Loads YAML, preferring Psych

@yaml_loaded = false

##
# Loads YAML, preferring Psych

def self.load_yaml
begin
require 'psych' unless ENV['TEST_SYCK']
rescue ::LoadError
ensure
require 'yaml'
end
return if @yaml_loaded

test_syck = ENV['TEST_SYCK']

unless test_syck
begin
gem 'psych', '~> 1.2', '>= 1.2.1'
rescue Gem::LoadError
# It's OK if the user does not have the psych gem installed. We will
# attempt to require the stdlib version
end

# Hack to handle syck's DefaultKey bug with psych.
# See the note at the top of lib/rubygems/requirement.rb for
# why we end up defining DefaultKey more than once.
if !defined? YAML::Syck
YAML.module_eval do
const_set 'Syck', Module.new {
const_set 'DefaultKey', Class.new
}
begin
# Try requiring the gem version *or* stdlib version of psych.
require 'psych'
rescue ::LoadError
# If we can't load psych, thats fine, go on.
else
# If 'yaml' has already been required, then we have to
# be sure to switch it over to the newly loaded psych.
if defined?(YAML::ENGINE) && YAML::ENGINE.yamler != "psych"
YAML::ENGINE.yamler = "psych"
end

require 'rubygems/psych_additions'
require 'rubygems/psych_tree'
end
end

require 'yaml'

# If we're supposed to be using syck, then we may have to force
# activate it via the YAML::ENGINE API.
if test_syck and defined?(YAML::ENGINE)
YAML::ENGINE.yamler = "syck" unless YAML::ENGINE.syck?
end

# Now that we're sure some kind of yaml library is loaded, pull
# in our hack to deal with Syck's DefaultKey ugliness.
require 'rubygems/syck_hack'

@yaml_loaded = true
end

##
Expand Down
9 changes: 9 additions & 0 deletions lib/rubygems/psych_additions.rb
@@ -0,0 +1,9 @@
# This exists just to satify bugs in marshal'd gemspecs that
# contain a reference to YAML::PrivateType. We prune these out
# in Specification._load, but if we don't have the constant, Marshal
# blows up.

module Psych
class PrivateType
end
end
27 changes: 27 additions & 0 deletions lib/rubygems/psych_tree.rb
@@ -0,0 +1,27 @@
module Gem
if defined? ::Psych::Visitors
class NoAliasYAMLTree < Psych::Visitors::YAMLTree
def visit_String(str)
return super unless str == '=' # or whatever you want

quote = Psych::Nodes::Scalar::SINGLE_QUOTED
@emitter.scalar str, nil, nil, false, true, quote
end

# Noop this out so there are no anchors
def register(target, obj)
end

# This is ported over from the yaml_tree in 1.9.3
def format_time time
if time.utc?
time.strftime("%Y-%m-%d %H:%M:%S.%9N Z")
else
time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z")
end
end

private :format_time
end
end
end
25 changes: 18 additions & 7 deletions lib/rubygems/requirement.rb
Expand Up @@ -129,18 +129,15 @@ def hash # :nodoc:
end

def marshal_dump # :nodoc:
fix_syck_default_key_in_requirements

[@requirements]
end

def marshal_load array # :nodoc:
@requirements = array[0]

# Fixup the Syck DefaultKey bug
@requirements.each do |r|
if r[0].kind_of? YAML::Syck::DefaultKey
r[0] = "="
end
end
fix_syck_default_key_in_requirements
end

def prerelease?
Expand All @@ -157,7 +154,7 @@ def pretty_print q # :nodoc:
# True if +version+ satisfies this Requirement.

def satisfied_by? version
requirements.all? { |op, rv| OPS[op].call version, rv }
requirements.all? { |op, rv| (OPS[op] || OPS["="]).call version, rv }
end

def to_s # :nodoc:
Expand All @@ -167,6 +164,20 @@ def to_s # :nodoc:
def <=> other # :nodoc:
to_s <=> other.to_s
end

private

def fix_syck_default_key_in_requirements
Gem.load_yaml

# Fixup the Syck DefaultKey bug
@requirements.each do |r|
if r[0].kind_of? Gem::SyckDefaultKey
r[0] = "="
end
end
end

end

# :stopdoc:
Expand Down
74 changes: 74 additions & 0 deletions lib/rubygems/syck_hack.rb
@@ -0,0 +1,74 @@
# :stopdoc:

# Hack to handle syck's DefaultKey bug
#
# This file is always loaded AFTER either syck or psych are already
# loaded. It then looks at what constants are available and creates
# a consistent view on all rubys.
#
# All this is so that there is always a YAML::Syck::DefaultKey
# class no matter if the full yaml library has loaded or not.
#

module YAML
# In newer 1.9.2, there is a Syck toplevel constant instead of it
# being underneith YAML. If so, reference it back under YAML as
# well.
if defined? ::Syck
# for tests that change YAML::ENGINE
# 1.8 does not support the second argument to const_defined?
remove_const :Syck rescue nil

Syck = ::Syck

# JRuby's "Syck" is called "Yecht"
elsif defined? YAML::Yecht
Syck = YAML::Yecht

# Otherwise, if there is no YAML::Syck, then we've got just psych
# loaded, so lets define a stub for DefaultKey.
elsif !defined? YAML::Syck
module Syck
class DefaultKey
end
end
end

# Now that we've got something that is always here, define #to_s
# so when code tries to use this, it at least just shows up like it
# should.
module Syck
class DefaultKey
remove_method :to_s rescue nil

def to_s
'='
end
end
end
end

# Sometime in the 1.9 dev cycle, the Syck constant was moved from under YAML
# to be a toplevel constant. So gemspecs created under these versions of Syck
# will have references to Syck::DefaultKey.
#
# So we need to be sure that we reference Syck at the toplevel too so that
# we can always load these kind of gemspecs.
#
if !defined?(Syck)
Syck = YAML::Syck
end

# Now that we've got Syck setup in all the right places, store
# a reference to the DefaultKey class inside Gem. We do this so that
# if later on YAML, etc are redefined, we've still got a consistent
# place to find the DefaultKey class for comparison.

module Gem
# for tests that change YAML::ENGINE
remove_const :SyckDefaultKey if const_defined? :SyckDefaultKey

SyckDefaultKey = YAML::Syck::DefaultKey
end

# :startdoc:

0 comments on commit e7aa0c2

Please sign in to comment.