Darwinweb

Convert Syck to Psych YAML format

November 12, 2012     

If you deal with a lot of YAML in your Ruby code, especially migrating from Ruby 1.8.x to 1.9.2 and 1.9.3, then you may have run into some of the numerous issues with YAML incompatibility. I won’t bore you with a lot of specifics, because most of these bugs are transient, but here are the basic facts:

  1. Syck is the antiquated builtin legacy YAML driver for Ruby.
  2. Psych is the new-fangled modern YAML driver for Ruby.
  3. Ruby 1.9.3 always defaults to Psych.
  4. Ruby 1.9.2 may default to Psych depending on the system state Ruby is compiled.

You can check which engine is active in Ruby 1.9.x like so:

1.9.3p327 :001 > require 'yaml'
 => true 
1.9.3p327 :002 > YAML
 => Psych

Hopefully you are using Psych for everything, but if you have legacy Syck YAML files lying around you could be in for some pain because they are not necessarily compatible. In my case, I had a bunch of i18n translation files emitted with Syck which uses an incompatible escape code structure instead of plain utf-8, thereby rendering the files unreadable by both Psych and humans alike. My solution was a little script utilizing the fact that the engine can be swapped dynamically:

require 'yaml'
require 'fileutils'

if ARGV.empty?
  $stderr.puts "! Must pass one or more filenames to convert from Syck output to Psych output."
  exit 1
end

bad_files = ARGV.select{ |f| ! File.exists?(f) }
if bad_files.any?
  $stderr.puts "! Aborting because the following files do not exist:"
  $stderr.puts bad_files
  exit 1
end

def use_syck
  YAML::ENGINE.yamler = 'syck'
  raise "Oops! Something went horribly wrong." unless YAML == Syck
end

def use_psych
  YAML::ENGINE.yamler = 'psych'
  raise "Oops! Something went horribly wrong." unless YAML == Psych
end

ARGV.each do |filename|
  $stdout.print "Converting #{filename} from Syck to Psych..."

  use_syck
  hash = YAML.load(File.read filename)
  FileUtils.cp filename, "#{filename}.bak"
  use_psych
  File.open(filename, 'w'){ |file| file.write(YAML.dump(hash)) }

  $stdout.puts " done."
end
1 comment Leave Yours