4 # creates a new Struct-based class, takes the following
7 # :file - pathname to the ESF file, this is always required
8 # :class - Ruby base class name, if the ESF file only has one
9 # event defined (besides MetaEventInfo), then specifying
10 # it is optional, otherwise it is required when multiple
11 # events are defined in the same ESF :file given above
12 # :parent - parent class or module, the default is 'Object' putting
13 # the new class in the global namespace.
14 # :event - event name if it differs from the Ruby base class name
15 # given (or inferred) above. For DRY-ness, you are
16 # recommended to keep your event names and Ruby class
17 # names in sync and not need this option.
18 # :skip - Array of field names to skip from the Event defininition
19 # entirely, these could include fields that are only
20 # implemented by the Listener. This may also be a
22 # :defaults - hash of default key -> value pairs to set at
25 def self.new(options, &block)
26 file = options[:file] or raise ArgumentError, "esf file missing"
27 test ?r, file or raise ArgumentError, "file #{file.inspect} not readable"
30 klass = options[:class] || begin
31 # make it easier to deal with single event files
32 events = (dump.keys - [ :MetaEventInfo ])
35 "multiple event defs available: #{events.inspect}\n" \
36 "pick one with :class"
40 name = options[:name] || klass
41 parent = options[:parent] || Object
42 event_def = dump[name.to_sym] or
43 raise RuntimeError, "#{name.inspect} not defined in #{file}"
45 # merge MetaEventInfo fields in
46 meta_event_info = dump[:MetaEventInfo]
47 alpha = proc { |a,b| a.first.to_s <=> b.first.to_s }
48 event_def = event_def.sort(&alpha)
50 seen = event_def.map { |(field, _)| field }
51 meta_event_info.sort(&alpha).each do |field_type|
52 seen.include?(field_type.first) or event_def << field_type
56 Array(options[:skip]).each do |x|
58 event_def.delete_if { |(f,_)| x =~ f.to_s }
60 if x.to_sym == :MetaEventInfo
61 meta_event_info.nil? and
62 raise RuntimeError, "MetaEventInfo not defined in #{file}"
63 meta_event_info.each do |(field,_)|
64 event_def.delete_if { |(f,_)| field == f }
67 event_def.delete_if { |(f,_)| f == x.to_sym }
72 tmp = ::Struct.new(*(event_def.map { |(field,_)| field }))
73 tmp = parent.const_set(klass, tmp)
74 tmp.const_set :TYPE_DB, db
75 ed = tmp.const_set :EVENT_DEF, {}
76 event_def.each { |(field,type)| ed[field] = type }
77 type_list = event_def.map { |(field,type)| [ field, field.to_s, type ] }
78 tmp.const_set :TYPE_LIST, type_list
80 defaults = options[:defaults] || {}
81 defaults = tmp.const_set :DEFAULTS, defaults.dup
82 tmp.class_eval(&block) if block_given?
84 # define a parent-level method, eval is faster than define_method
91 if Hash === (init = args.first)
93 DEFAULTS.merge(init).each_pair { |k,v| rv[k] = v }
97 DEFAULTS.each_pair { |k,v| rv[k] ||= v }