class PackedModel::Base
Public Class Methods
add_unpack_callback(callback)
click to toggle source
# File lib/packed_model/base.rb, line 269 def self.add_unpack_callback(callback) @unpack_callbacks ||= [] @unpack_callbacks << callback end
all_field_options()
click to toggle source
# File lib/packed_model/base.rb, line 15 def self.all_field_options {} end
attribute(name, options={}, &block)
click to toggle source
# File lib/packed_model/base.rb, line 39 def self.attribute(name, options={}, &block) options[:name] = name.to_sym raise "duplicate field name #{name}" if self.keys.include?(options[:name]) options[:index] = self.fields.size options[:type] ||= :string case options[:type] when :string options[:strip] = true options[:null] = true options[:pack_directive] = 'Z*' when :integer, :int options[:default] ||= 0 # pack 'N' directive requires a value for an integer options[:bytesize] = 4 options[:pack_directive] = 'N' # network byte order when :char options[:size] ||= 1 options[:bytesize] = options[:size] options[:pack_directive] = "a#{options[:size]}" when :marker raise "missing value for marker" unless options[:value] options[:default] = options[:value] options[:bytesize] = 4 options[:pack_directive] = 'N' # network byte order options[:unpack_callback] ||= create_marker_unpack_callback(options[:index], options[:value]) when :custom raise "missing pack_directive for #{name}" unless options[:pack_directive] else raise "Unknown type #{options[:type]}" end add_unpack_callback(options[:unpack_callback]) if options[:unpack_callback].is_a?(Proc) add_unpack_callback(create_strip_unpack_callback(options[:index], options[:null])) if options[:strip] class_eval do attrs = self.fields << options ks = self.keys << options[:name] defaults = self.default_values << options[:default] ps = self.pack_string << options[:pack_directive] kopts = self.all_field_options.merge name.to_sym => options class << self; self end.send(:define_method, "fields") do attrs end class << self; self end.send(:define_method, "keys") do ks end class << self; self end.send(:define_method, "default_values") do defaults end class << self; self end.send(:define_method, "pack_string") do ps end class << self; self end.send(:define_method, "all_field_options") do kopts end if self.fixed_width? && options[:bytesize].nil? class << self; self end.send(:define_method, "fixed_width?") do false end class << self; self end.send(:define_method, "bytesize") do nil end end if self.fixed_width? bs = self.bytesize + options[:bytesize] class << self; self end.send(:define_method, "bytesize") do bs end end end method_src = <<-METHOD def #{name} @values[#{options[:index]}] || #{options[:default].inspect} end METHOD if options.has_key?(:nil) method_src << <<-METHOD def #{name}_with_nil_check #{name}_without_nil_check == #{options[:nil].inspect} ? nil : #{name}_without_nil_check end alias #{name}_without_nil_check #{name} alias #{name} #{name}_with_nil_check METHOD end case options[:type] when :string, :char method_src << <<-METHOD def #{name}=(val) val = val.nil? ? nil : val.to_s @changed = @changed || @values[#{options[:index]}] != val @values[#{options[:index]}] = val end METHOD when :integer, :int method_src << <<-METHOD def #{name}=(val) val = val.to_i @changed = @changed || @values[#{options[:index]}] != val @values[#{options[:index]}] = val end METHOD when :custom if options[:filter].is_a?(Symbol) || options[:filter].is_a?(String) method_src << <<-METHOD def #{name}=(val) val = val.#{options[:filter]} @changed = @changed || @values[#{options[:index]}] != val @values[#{options[:index]}] = val end METHOD elsif options[:filter] ||= block method_src << <<-METHOD def #{name}=(val) val = self.class.field_options(#{name.inspect})[:filter].call(val) @changed = @changed || @values[#{options[:index]}] != val @values[#{options[:index]}] = val end METHOD else method_src << <<-METHOD def #{name}=(val) @changed = @changed || @values[#{options[:index]}] != val @values[#{options[:index]}] = val end METHOD end end self.class_eval method_src, __FILE__, __LINE__ end
bit_vector(name, fields, options={})
click to toggle source
# File lib/packed_model/base.rb, line 180 def self.bit_vector(name, fields, options={}) raise "too many fields for bit vecotr #{name}" if fields.size > 32 self.attribute name, {:type => :integer, :fields => fields}.merge(options) name_equals = "#{name}=" fields.each_with_index do |fld, idx| mask = 1 << idx neg_mask = 0xFFFFFFFF ^ mask define_method fld do (self.send(name) & mask) == mask end define_method "#{fld}=" do |val| if self.class.valid_true_boolean_values.include?(val) self.send(name_equals, self.send(name) | mask) else self.send(name_equals, self.send(name) & neg_mask) end end end end
field_index(key)
click to toggle source
# File lib/packed_model/base.rb, line 23 def self.field_index(key) field_options(key)[:index] end
field_options(key)
click to toggle source
# File lib/packed_model/base.rb, line 19 def self.field_options(key) all_field_options[key.to_sym] end
fields()
click to toggle source
# File lib/packed_model/base.rb, line 3 def self.fields [] end
fixed_width?()
click to toggle source
# File lib/packed_model/base.rb, line 31 def self.fixed_width? true end
keys()
click to toggle source
# File lib/packed_model/base.rb, line 11 def self.keys [] end
new(values=nil)
click to toggle source
# File lib/packed_model/base.rb, line 205 def initialize(values=nil) case values when String raise InvalidDataException.new("fixed width model expected #{self.bytesize} but got #{values.bytesize}") if self.fixed_width? && self.bytesize != values.bytesize self.unpack values when Hash @changed = true @values = [] values.each do |k, v| send("#{k}=", v) if self.class.field_options(k) end fields.each do |options| next unless options[:fields] options[:fields].each do |field| send("#{field}=", values[field]) if values.has_key?(field) end end # set default values fields.each_with_index do |field, idx| @values[idx] ||= self.class.default_values[idx] end when NilClass @changed = true @values = self.class.default_values.dup else raise InvalidDataException.new("invalid values") end end
unpack_callbacks()
click to toggle source
# File lib/packed_model/base.rb, line 274 def self.unpack_callbacks @unpack_callbacks end
valid_true_boolean_values()
click to toggle source
# File lib/packed_model/base.rb, line 176 def self.valid_true_boolean_values @@valid_true_boolean_values ||= [true, 1, '1', 'true', 'on', 'yes'] end
Private Class Methods
bytesize()
click to toggle source
# File lib/packed_model/base.rb, line 288 def self.bytesize 0 end
create_marker_unpack_callback(index, value)
click to toggle source
# File lib/packed_model/base.rb, line 296 def self.create_marker_unpack_callback(index, value) Proc.new do |m| raise BadMarkerException.new("unpack failed, invalid marker value (#{m.values[index]}) expected #{value}") unless m.values[index] == value end end
create_strip_unpack_callback(index, null)
click to toggle source
# File lib/packed_model/base.rb, line 302 def self.create_strip_unpack_callback(index, null) if null Proc.new do |m| if m.values[index].empty? m.values[index] = nil else m.values[index].strip! end end else Proc.new do |m| m.values[index].strip! end end end
default_values()
click to toggle source
# File lib/packed_model/base.rb, line 284 def self.default_values [] end
pack_string()
click to toggle source
# File lib/packed_model/base.rb, line 292 def self.pack_string "" end
Public Instance Methods
bytesize()
click to toggle source
# File lib/packed_model/base.rb, line 259 def bytesize self.class.fixed_width? ? self.class.bytesize : (@bytesize ||= self.pack.bytesize) end
Also aliased as: size
changed!()
click to toggle source
# File lib/packed_model/base.rb, line 242 def changed! @changed = true end
changed?()
click to toggle source
# File lib/packed_model/base.rb, line 238 def changed? !! @changed end
fields()
click to toggle source
# File lib/packed_model/base.rb, line 7 def fields self.class.fields end
fixed_width?()
click to toggle source
# File lib/packed_model/base.rb, line 35 def fixed_width? self.class.fixed_width? end
keys()
click to toggle source
# File lib/packed_model/base.rb, line 27 def keys self.class.keys end
not_changed!()
click to toggle source
# File lib/packed_model/base.rb, line 246 def not_changed! @changed = false end
pack()
click to toggle source
# File lib/packed_model/base.rb, line 255 def pack @values.pack self.class.pack_string end
to_hash()
click to toggle source
# File lib/packed_model/base.rb, line 250 def to_hash Hash[self.keys.zip @values] end
Also aliased as: to_h
unpack(str)
click to toggle source
# File lib/packed_model/base.rb, line 264 def unpack(str) @values = str.unpack(self.class.pack_string) self.unpack_callbacks.each { |c| c.call self } if self.unpack_callbacks end
unpack_callbacks()
click to toggle source
# File lib/packed_model/base.rb, line 278 def unpack_callbacks self.class.unpack_callbacks end
values()
click to toggle source
# File lib/packed_model/base.rb, line 234 def values @values end