norikra 0.0.13-java → 0.0.14-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -78,34 +78,47 @@ module Norikra
78
78
  debug "norikra engine stopped"
79
79
  end
80
80
 
81
- def open(target, fields=nil)
81
+ def open(target_name, fields=nil, auto_field=true)
82
82
  # fields nil || [] => lazy
83
83
  # fields {'fieldname' => 'type'} : type 'string', 'boolean', 'int', 'long', 'float', 'double'
84
- info "opening target", :target => target, :fields => fields
84
+ info "opening target", :target => target_name, :fields => fields, :auto_field => auto_field
85
+ raise Norikra::ArgumentError, "invalid target name" unless Norikra::Target.valid?(target_name)
86
+ target = Norikra::Target.new(target_name, fields, auto_field)
85
87
  return false if @targets.include?(target)
86
- raise Norikra::ArgumentError, "invalid target name" unless Norikra::Target.valid?(target)
87
- open_target(target, fields)
88
+ open_target(target)
88
89
  end
89
90
 
90
- def close(target)
91
- info "closing target", :target => target
92
- return false unless @targets.include?(target)
93
- @queries.select{|q| q.targets.include?(target)}.each do |query|
91
+ def close(target_name)
92
+ info "closing target", :target => target_name
93
+ targets = @targets.select{|t| t.name == target_name}
94
+ return false if targets.size != 1
95
+ target = targets.first
96
+ @queries.select{|q| q.targets.include?(target.name)}.each do |query|
94
97
  deregister_query(query)
95
98
  end
96
99
  close_target(target)
97
100
  end
98
101
 
99
- def reserve(target, field, type)
100
- @typedef_manager.reserve(target, field, type)
102
+ def modify(target_name, auto_field)
103
+ info "modify target", :target => target_name, :auto_field => auto_field
104
+ targets = @targets.select{|t| t.name == target_name}
105
+ if targets.size != 1
106
+ raise Norikra::ArgumentError, "target name '#{target_name}' not found"
107
+ end
108
+ target = targets.first
109
+ target.auto_field = auto_field
110
+ end
111
+
112
+ def reserve(target_name, field, type)
113
+ @typedef_manager.reserve(target_name, field, type)
101
114
  end
102
115
 
103
116
  def register(query)
104
117
  info "registering query", :name => query.name, :targets => query.targets, :expression => query.expression
105
118
  raise Norikra::ClientError, "query name '#{query.name}' already exists" if @queries.select{|q| q.name == query.name }.size > 0
106
119
 
107
- query.targets.each do |target|
108
- open(target) unless @targets.include?(target)
120
+ query.targets.each do |target_name|
121
+ open(target_name) unless @targets.any?{|t| t.name == target_name}
109
122
  end
110
123
  register_query(query)
111
124
  end
@@ -118,42 +131,48 @@ module Norikra
118
131
  deregister_query(queries.first)
119
132
  end
120
133
 
121
- def send(target, events)
122
- trace "send messages", :target => target, :events => events
123
- unless @targets.include?(target) # discard events for target not registered
124
- trace "messages skipped for non-opened target", :target => target
134
+ def send(target_name, events)
135
+ trace "send messages", :target => target_name, :events => events
136
+ unless @targets.include?(target_name) # discard events for target not registered
137
+ trace "messages skipped for non-opened target", :target => target_name
125
138
  return
126
139
  end
127
140
  return if events.size < 1
128
141
 
129
- if @typedef_manager.lazy?(target)
130
- info "opening lazy target", :target => target
131
- debug "generating base fieldset from event", :target => target, :event => events.first
132
- base_fieldset = @typedef_manager.generate_base_fieldset(target, events.first)
142
+ if @typedef_manager.lazy?(target_name)
143
+ info "opening lazy target", :target => target_name
144
+ debug "generating base fieldset from event", :target => target_name, :event => events.first
145
+ base_fieldset = @typedef_manager.generate_base_fieldset(target_name, events.first)
133
146
 
134
- debug "registering base fieldset", :target => target, :base => base_fieldset
135
- register_base_fieldset(target, base_fieldset)
147
+ debug "registering base fieldset", :target => target_name, :base => base_fieldset
148
+ register_base_fieldset(target_name, base_fieldset)
136
149
 
137
- info "target successfully opened with fieldset", :target => target, :base => base_fieldset
150
+ info "target successfully opened with fieldset", :target => target_name, :base => base_fieldset
138
151
  end
