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
|
module Packable
# Packers for any packable class.
class Packers < Hash
SPECIAL = [:default, :merge_all].freeze
# Usage:
# PackableClass.packers.set :shortcut, :option => value, ...
# PackableClass.packers { |p| p.set...; p.set... }
# PackableClass.packers.set :shortcut, :another_shortcut
# PackableClass.packers.set :shortcut do |packer|
# packer.write{|io| io << self.something... }
# packer.read{|io| Whatever.new(io.read(...)) }
# end
def set(key, options_or_shortcut={})
if block_given?
packer = FilterCapture.new options_or_shortcut
yield packer
end
self[key] = options_or_shortcut
self
end
def initialize(klass) #:nodoc:
@klass = klass
end
def lookup(key) #:nodoc:
k = @klass
begin
if found = Packers.for(k)[key]
return found
end
k = k.superclass
end while k
SPECIAL.include?(key) ? {} : raise("Unknown option #{key} for #{@klass}")
end
def finalize(options) #:nodoc:
options = lookup(options) while options.is_a? Symbol
lookup(:merge_all).merge(options)
end
@@packers_for_class = Hash.new{|h, klass| h[klass] = Packers.new(klass)}
# Returns the configuration for the given +klass+.
def self.for(klass)
@@packers_for_class[klass]
end
def self.to_class_option_list(*arg) #:nodoc:
r = []
until arg.empty? do
k, options = original = arg.shift
k, options = global_lookup(k) if k.is_a? Symbol
raise TypeError, "Expected a class or symbol: #{k.inspect}" unless k.instance_of? Class
options ||= arg.first.is_a?(Hash) ? arg.shift.tap{|o| original = [original, o]} : :default
r << [k, k.packers.finalize(options), original]
end
r
end
def self.to_object_option_list(*arg) #:nodoc:
r=[]
until arg.empty? do
obj = arg.shift
options = case arg.first
when Hash, Symbol
arg.shift
else
:default
end
r << [obj, obj.class.packers.finalize(options)]
end
r
end
private
def self.global_lookup(key) #:nodoc:
@@packers_for_class.each do |klass, packers|
if options = packers[key]
return [klass, options]
end
end
raise "Couldn't find packing option #{key}"
end
end
# Use to capture the blocks given to read/write
class FilterCapture #:nodoc:
attr_accessor :options
def initialize(options)
self.options = options
end
def read(&block)
options[:read_packed] = block
end
def write(&block)
options[:write_packed] = block.unbind
end
end
end
|