norikra 0.0.13-java → 0.0.14-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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