139
152
 
140
- registered_data_fieldset = @registered_fieldsets[target][:data]
153
+ registered_data_fieldset = @registered_fieldsets[target_name][:data]
154
+
155
+ target = @targets.select{|t| t.name == target_name}.first
156
+ strict_refer = (not target.auto_field?)
141
157
 
142
158
  events.each do |event|
143
- fieldset = @typedef_manager.refer(target, event)
159
+ fieldset = @typedef_manager.refer(target_name, event, strict_refer)
144
160
 
145
161
  unless registered_data_fieldset[fieldset.summary]
146
162
  # register waiting queries including this fieldset, and this fieldset itself
147
- debug "registering unknown fieldset", :target => target, :fieldset => fieldset
148
- register_fieldset(target, fieldset)
163
+ debug "registering unknown fieldset", :target => target_name, :fieldset => fieldset
164
+ register_fieldset(target_name, fieldset)
149
165
  debug "successfully registered"
150
166
 
151
167
  # fieldset should be refined, when waiting_queries rewrite inheritance structure and data fieldset be renewed.
152
- fieldset = @typedef_manager.refer(target, event)
168
+ fieldset = @typedef_manager.refer(target_name, event, strict_refer)
153
169
  end
154
170
 
155
- trace "calling sendEvent", :target => target, :fieldset => fieldset, :event_type_name => fieldset.event_type_name, :event => event
156
- @runtime.sendEvent(@typedef_manager.format(target, event).to_java, fieldset.event_type_name)
171
+ trace "calling sendEvent with bound fieldset (w/ valid event_type_name)", :target => target_name, :event => event
172
+ trace "This is assert for valid event_type_name", :event_type_name => fieldset.event_type_name
173
+ formed = fieldset.format(event)
174
+ trace "sendEvent", :data => formed
175
+ @runtime.sendEvent(formed.to_java, fieldset.event_type_name)
157
176
  end
158
177
  nil
159
178
  end
@@ -202,18 +221,18 @@ module Norikra
202
221
 
203
222
  private
204
223
 
205
- def open_target(target, fields)
224
+ def open_target(target)
206
225
  @mutex.synchronize do
207
226
  return false if @targets.include?(target)
208
227
 
209
- @typedef_manager.add_target(target, fields)
210
- @registered_fieldsets[target] = {:base => {}, :query => {}, :data => {}}
228
+ @typedef_manager.add_target(target.name, target.fields)
229
+ @registered_fieldsets[target.name] = {:base => {}, :query => {}, :data => {}}
211
230
 
212
- unless @typedef_manager.lazy?(target)
213
- base_fieldset = @typedef_manager.base_fieldset(target)
231
+ unless @typedef_manager.lazy?(target.name)
232
+ base_fieldset = @typedef_manager.base_fieldset(target.name)
214
233
 
215
- @typedef_manager.bind_fieldset(target, :base, base_fieldset)
216
- register_fieldset_actually(target, base_fieldset, :base)
234
+ @typedef_manager.bind_fieldset(target.name, :base, base_fieldset)
235
+ register_fieldset_actually(target.name, base_fieldset, :base)
217
236
  end
218
237
 
219
238
  @targets.push(target)
@@ -225,33 +244,33 @@ module Norikra
225
244
  @mutex.synchronize do
226
245
  return false unless @targets.include?(target)
227
246
 
228
- @typedef_manager.remove_target(target)
229
- @registered_fieldsets.delete(target)
247
+ @typedef_manager.remove_target(target.name)
248
+ @registered_fieldsets.delete(target.name)
230
249
 
231
250
  @targets.delete(target)
232
251
  end
233
252
  true
234
253
  end
235
254
 
236
- def register_base_fieldset(target, fieldset)
255
+ def register_base_fieldset(target_name, fieldset)
237
256
  # for lazy target, with generated fieldset from sent events.first
238
257
  @mutex.synchronize do
239
- return false unless @typedef_manager.lazy?(target)
258
+ return false unless @typedef_manager.lazy?(target_name)
240
259
 
241
- @typedef_manager.activate(target, fieldset)
242
- register_fieldset_actually(target, fieldset, :base)
260
+ @typedef_manager.activate(target_name, fieldset)
261
+ register_fieldset_actually(target_name, fieldset, :base)
243
262
  end
244
263
  true
245
264
  end
246
265
 
