Skip to content

Instantly share code, notes, and snippets.

@pocari
Created September 29, 2012 10:36
Show Gist options
  • Save pocari/3803659 to your computer and use it in GitHub Desktop.
Save pocari/3803659 to your computer and use it in GitHub Desktop.
FixedLayoutMapper
#coding: Windows-31J
module FixedLayoutMapper
class Mapper
class ColumnMapper
attr_accessor :layout_mapper, :converter
def initialize(layout_mapper, converter = nil)
@layout_mapper = layout_mapper
@converter = converter
end
def convert(value)
if @converter
@converter.(value)
else
value
end
end
end
class SimpleMapper < ColumnMapper
def initialize(layout_mapper, len, converter = nil)
super layout_mapper, converter
@len = len
end
def map(bytes)
value = bytes.take(@len).pack("C*").force_encoding(layout_mapper.encoding)
#value = converter.(value) if converter
[convert(value), bytes.drop(@len)]
end
end
class SubLayoutMapper < ColumnMapper
def initialize(layout_mapper, layout_id, converter = nil)
super layout_mapper, converter
@layout_id = layout_id
end
def map(bytes)
value, rest = layout_mapper.get_layout(@layout_id).map(bytes, layout_mapper.get_result_class(@layout_id))
[convert(value), rest]
end
end
class ArrayMapper < ColumnMapper
def initialize(layout_mapper, mappers, converter = nil)
super layout_mapper, converter
@mappers = mappers
end
def map(bytes)
ret = []
rest = @mappers.inject(bytes) do |acc, mapper|
value, acc = mapper.map(acc)
ret << value
acc
end
[convert(ret), rest]
end
end
class Layout
def initialize
@layout = []
end
def syms
@layout.map{|e| e[0]}
end
def add(sym, mapper)
@layout << [sym, mapper]
end
def map(bytes, result_class)
obj = result_class.new
rest = @layout.inject(bytes) do |acc, (sym, mapper)|
value, acc = mapper.map(acc)
obj[sym] = value
acc
end
[obj, rest]
end
end
class << self
def define_layout(opts = {:encoding => Encoding.default_external}, &block)
obj = Mapper.new(opts)
obj.define_layout(&block)
obj
end
end
attr_reader :encoding
def initialize(opts = {:encoding => Encoding.default_external})
@current_layout = :default_layout
@layouts = {}
@result_class = {}
@encoding = opts[:encoding]
end
def define_layout(&block)
instance_eval(&block)
build_layout
end
def map(data, layout_id = @current_layout)
obj, = @layouts[layout_id].map(data.unpack("C*"), @result_class[layout_id])
obj
end
def get_layout(layout_id)
@layouts[layout_id]
end
def get_result_class(layout_id)
@result_class[layout_id]
end
private
def build_layout
@layouts.each do |layout_id, layout|
@result_class[layout_id] = Struct.new(*layout.syms) do
def to_hash
each_pair.inject({}) do |acc, (key, value)|
case
when value.respond_to?(:to_hash)
acc[key] = value.to_hash
when Array === value
acc[key] = value.map{|e|
if e.respond_to?(:to_hash)
e.to_hash
else
e
end
}
else
acc[key] = value
end
acc
end
end
end
end
end
def layout(layout_id, &block)
change_current_layout(layout_id) do
instance_eval(&block)
end
end
def col(sym, map_info, layout_id = @current_layout, &block)
case map_info
when Numeric
col_len(sym, map_info, layout_id, block)
when Symbol
col_from_sub_layout(sym, map_info, layout_id, block)
when Array
col_array(sym, map_info, layout_id, block)
end
end
def col_len(sym, len, layout_id = @current_layout, block)
@layouts[layout_id] ||= Layout.new
@layouts[layout_id].add(sym, SimpleMapper.new(self, len, block))
end
def col_from_sub_layout(sym, sub_layout, layout_id = @current_layout, block)
@layouts[layout_id] ||= Layout.new
@layouts[layout_id].add(sym, SubLayoutMapper.new(self, sub_layout, block))
end
def col_array(sym, array_param, layout_id = @current_layout, block)
@layouts[layout_id] ||= Layout.new
@layouts[layout_id].add(sym, ArrayMapper.new(self,
array_param.map{|arg|
case arg
when Numeric
mapper_class = SimpleMapper
when Symbol
mapper_class = SubLayoutMapper
else
raise "elements must be Numeric or Symbol"
end
mapper_class.new(self, arg)
}, block)
)
end
def change_current_layout(layout)
tmp = @current_layout
@current_layout = layout
yield
@current_layout = tmp
end
end
end
if $0 == __FILE__
divider = FixedLayoutMapper::Mapper.define_layout do
layout :sub2 do
col :sub2_1, 1
col :sub2_2, 3
end
layout :sub3 do
col :sub3_1, 2
end
layout :sub1 do
col :f1, 5 do |v|
v * 2
end
col :f2, 6
col :f3, [2, :sub3, 2]
col :f4, :sub2
end
col :f1, :sub1 do |sub1|
sub1.f1 *= 2
sub1
end
col :f2, [:sub3] * 4 do |sub3_ary|
sub3_ary.map{|e| e.sub3_1 *= 2; e}
end
end
data = [
%w(12345 123456 12 12 12 1 123 あ い う え)
]
obj = divider.map(data[0].join)
p obj
p obj.to_hash
require 'yaml'
puts obj.to_yaml
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment