Class: LWES::Struct
- Inherits:
-
Object
- Object
- LWES::Struct
- Extended by:
- ClassMaker
- Defined in:
- lib/lwes/struct.rb
Overview
LWES Events in an ESF file can be automatically turned into Ruby ::Struct-based objects and emitted directly through LWES::Emitter.
LWES::Struct is may be more memory-efficient and faster if your application uses all or most of the fields in the event definition. LWES::Event should be used in cases where many event fields are unused in an event definition.
LWES::Struct is created by LWES::TypeDB#create_classes! by default where the :sparse flag is false. There is little need to use this class directly.
Class Method Summary collapse
-
.new(options, &block) ⇒ Object
There is usually no need to use this method, LWES::TypeDB.create_classes! will call this for you.
Methods included from ClassMaker
class_for, set_constants, type_db
Class Method Details
.new(options, &block) ⇒ Object
There is usually no need to use this method, LWES::TypeDB.create_classes! will call this for you
Creates a new Struct-based class, takes the following options hash:
:db - pre-created LWES::TypeDB object
this is required unless :file is given
:file - pathname to the ESF file,
this is required unless :db is given
:class - Ruby base class name, if the ESF file only has one
event defined (besides MetaEventInfo), then specifying
it is optional, otherwise it is required when multiple
events are defined in the same ESF :file given above
:parent - parent class or module, the default is 'Object' putting
the new class in the global namespace. May be +nil+ for
creating anonymous classes
:name - event name if it differs from the Ruby base class name
given (or inferred) above. For DRY-ness, you are
recommended to keep your event names and Ruby class
names in sync and not need this option.
:skip - Array of field names to skip from the Event defininition
entirely, these could include fields that are only
implemented by the Listener. This may also be a
regular expression.
:defaults - hash of default key -> value pairs to set at
creation time
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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/lwes/struct.rb', line 42 def self.new(, &block) db = type_db() dump = db.to_hash klass, name, event_def = class_for(, dump) # merge MetaEventInfo fields in = dump[:MetaEventInfo] alpha = proc { |a,b| a.first.to_s <=> b.first.to_s } event_def = event_def.sort(&alpha) if seen = event_def.map { |(field, _)| field } .sort(&alpha).each do |field_type| seen.include?(field_type.first) or event_def << field_type end end Array([:skip]).each do |x| if Regexp === x event_def.delete_if { |(f,_)| x =~ f.to_s } else if x.to_sym == :MetaEventInfo .nil? and raise RuntimeError, "MetaEventInfo not defined in #{file}" .each do |(field,_)| event_def.delete_if { |(f,_)| field == f } end else event_def.delete_if { |(f,_)| f == x.to_sym } end end end tmp = ::Struct.new(*(event_def.map { |(field,_)| field })) set_constants(tmp, db, klass, name, ) ed = tmp.const_set :EVENT_DEF, {} event_def.each { |(field,type)| ed[field] = type } # freeze since emitter.c can segfault if this ever changes type_list = event_def.map do |(field,type)| [ field, field.to_s.freeze, type ].freeze end.freeze tmp.const_set :TYPE_LIST, type_list aref_map = tmp.const_set :AREF_MAP, {} type_list.each_with_index do |(field_sym,field_str,_),idx| aref_map[field_sym] = aref_map[field_str] = idx end tmp.const_set :HAVE_ENCODING, type_list.include?([ :enc, 'enc', LWES::INT_16 ]) tmp.class_eval(&block) if block_given? # define a parent-level method, eval is faster than define_method tmp.class_eval <<EOS class << self alias _new new undef_method :new def new(*args) if Hash === (init = args.first) rv = _new() DEFAULTS.merge(init).each_pair { |k,v| rv[k] = v } rv else rv = _new(*args) DEFAULTS.each_pair { |k,v| rv[k] ||= v } rv end end end EOS # avoid linear scans for large structs, not sure if 50 is a good enough # threshold but it won't help for anything <= 10 since Ruby (or at least # MRI) already optimizes those cases if event_def.size > 50 tmp.class_eval <<EOS alias __aref [] alias __aset []= def [](key) __aref(key.kind_of?(Fixnum) ? key : AREF_MAP[key]) end def []=(key, value) __aset(key.kind_of?(Fixnum) ? key : AREF_MAP[key], value) end EOS fast_methods = [] event_def.each_with_index do |(fld,_), idx| next if idx <= 9 if idx != aref_map[fld] raise LoadError, "event_def corrupt: #{event_def}" end fast_methods << "undef_method :#{fld}, :#{fld}=\n" fast_methods << "\ndef #{fld}; __aref #{idx}; end\n" fast_methods << "\ndef #{fld}=(val); __aset #{idx}, val ; end\n" end tmp.class_eval fast_methods.join("\n") end tmp end |