247
- def update_inherits_graph(target, query_fieldset)
266
+ def update_inherits_graph(target_name, query_fieldset)
248
267
  # replace registered data fieldsets with new fieldset inherits this query fieldset
249
- @typedef_manager.supersets(target, query_fieldset).each do |set|
268
+ @typedef_manager.supersets(target_name, query_fieldset).each do |set|
250
269
  rebound = set.rebind(true) # update event_type_name with new inheritations
251
270
 
252
- register_fieldset_actually(target, rebound, :data, true) # replacing on esper engine
253
- @typedef_manager.replace_fieldset(target, set, rebound)
254
- deregister_fieldset_actually(target, set.event_type_name, :data)
271
+ register_fieldset_actually(target_name, rebound, :data, true) # replacing on esper engine
272
+ @typedef_manager.replace_fieldset(target_name, set, rebound)
273
+ deregister_fieldset_actually(target_name, set.event_type_name, :data)
255
274
  end
256
275
  end
257
276
 
@@ -261,16 +280,18 @@ module Norikra
261
280
 
262
281
  unless @typedef_manager.ready?(query)
263
282
  @waiting_queries.push(query)
283
+ trace "waiting query fields", :targets => query.targets, :fields => query.targets.map{|t| query.fields(t)}
284
+ @typedef_manager.register_waiting_fields(query)
264
285
  @queries.push(query)
265
286
  return
266
287
  end
267
288
 
268
289
  mapping = @typedef_manager.generate_fieldset_mapping(query)
269
- mapping.each do |target, query_fieldset|
270
- @typedef_manager.bind_fieldset(target, :query, query_fieldset)
271
- register_fieldset_actually(target, query_fieldset, :query)
272
- update_inherits_graph(target, query_fieldset)
273
- query.fieldsets[target] = query_fieldset
290
+ mapping.each do |target_name, query_fieldset|
291
+ @typedef_manager.bind_fieldset(target_name, :query, query_fieldset)
292
+ register_fieldset_actually(target_name, query_fieldset, :query)
293
+ update_inherits_graph(target_name, query_fieldset)
294
+ query.fieldsets[target_name] = query_fieldset
274
295
  end
275
296
 
276
297
  register_query_actually(query, mapping)
@@ -289,19 +310,19 @@ module Norikra
289
310
  if @waiting_queries.include?(query)
290
311
  @waiting_queries.delete(query)
291
312
  else
292
- query.fieldsets.each do |target, query_fieldset|
313
+ query.fieldsets.each do |target_name, query_fieldset|
293
314
  removed_event_type_name = query_fieldset.event_type_name
294
315
 
295
- @typedef_manager.unbind_fieldset(target, :query, query_fieldset)
296
- update_inherits_graph(target, query_fieldset)
297
- deregister_fieldset_actually(target, removed_event_type_name, :query)
316
+ @typedef_manager.unbind_fieldset(target_name, :query, query_fieldset)
317
+ update_inherits_graph(target_name, query_fieldset)
318
+ deregister_fieldset_actually(target_name, removed_event_type_name, :query)
298
319
  end
299
320
  end
300
321
  end
301
322
  true
302
323
  end
303
324
 
304
- def register_waiting_queries(target)
325
+ def register_waiting_queries
305
326
  ready = []
306
327
  not_ready = []
307
328
  @waiting_queries.each do |q|
@@ -315,25 +336,25 @@ module Norikra
315
336
 
316
337
  ready.each do |query|
317
338
  mapping = @typedef_manager.generate_fieldset_mapping(query)
318
- mapping.each do |target, query_fieldset|
319
- @typedef_manager.bind_fieldset(target, :query, query_fieldset)
320
- register_fieldset_actually(target, query_fieldset, :query)
321
- update_inherits_graph(target, query_fieldset)
322
- query.fieldsets[target] = query_fieldset
339
+ mapping.each do |target_name, query_fieldset|
340
+ @typedef_manager.bind_fieldset(target_name, :query, query_fieldset)
341
+ register_fieldset_actually(target_name, query_fieldset, :query)
342
+ update_inherits_graph(target_name, query_fieldset)
343
+ query.fieldsets[target_name] = query_fieldset
323
344
  end
324
345
  register_query_actually(query, mapping)
325
346
  end
326
347
  end
327
348
 
328
- def register_fieldset(target, fieldset)
349
+ def register_fieldset(target_name, fieldset)
329
350
  @mutex.synchronize do
