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
|
require 'yaml'
class String
def wordwrap( len )
gsub( /\n/, "\n\n" ).gsub( /.{#{len},}?\s+/, "\\0\n" )
end
end
puts "** Ordering YAML mapping keys **"
puts
#
# Robert Feldt wrote:
#
# > ...when I use Yaml for ini files I'd like to be able to
# > spec an order for map pairs to be serialized in. Obviously Ruby hashes has
# > no garantuee for this but are there any plans on supporting this? If not
# > I'll whip something up myself; I really wanna make sure the most important
# > stuff comes out on top...
#
# I had three suggestions:
#
# 1. When using objects, you can specify the ordering of your properties
# explicitly by defining a `to_yaml_properties' method.
#
class Instrument
attr_accessor :name, :key, :is_woodwind
def initialize( n, k, ww )
@name, @key, @is_woodwind = n, k, ww
end
def to_yaml_properties
[ '@name', '@key', '@is_woodwind' ]
end
end
puts "-- 1. Instrument object --"
test1 = Instrument.new( 'Alto Saxophone', 'Eb', true )
puts test1.inspect.wordwrap( 30 ).gsub( /^/, ' ' )
puts
puts "** With ordered properties **"
puts test1.to_yaml.gsub( /^/, ' ' )
puts
#
# 2. The same can't be done for Hashes because the key set isn't
# predictable. But if the :SortKeys method is called, then
# the Hash will be sorted with Hash#sort. You could define a
# singleton `sort' method to sort certain Hashes.
#
test2 = { 'name' => 'Alto Saxophone', 'key' => 'Eb',
'is_woodwind' => true }
def test2.sort
order = [ 'name', 'key', 'is_woodwind' ]
super { |a, b| order.index( a[0] ) <=> order.index( b[0] ) }
end
puts "-- 2. Instrument hash --"
puts test2.inspect.wordwrap( 30 ).gsub( /^/, ' ' )
puts
puts "** With ordered keys **"
puts test2.to_yaml( :SortKeys => true ).gsub( /^/, ' ' )
puts
#
# Alternatively, you could define a singleton `to_a' to sort
# correctly and skip the :SortKeys option.
#
# 3. Finally, the YAML spec now defines an !omap type. This type is
# an ordered mapping with unique keys. YAML.rb will load this
# into a Hash-like class.
#
test3 = YAML::Omap[
[ 'name', 'Alto Saxophone' ],
[ 'key', 'Eb' ],
[ 'is_woodwind', 'true' ]
]
puts "-- 3. Instrument Omap --"
puts test3.inspect.wordwrap( 30 ).gsub( /^/, ' ' )
puts
puts "** With ordered keys **"
puts test3.to_yaml.gsub( /^/, ' ' )
puts
#
# Robert's answer was great. He used a mixin to add a singleton `to_a'
# method, which allows him to easily sort many hashes.
#
module SortableToAKeys
def key_order=( keys )
@__key_order = keys
end
def to_a
ks, out = self.keys, []
(@__key_order + (ks - @__key_order)).each do |key|
out << [key, self[key]] if ks.include?(key)
end
out
end
end
test4 = { 'name' => 'Alto Saxophone', 'key' => 'Eb',
'is_woodwind' => true }
puts "-- 4. Instrument SortableToAKeys --"
puts test4.inspect.wordwrap( 30 ).gsub( /^/, ' ' )
puts
test4.extend SortableToAKeys
test4.key_order = [ 'name', 'key', 'is_woodwind' ]
puts "** With ordered keys **"
puts test4.to_yaml.gsub( /^/, ' ' )
puts
|