Parent

Methods

Files

Class/Module Index [+]

Quicksearch

LWES::Struct

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.

Public Class Methods

new(options, &block) click to toggle source

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
# File lib/lwes/struct.rb, line 42
def self.new(options, &block)
  db = type_db(options)
  dump = db.to_hash
  klass, name, event_def = class_for(options, dump)

  # merge MetaEventInfo fields in
  meta_event_info = dump[:MetaEventInfo]
  alpha = proc { |a,b| a.first.to_s <=> b.first.to_s }
  event_def = event_def.sort(&alpha)
  if meta_event_info
    seen = event_def.map { |(field, _)| field }
    meta_event_info.sort(&alpha).each do |field_type|
      seen.include?(field_type.first) or event_def << field_type
    end
  end

  Array(options[:skip]).each do |x|
    if Regexp === x
      event_def.delete_if { |(f,_)| x =~ f.to_s }
    else
      if x.to_sym == :MetaEventInfo
        meta_event_info.nil? and
          raise RuntimeError, "MetaEventInfo not defined in #{file}"
        meta_event_info.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, options)
  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 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  endend

# 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 alias __aref []alias __aset []=def [](key)  __aref(key.kind_of?(Fixnum) ? key : AREF_MAP[key])enddef []=(key, value)  __aset(key.kind_of?(Fixnum) ? key : AREF_MAP[key], value)end
  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

[Validate]

Generated with the Darkfish Rdoc Generator 2.