330
- @typedef_manager.bind_fieldset(target, :data, fieldset)
351
+ @typedef_manager.bind_fieldset(target_name, :data, fieldset)
331
352
 
332
353
  if @waiting_queries.size > 0
333
- register_waiting_queries(target)
354
+ register_waiting_queries
334
355
  end
335
-
336
- register_fieldset_actually(target, fieldset, :data)
356
+ debug "registering data fieldset", :target => target_name, :fields => fieldset.fields
357
+ register_fieldset_actually(target_name, fieldset, :data)
337
358
  end
338
359
  end
339
360
 
@@ -359,7 +380,7 @@ module Norikra
359
380
  administrator = @service.getEPAdministrator
360
381
 
361
382
  statement_model = administrator.compileEPL(query.expression)
362
- Norikra::Query.rewrite_event_type_name(statement_model, event_type_name_map)
383
+ Norikra::Query.rewrite_query(statement_model, event_type_name_map)
363
384
 
364
385
  epl = administrator.create(statement_model)
365
386
  epl.java_send :addListener, [com.espertech.esper.client.UpdateListener.java_class], Listener.new(query.name, query.group, @output_pool)
@@ -379,8 +400,8 @@ module Norikra
379
400
  end
380
401
 
381
402
  # this method should be protected with @mutex lock
382
- def register_fieldset_actually(target, fieldset, level, replace=false)
383
- return if level == :data && @registered_fieldsets[target][level][fieldset.summary] && !replace
403
+ def register_fieldset_actually(target_name, fieldset, level, replace=false)
404
+ return if level == :data && @registered_fieldsets[target_name][level][fieldset.summary] && !replace
384
405
 
385
406
  # Map Supertype (target) and Subtype (typedef name, like TARGET_TypeDefName)
386
407
  # http://esper.codehaus.org/esper-4.9.0/doc/reference/en-US/html/event_representation.html#eventrep-map-supertype
@@ -388,29 +409,29 @@ module Norikra
388
409
  # .addEventType("AccountUpdate", accountUpdateDef, new String[] {"BaseUpdate"});
389
410
  case level
390
411
  when :base
391
- debug "add event type", :target => target, :level => 'base', :event_type => fieldset.event_type_name
412
+ debug "add event type", :target => target_name, :level => 'base', :event_type => fieldset.event_type_name
392
413
  @config.addEventType(fieldset.event_type_name, fieldset.definition)
393
414
  when :query
394
- base_name = @typedef_manager.base_fieldset(target).event_type_name
395
- debug "add event type", :target => target, :level => 'query', :event_type => fieldset.event_type_name, :base => base_name
415
+ base_name = @typedef_manager.base_fieldset(target_name).event_type_name
416
+ debug "add event type", :target => target_name, :level => 'query', :event_type => fieldset.event_type_name, :base => base_name
396
417
  @config.addEventType(fieldset.event_type_name, fieldset.definition, [base_name].to_java(:string))
397
418
  else
398
- subset_names = @typedef_manager.subsets(target, fieldset).map(&:event_type_name)
399
- debug "add event type", :target => target, :level => 'data', :event_type => fieldset.event_type_name, :inherit => subset_names
419
+ subset_names = @typedef_manager.subsets(target_name, fieldset).map(&:event_type_name)
420
+ debug "add event type", :target => target_name, :level => 'data', :event_type => fieldset.event_type_name, :inherit => subset_names
400
421
  @config.addEventType(fieldset.event_type_name, fieldset.definition, subset_names.to_java(:string))
401
422
 
402
- @registered_fieldsets[target][level][fieldset.summary] = fieldset
423
+ @registered_fieldsets[target_name][level][fieldset.summary] = fieldset
403
424
  end
404
425
  nil
405
426
  end
406
427
 
407
428
  # this method should be protected with @mutex lock as same as register
408
- def deregister_fieldset_actually(target, event_type_name, level)
429
+ def deregister_fieldset_actually(target_name, event_type_name, level)
409
430
  return if level == :base
410
431
 
411
- # DON'T check @registered_fieldsets[target][level][fieldset.summary]
432
+ # DON'T check @registered_fieldsets[target_name][level][fieldset.summary]
412
433
  # removed fieldset should be already replaced with register_fieldset_actually w/ replace flag
413
- debug "remove event type", :target => target, :event_type => event_type_name
434
+ debug "remove event type", :target => target_name, :event_type => event_type_name
414
435
  @config.removeEventType(event_type_name, true)
415
436
  end
416
437
 
@@ -0,0 +1,204 @@
1
+ require 'norikra/error'
2
+
3
+ module Norikra
4
+ class Field
5
+ ### esper types
6
+ ### http://esper.codehaus.org/esper-4.9.0/doc/reference/en-US/html/epl_clauses.html#epl-syntax-datatype
7
+ # string A single character to an unlimited number of characters.
8
+ # boolean A boolean value.
9
+ # integer An integer value (4 byte).
10
+ # long A long value (8 byte). Use the "L" or "l" (lowercase L) suffix. # select 1L as field1, 1l as field2
11
+ # double A double-precision 64-bit IEEE 754 floating point. # select 1.67 as field1, 167e-2 as field2, 1.67d as field3
12
+ # float A single-precision 32-bit IEEE 754 floating point. Use the "f" suffix. # select 1.2f as field1, 1.2F as field2
13
+ # byte A 8-bit signed two's complement integer. # select 0x10 as field1
14
+ #
15
+ ### norikra types of container
16
+ # hash A single value which is represented as Hash class (ex: parsed json object), and can be nested with hash/array
17
+ # # select h.value1, h.value2.value3 #<= "h":{"value1":"...","value2":{"value3":"..."}}
18
+ # Hash key item as "String of Numbers" be escaped with '$$'.
19
+ # # select h.$$3 #<= "h":{"3":3}
20
+ #
21
+ # array A single value which is represented as Array class (ex: parsed json array), and can be nested with hash/array
22
+ # # select h.$0, h.$1.$0.name #<= "h":["....", [{"name":"value..."}]]
23
+ #
24
+ #### 'integer' in epser document IS WRONG.
25
+ #### If 'integer' specified, esper raises this exception:
26
+ ### Exception: Nestable type configuration encountered an unexpected property type name 'integer' for property 'status',
27
+ ### expected java.lang.Class or java.util.Map or the name of a previously-declared Map or ObjectArray type
28
+ #### Correct type name is 'int'. see and run 'junks/esper-test.rb'
29
+
30
+ attr_accessor :name, :type, :optional, :escaped_name, :container_name, :container_type
31
+
32
+ def initialize(name, type, optional=nil)
33
+ @name = name.to_s
34
+ @type = self.class.valid_type?(type)
35
+ @optional = optional
36
+
37
+ @escaped_name = self.class.escape_name(@name)
38
+
39
+ @container_name = @container_type = nil
40
+
41
+ @chained_access = !!@name.index('.')
42
+ if @chained_access
43
+ parts = @name.split(/(?<!\.)\./)
44
+ @container_name = parts[0]
45
+ @container_type = parts[1] =~ /^(\$)?\d+$/ ? 'array' : 'hash'
46
+ @optional = true
47
+ end
48
+
49
+ define_value_accessor(@name, @chained_access)
50
+ end
51
+
52
+ def container_field?
53
+ @type == 'hash' || @type == 'array'
54
+ end
55
+
56
+ def chained_access?
57
+ @chained_access
58
+ end
59
+
60
+ def self.escape_name(name)
61
+ # hoge.pos #=> "hoge$pos"
62
+ # hoge.0 #=> "hoge$$0"
63
+ # hoge.$0 #=> "hoge$$0"
64
+ # hoge.$$0 #=> "hoge$$$0"
65
+
66
+ # hoge..pos #=> "hoge$_pos"
67
+ # hoge...pos #=> "hoge$__pos"
68
+
69
+ parts = name.split(/(?<!\.)\./).map do |part|
70
+ if part =~ /^\d+$/
71
+ '$' + part.to_s
72
+ else
73
+ part.gsub(/[^$_a-zA-Z0-9]/,'_')
74
+ end
75
+ end
76
+ parts.join('$')
77
+ end
78
+
79
+ def self.regulate_key_chain(keys)
80
+ keys.map{|key|
81
+ case
82
+ when key.is_a?(Integer) then '$' + key.to_s
83
+ when key.is_a?(String) && key =~ /^[0-9]+$/ then '$$' + key.to_s
84
+ else key.to_s.gsub(/[^$_a-zA-Z0-9]/,'_')
85
+ end
86
+ }
87
+ end
88
+
89
+ def self.escape_key_chain(*keys)
90
+ # "hoge", "pos" #=> "hoge$pos"
91
+ # "hoge", 3 #=> "hoge$$3"
92
+ # "hoge", "3" #=> "hoge$$$3"
93
+ # "hoge", ".pos" #=> "hoge
94
+ regulate_key_chain(keys).join('$')
95
+ end
96
+
97
+ def to_hash(sym=false)
98
+ if sym
99
+ {name: @name, type: @type, optional: @optional}
100
+ else
101
+ {'name' => @name, 'type' => @type, 'optional' => @optional}
102
+ end
103
+ end
104
+
105
+ def dup(optional=nil)
106
+ self.class.new(@name, @type, optional.nil? ? @optional : optional)
107
+ end
108
+
109
+ def ==(other)
110
+ self.name == other.name && self.type == other.type && self.optional == other.optional
111
+ end
112
+
113
+ def optional? # used outside of FieldSet
114
+ @optional
115
+ end
116
+
117
+ def self.container_type?(type)
118
+ case type.to_s.downcase
119
+ when 'hash' then true
120
+ when 'array' then true
121
+ else
122
+ false
123
+ end
124
+ end
125
+
126
+ def self.valid_type?(type)
127
+ case type.to_s.downcase
128
+ when 'string' then 'string'
129
+ when 'boolean' then 'boolean'
130
+ when 'int' then 'int'
131
+ when 'long' then 'long'
132
+ when 'float' then 'float'
133
+ when 'double' then 'double'
134
+ when 'hash' then 'hash'
135
+ when 'array' then 'array'
136
+ else
137
+ raise Norikra::ArgumentError, "invalid field type '#{type}'"
138
+ end
139
+ end
140
+
141
+ # def value(event) # by define_value_accessor
142
+
143
+ def format(value, element_path=nil) #element_path ex: 'fname.fchild', 'fname.$0', 'f.fchild.$2'
144
+ case @type
145
+ when 'string' then value.to_s
146
+ when 'boolean' then value =~ /^(true|false)$/i ? ($1.downcase == 'true') : (!!value)
147
+ when 'long','int' then value.to_i
148
+ when 'double','float' then value.to_f
149
+ when 'hash', 'array'
150
+ raise RuntimeError, "container field not permitted to access directly, maybe BUG. name:#{@name},type:#{@type}"
151
+ else
152
+ raise RuntimeError, "unknown field type (in format), maybe BUG. name:#{@name},type:#{@type}"
153
+ end
154
+ end
155
+
156
+ def define_value_accessor(name, chained)
157
+ # "fieldname" -> def value(event) ; event["fieldname"] ; end
158
+ # "fieldname.key1" -> def value(event) ; event["fieldname"]["key1"] ; end
159
+ # "fieldname.key1.$$2" -> def value(event) ; event["fieldname"]["key1"]["2"] ; end
160
+ # "fieldname.2" -> def value(event) ; event["fieldname"][2] ; end
161
+ # "fieldname.$2" -> def value(event) ; event["fieldname"][2] ; end
162
+
163
+ unless chained
164
+ @accessors = [name]
165
+ self.instance_eval do
166
+ def value(event)
167
+ event[@accessors.first]
168
+ end
169
+ end
170
+ return
171
+ end
172
+
173
+ @accessors = name.split(/(?<!\.)\./).map do |part|
174
+ case part
175
+ when /^\d+$/ then part.to_i
176
+ when /^\$(\d+)$/ then $1.to_i
177
+ when /^\$\$(\d+)$/ then $1.to_s
178
+ else part
179
+ end
180
+ end
181
+ self.instance_eval do
182
+ def safe_fetch(v, accessor)
183
+ unless accessor.is_a?(String) || accessor.is_a?(Fixnum)
184
+ raise ArgumentError, "container_accessor must be a String or Interger, but #{accessor.class.to_s}"
185
+ end
186
+ if v.is_a?(Hash)
187
+ v[accessor] || v[accessor.to_s] # hash[string] is valid, and hash[int] is also valid, and hash["int"] is valid too.
188
+ elsif v.is_a?(Array)
189
+ if accessor.is_a?(Fixnum)
190
+ v[accessor]
191
+ else # String -> Hash expected
192
+ nil
193
+ end
194
+ else # non-container value
195
+ nil
196
+ end
197
+ end
198
+ def value(event)
199
+ @accessors.reduce(event){|e,a| safe_fetch(e, a)}
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end